chore: Ran code formatter over all files

main
Sofus Albert Høgsbro Rose 2024-03-20 12:56:50 +01:00
parent be4eec2242
commit 0fbf201d08
Signed by: so-rose
GPG Key ID: AD901CB0F3701434
150 changed files with 4567 additions and 4261 deletions

View File

@ -49,4 +49,3 @@ PATH_ADDON_DEV_BLEND = PATH_DEV / 'demo.blend'
PATH_ADDON_DEV_DEPS = PATH_DEV / '.cached-dev-dependencies'
PATH_ADDON_DEV_DEPS.mkdir(exist_ok=True)

View File

@ -47,7 +47,10 @@ def zipped_addon(
tempfile.NamedTemporaryFile(mode='w') as f_tmp,
):
initpy = f_init.read()
for to_replace, replacement in BL_INFO_REPLACEMENTS.items():
for (
to_replace,
replacement,
) in BL_INFO_REPLACEMENTS.items():
initpy = initpy.replace(to_replace, replacement)
f_tmp.write(initpy)
@ -67,10 +70,7 @@ def zipped_addon(
f_zip.write(
path_pyproject_toml,
str(
(
Path(path_addon_pkg.name)
/ Path(path_pyproject_toml.name)
)
(Path(path_addon_pkg.name) / Path(path_pyproject_toml.name))
.with_suffix('')
.with_suffix('.toml')
),

View File

@ -1,4 +1,5 @@
import sympy as sp
sp.printing.str.StrPrinter._default_settings['abbrev'] = True
## In this tree, all Sympy unit printing must be abbreviated.
## By configuring this in __init__.py, we guarantee it for all subimports.

View File

@ -26,36 +26,35 @@ Unit = typ.Any ## Type of a valid unit
SOCKET_DEFS = {
socket_type: getattr(
sck,
socket_type.value.removesuffix("SocketType") + "SocketDef",
socket_type.value.removesuffix('SocketType') + 'SocketDef',
)
for socket_type in ST
if hasattr(
sck,
socket_type.value.removesuffix("SocketType") + "SocketDef"
)
if hasattr(sck, socket_type.value.removesuffix('SocketType') + 'SocketDef')
}
## TODO: Bit of a hack. Is it robust enough?
for socket_type in ST:
if not hasattr(
sck,
socket_type.value.removesuffix("SocketType") + "SocketDef",
socket_type.value.removesuffix('SocketType') + 'SocketDef',
):
print("Missing SocketDef for", socket_type.value)
print('Missing SocketDef for', socket_type.value)
####################
# - BL Socket Size Parser
####################
BL_SOCKET_3D_TYPE_PREFIXES = {
"NodeSocketVector",
"NodeSocketRotation",
'NodeSocketVector',
'NodeSocketRotation',
}
BL_SOCKET_4D_TYPE_PREFIXES = {
"NodeSocketColor",
'NodeSocketColor',
}
def size_from_bl_interface_socket(
bl_interface_socket: bpy.types.NodeTreeInterfaceSocket
bl_interface_socket: bpy.types.NodeTreeInterfaceSocket,
) -> typx.Literal[1, 2, 3, 4]:
"""Parses the `size`, aka. number of elements, contained within the `default_value` of a Blender interface socket.
@ -66,7 +65,8 @@ def size_from_bl_interface_socket(
- For 3D sockets, a hard-coded list of Blender node socket types is used.
- Else, it is a 1D socket type.
"""
if bl_interface_socket.description.startswith("2D"): return 2
if bl_interface_socket.description.startswith('2D'):
return 2
if any(
bl_interface_socket.socket_type.startswith(bl_socket_3d_type_prefix)
for bl_socket_3d_type_prefix in BL_SOCKET_3D_TYPE_PREFIXES
@ -114,7 +114,7 @@ def parse_bl_interface_socket(
# Parse Description for Socket Type
tokens = (
_tokens
if (_tokens := bl_interface_socket.description.split(" "))[0] != "2D"
if (_tokens := bl_interface_socket.description.split(' '))[0] != '2D'
else _tokens[1:]
) ## Don't include the "2D" token, if defined.
if (
@ -122,25 +122,36 @@ def parse_bl_interface_socket(
(tokens[0], bl_interface_socket.socket_type, size)
)
) is None:
return (direct_socket_type, None) ## Description doesn't map to anything
return (
direct_socket_type,
None,
) ## Description doesn't map to anything
# Determine Socket Unit (to use instead of "unit system")
## This is entirely OPTIONAL
socket_unit = None
if socket_type in ct.SOCKET_UNITS:
## Case: Unit is User-Defined
if len(tokens) > 1 and "(" in tokens[1] and ")" in tokens[1]:
if len(tokens) > 1 and '(' in tokens[1] and ')' in tokens[1]:
# Compute (<unit_str>) as Unit Token
unit_token = tokens[1].removeprefix("(").removesuffix(")")
unit_token = tokens[1].removeprefix('(').removesuffix(')')
# Compare Unit Token to Valid Sympy-Printed Units
socket_unit = _socket_unit if (_socket_unit := [
socket_unit = (
_socket_unit
if (
_socket_unit := [
unit
for unit in ct.SOCKET_UNITS[socket_type]["values"].values()
for unit in ct.SOCKET_UNITS[socket_type][
'values'
].values()
if str(unit) == unit_token
]) else ct.SOCKET_UNITS[socket_type]["values"][
ct.SOCKET_UNITS[socket_type]["default"]
]
)
else ct.SOCKET_UNITS[socket_type]['values'][
ct.SOCKET_UNITS[socket_type]['default']
]
)
## TODO: Enforce abbreviated sympy printing here, not globally
return (socket_type, socket_unit)
@ -152,11 +163,8 @@ def parse_bl_interface_socket(
def socket_def_from_bl_interface_socket(
bl_interface_socket: bpy.types.NodeTreeInterfaceSocket,
):
"""Computes an appropriate (no-arg) SocketDef from the given `bl_interface_socket`, by parsing it.
"""
return SOCKET_DEFS[
parse_bl_interface_socket(bl_interface_socket)[0]
]
"""Computes an appropriate (no-arg) SocketDef from the given `bl_interface_socket`, by parsing it."""
return SOCKET_DEFS[parse_bl_interface_socket(bl_interface_socket)[0]]
####################
@ -190,6 +198,7 @@ def value_from_bl(
return parsed_bl_socket_value
####################
# - Convert to Blender-Compatible Value
####################
@ -211,6 +220,7 @@ def make_scalar_bl_compat(scalar: typ.Any) -> typ.Any:
return scalar
def value_to_bl(
bl_interface_socket: bpy.types.NodeSocket,
value: typ.Any,
@ -221,31 +231,29 @@ def value_to_bl(
# Set Socket
if unit is not None:
bl_socket_value = spu.convert_to(value, unit) / unit
elif (
unit_system is not None
and socket_type in unit_system
):
bl_socket_value = spu.convert_to(
value, unit_system[socket_type]
) / unit_system[socket_type]
elif unit_system is not None and socket_type in unit_system:
bl_socket_value = (
spu.convert_to(value, unit_system[socket_type])
/ unit_system[socket_type]
)
else:
bl_socket_value = value
return {
1: lambda: make_scalar_bl_compat(bl_socket_value),
2: lambda: tuple([
2: lambda: tuple(
[
make_scalar_bl_compat(bl_socket_value[0]),
make_scalar_bl_compat(bl_socket_value[1]),
bl_interface_socket.default_value[2]
bl_interface_socket.default_value[2],
## Don't touch (unused) 3rd bl_socket coordinate
]),
3: lambda: tuple([
make_scalar_bl_compat(el)
for el in bl_socket_value
]),
4: lambda: tuple([
make_scalar_bl_compat(el)
for el in bl_socket_value
]),
]
),
3: lambda: tuple(
[make_scalar_bl_compat(el) for el in bl_socket_value]
),
4: lambda: tuple(
[make_scalar_bl_compat(el) for el in bl_socket_value]
),
}[size_from_bl_interface_socket(bl_interface_socket)]()
## The 'lambda' delays construction until size is determined

View File

@ -6,6 +6,8 @@ from . import contracts as ct
from .nodes import BL_NODES
DYNAMIC_SUBMENU_REGISTRATIONS = []
def mk_node_categories(
tree,
syllable_prefix=[],
@ -15,7 +17,7 @@ def mk_node_categories(
items = []
# Add Node Items
base_category = ct.NodeCategory["_".join(syllable_prefix)]
base_category = ct.NodeCategory['_'.join(syllable_prefix)]
for node_type, node_category in BL_NODES.items():
if node_category == base_category:
items.append(nodeitems_utils.NodeItem(node_type.value))
@ -23,16 +25,15 @@ def mk_node_categories(
# Add Node Sub-Menus
for syllable, sub_tree in tree.items():
current_syllable_path = syllable_prefix + [syllable]
current_category = ct.NodeCategory[
"_".join(current_syllable_path)
]
current_category = ct.NodeCategory['_'.join(current_syllable_path)]
# Build Items for Sub-Categories
subitems = mk_node_categories(
sub_tree,
current_syllable_path,
)
if len(subitems) == 0: continue
if len(subitems) == 0:
continue
# Define Dynamic Node Submenu
def draw_factory(items):
@ -44,7 +45,7 @@ def mk_node_categories(
):
nodeitem = nodeitem_or_submenu
op_add_node_cfg = self.layout.operator(
"node.add_node",
'node.add_node',
text=nodeitem.label,
)
op_add_node_cfg.type = nodeitem.nodetype
@ -52,13 +53,18 @@ def mk_node_categories(
elif isinstance(nodeitem_or_submenu, str):
submenu_id = nodeitem_or_submenu
self.layout.menu(submenu_id)
return draw
menu_class = type(str(current_category.value), (bpy.types.Menu,), {
menu_class = type(
str(current_category.value),
(bpy.types.Menu,),
{
'bl_idname': current_category.value,
'bl_label': ct.NODE_CAT_LABELS[current_category],
'draw': draw_factory(tuple(subitems)),
})
},
)
# Report to Items and Registration List
items.append(current_category.value)
@ -67,19 +73,19 @@ def mk_node_categories(
return items
####################
# - Blender Registration
####################
BL_NODE_CATEGORIES = mk_node_categories(
ct.NodeCategory.get_tree()["MAXWELLSIM"],
syllable_prefix = ["MAXWELLSIM"],
ct.NodeCategory.get_tree()['MAXWELLSIM'],
syllable_prefix=['MAXWELLSIM'],
)
## TODO: refactor, this has a big code smell
BL_REGISTER = [
*DYNAMIC_SUBMENU_REGISTRATIONS
] ## Must be run after, right now.
## TEST - TODO this is a big code smell
def menu_draw(self, context):
if context.space_data.tree_type == ct.TreeType.MaxwellSim.value:
@ -88,4 +94,5 @@ def menu_draw(self, context):
submenu_id = nodeitem_or_submenu
self.layout.menu(submenu_id)
bpy.types.NODE_MT_add.append(menu_draw)

View File

@ -7,20 +7,32 @@ import bpy
####################
# - Pure BL Types
####################
BLEnumID = pytypes_ext.Annotated[str, pyd.StringConstraints(
BLEnumID = pytypes_ext.Annotated[
str,
pyd.StringConstraints(
pattern=r'^[A-Z_]+$',
)]
SocketName = pytypes_ext.Annotated[str, pyd.StringConstraints(
),
]
SocketName = pytypes_ext.Annotated[
str,
pyd.StringConstraints(
pattern=r'^[a-zA-Z0-9_]+$',
)]
PresetName = pytypes_ext.Annotated[str, pyd.StringConstraints(
),
]
PresetName = pytypes_ext.Annotated[
str,
pyd.StringConstraints(
pattern=r'^[a-zA-Z0-9_]+$',
)]
),
]
BLColorRGBA = tuple[float, float, float, float]
####################
# - Shared-With-BL Types
####################
ManagedObjName = pytypes_ext.Annotated[str, pyd.StringConstraints(
ManagedObjName = pytypes_ext.Annotated[
str,
pyd.StringConstraints(
pattern=r'^[a-z_]+$',
)]
),
]

View File

@ -2,6 +2,7 @@ import enum
from ....utils.blender_type_enum import BlenderTypeEnum
class DataFlowKind(BlenderTypeEnum):
"""Defines a shape/kind of data that may flow through a node tree.

View File

@ -1,4 +1,5 @@
from ....utils.blender_type_enum import BlenderTypeEnum
class Icon(BlenderTypeEnum):
SimNodeEditor = "MOD_SIMPLEDEFORM"
SimNodeEditor = 'MOD_SIMPLEDEFORM'

View File

@ -1,8 +1,7 @@
import enum
from ....utils.blender_type_enum import (
BlenderTypeEnum
)
from ....utils.blender_type_enum import BlenderTypeEnum
class ManagedObjType(BlenderTypeEnum):
ManagedBLObject = enum.auto()

View File

@ -2,48 +2,39 @@ from .node_cats import NodeCategory as NC
NODE_CAT_LABELS = {
# Inputs/
NC.MAXWELLSIM_INPUTS: "Inputs",
NC.MAXWELLSIM_INPUTS_IMPORTERS: "Importers",
NC.MAXWELLSIM_INPUTS_SCENE: "Scene",
NC.MAXWELLSIM_INPUTS_PARAMETERS: "Parameters",
NC.MAXWELLSIM_INPUTS_CONSTANTS: "Constants",
NC.MAXWELLSIM_INPUTS_LISTS: "Lists",
NC.MAXWELLSIM_INPUTS: 'Inputs',
NC.MAXWELLSIM_INPUTS_IMPORTERS: 'Importers',
NC.MAXWELLSIM_INPUTS_SCENE: 'Scene',
NC.MAXWELLSIM_INPUTS_PARAMETERS: 'Parameters',
NC.MAXWELLSIM_INPUTS_CONSTANTS: 'Constants',
NC.MAXWELLSIM_INPUTS_LISTS: 'Lists',
# Outputs/
NC.MAXWELLSIM_OUTPUTS: "Outputs",
NC.MAXWELLSIM_OUTPUTS_VIEWERS: "Viewers",
NC.MAXWELLSIM_OUTPUTS_EXPORTERS: "Exporters",
NC.MAXWELLSIM_OUTPUTS_PLOTTERS: "Plotters",
NC.MAXWELLSIM_OUTPUTS: 'Outputs',
NC.MAXWELLSIM_OUTPUTS_VIEWERS: 'Viewers',
NC.MAXWELLSIM_OUTPUTS_EXPORTERS: 'Exporters',
NC.MAXWELLSIM_OUTPUTS_PLOTTERS: 'Plotters',
# Sources/
NC.MAXWELLSIM_SOURCES: "Sources",
NC.MAXWELLSIM_SOURCES_TEMPORALSHAPES: "Temporal Shapes",
NC.MAXWELLSIM_SOURCES: 'Sources',
NC.MAXWELLSIM_SOURCES_TEMPORALSHAPES: 'Temporal Shapes',
# Mediums/
NC.MAXWELLSIM_MEDIUMS: "Mediums",
NC.MAXWELLSIM_MEDIUMS_NONLINEARITIES: "Non-Linearities",
NC.MAXWELLSIM_MEDIUMS: 'Mediums',
NC.MAXWELLSIM_MEDIUMS_NONLINEARITIES: 'Non-Linearities',
# Structures/
NC.MAXWELLSIM_STRUCTURES: "Structures",
NC.MAXWELLSIM_STRUCTURES_PRIMITIVES: "Primitives",
NC.MAXWELLSIM_STRUCTURES: 'Structures',
NC.MAXWELLSIM_STRUCTURES_PRIMITIVES: 'Primitives',
# Bounds/
NC.MAXWELLSIM_BOUNDS: "Bounds",
NC.MAXWELLSIM_BOUNDS_BOUNDCONDS: "Bound Conds",
NC.MAXWELLSIM_BOUNDS: 'Bounds',
NC.MAXWELLSIM_BOUNDS_BOUNDCONDS: 'Bound Conds',
# Monitors/
NC.MAXWELLSIM_MONITORS: "Monitors",
NC.MAXWELLSIM_MONITORS_NEARFIELDPROJECTIONS: "Near-Field Projections",
NC.MAXWELLSIM_MONITORS: 'Monitors',
NC.MAXWELLSIM_MONITORS_NEARFIELDPROJECTIONS: 'Near-Field Projections',
# Simulations/
NC.MAXWELLSIM_SIMS: "Simulations",
NC.MAXWELLSIM_SIMGRIDAXES: "Sim Grid Axes",
NC.MAXWELLSIM_SIMS: 'Simulations',
NC.MAXWELLSIM_SIMGRIDAXES: 'Sim Grid Axes',
# Utilities/
NC.MAXWELLSIM_UTILITIES: "Utilities",
NC.MAXWELLSIM_UTILITIES_CONVERTERS: "Converters",
NC.MAXWELLSIM_UTILITIES_OPERATIONS: "Operations",
NC.MAXWELLSIM_UTILITIES: 'Utilities',
NC.MAXWELLSIM_UTILITIES_CONVERTERS: 'Converters',
NC.MAXWELLSIM_UTILITIES_OPERATIONS: 'Operations',
# Viz/
NC.MAXWELLSIM_VIZ: "Viz",
NC.MAXWELLSIM_VIZ: 'Viz',
}

View File

@ -1,8 +1,7 @@
import enum
from ....utils.blender_type_enum import (
BlenderTypeEnum, wrap_values_in_MT
)
from ....utils.blender_type_enum import BlenderTypeEnum, wrap_values_in_MT
@wrap_values_in_MT
class NodeCategory(BlenderTypeEnum):
@ -58,9 +57,9 @@ class NodeCategory(BlenderTypeEnum):
def get_tree(cls):
## TODO: Refactor
syllable_categories = [
str(node_category.value).split("_")
str(node_category.value).split('_')
for node_category in cls
if node_category.value != "MAXWELLSIM"
if node_category.value != 'MAXWELLSIM'
]
category_tree = {}

View File

@ -1,9 +1,11 @@
import enum
from ....utils.blender_type_enum import (
BlenderTypeEnum, append_cls_name_to_values
BlenderTypeEnum,
append_cls_name_to_values,
)
@append_cls_name_to_values
class NodeType(BlenderTypeEnum):
KitchenSink = enum.auto()
@ -35,7 +37,6 @@ class NodeType(BlenderTypeEnum):
## Inputs /
InputFile = enum.auto()
# Outputs
## Outputs / Viewers
Viewer = enum.auto()
@ -46,7 +47,6 @@ class NodeType(BlenderTypeEnum):
JSONFileExporter = enum.auto()
Tidy3DWebExporter = enum.auto()
# Sources
## Sources / Temporal Shapes
GaussianPulseTemporalShape = enum.auto()
@ -95,7 +95,6 @@ class NodeType(BlenderTypeEnum):
SphereStructure = enum.auto()
CylinderStructure = enum.auto()
# Bounds
BoundConds = enum.auto()
@ -108,7 +107,6 @@ class NodeType(BlenderTypeEnum):
PeriodicBoundCond = enum.auto()
AbsorbingBoundCond = enum.auto()
# Monitors
EHFieldMonitor = enum.auto()
FieldPowerFluxMonitor = enum.auto()
@ -120,7 +118,6 @@ class NodeType(BlenderTypeEnum):
ObservationAngleNearFieldProjectionMonitor = enum.auto()
KSpaceNearFieldProjectionMonitor = enum.auto()
# Sims
SimDomain = enum.auto()
SimGrid = enum.auto()
@ -134,7 +131,6 @@ class NodeType(BlenderTypeEnum):
## Sim /
FDTDSim = enum.auto()
# Utilities
Combine = enum.auto()
Separate = enum.auto()
@ -146,7 +142,5 @@ class NodeType(BlenderTypeEnum):
## Utilities / Operations
ArrayOperation = enum.auto()
# Viz
FDTDSimDataViz = enum.auto()

View File

@ -10,16 +10,14 @@ class ManagedObj(typ.Protocol):
def __init__(
self,
name: ManagedObjName,
):
...
): ...
@property
def name(self) -> str: ...
@name.setter
def name(self, value: str): ...
def free(self):
...
def free(self): ...
def bl_select(self):
"""If this is a managed Blender object, and the operation "select this in Blender" makes sense, then do so.

View File

@ -6,6 +6,7 @@ import pydantic as pyd
from ..bl import PresetName, SocketName, BLEnumID
from .managed_obj import ManagedObj
class ManagedObjDef(pyd.BaseModel):
mk: typ.Callable[[str], ManagedObj]
name_prefix: str = ""
name_prefix: str = ''

View File

@ -4,6 +4,7 @@ import pydantic as pyd
from ..bl import PresetName, SocketName, BLEnumID
class PresetDef(pyd.BaseModel):
label: PresetName
description: str

View File

@ -10,13 +10,11 @@ SOCKET_COLORS = {
ST.Bool: (0.7, 0.7, 0.7, 1.0), # Medium Light Grey
ST.String: (0.7, 0.7, 0.7, 1.0), # Medium Light Grey
ST.FilePath: (0.6, 0.6, 0.6, 1.0), # Medium Grey
# Number
ST.IntegerNumber: (0.5, 0.5, 1.0, 1.0), # Light Blue
ST.RationalNumber: (0.4, 0.4, 0.9, 1.0), # Medium Light Blue
ST.RealNumber: (0.3, 0.3, 0.8, 1.0), # Medium Blue
ST.ComplexNumber: (0.2, 0.2, 0.7, 1.0), # Dark Blue
# Vector
ST.Integer2DVector: (0.5, 1.0, 0.5, 1.0), # Light Green
ST.Real2DVector: (0.5, 1.0, 0.5, 1.0), # Light Green
@ -24,7 +22,6 @@ SOCKET_COLORS = {
ST.Integer3DVector: (0.3, 0.8, 0.3, 1.0), # Medium Green
ST.Real3DVector: (0.3, 0.8, 0.3, 1.0), # Medium Green
ST.Complex3DVector: (0.2, 0.7, 0.2, 1.0), # Dark Green
# Physical
ST.PhysicalUnitSystem: (1.0, 0.5, 0.5, 1.0), # Light Red
ST.PhysicalTime: (1.0, 0.5, 0.5, 1.0), # Light Red
@ -44,14 +41,12 @@ SOCKET_COLORS = {
ST.PhysicalForce3D: (0.6, 0.45, 0.25, 1.0), # Medium Dark Orange
ST.PhysicalPol: (0.5, 0.4, 0.2, 1.0), # Dark Orange
ST.PhysicalFreq: (1.0, 0.7, 0.5, 1.0), # Light Peach
# Blender
ST.BlenderObject: (0.7, 0.5, 1.0, 1.0), # Light Purple
ST.BlenderCollection: (0.6, 0.45, 0.9, 1.0), # Medium Light Purple
ST.BlenderImage: (0.5, 0.4, 0.8, 1.0), # Medium Purple
ST.BlenderGeoNodes: (0.3, 0.3, 0.6, 1.0), # Dark Purple
ST.BlenderText: (0.5, 0.5, 0.75, 1.0), # Light Lavender
# Maxwell
ST.MaxwellSource: (1.0, 1.0, 0.5, 1.0), # Light Yellow
ST.MaxwellTemporalShape: (0.9, 0.9, 0.45, 1.0), # Medium Light Yellow
@ -66,8 +61,6 @@ SOCKET_COLORS = {
ST.MaxwellSimGrid: (0.5, 0.4, 0.3, 1.0), # Dark Gold
ST.MaxwellSimGridAxis: (0.4, 0.3, 0.25, 1.0), # Darkest Gold
ST.MaxwellSimDomain: (0.4, 0.3, 0.25, 1.0), # Darkest Gold
# Tidy3D
ST.Tidy3DCloudTask: (0.4, 0.3, 0.25, 1.0), # Darkest Gold
}

View File

@ -1,78 +1,62 @@
from .socket_types import SocketType as ST
BL_SOCKET_DESCR_ANNOT_STRING = ":: "
BL_SOCKET_DESCR_ANNOT_STRING = ':: '
BL_SOCKET_DESCR_TYPE_MAP = {
("Time", "NodeSocketFloat", 1): ST.PhysicalTime,
("Angle", "NodeSocketFloat", 1): ST.PhysicalAngle,
("SolidAngle", "NodeSocketFloat", 1): ST.PhysicalSolidAngle,
("Rotation", "NodeSocketVector", 2): ST.PhysicalRot2D,
("Rotation", "NodeSocketVector", 3): ST.PhysicalRot3D,
("Freq", "NodeSocketFloat", 1): ST.PhysicalFreq,
("AngFreq", "NodeSocketFloat", 1): ST.PhysicalAngFreq,
('Time', 'NodeSocketFloat', 1): ST.PhysicalTime,
('Angle', 'NodeSocketFloat', 1): ST.PhysicalAngle,
('SolidAngle', 'NodeSocketFloat', 1): ST.PhysicalSolidAngle,
('Rotation', 'NodeSocketVector', 2): ST.PhysicalRot2D,
('Rotation', 'NodeSocketVector', 3): ST.PhysicalRot3D,
('Freq', 'NodeSocketFloat', 1): ST.PhysicalFreq,
('AngFreq', 'NodeSocketFloat', 1): ST.PhysicalAngFreq,
## Cartesian
("Length", "NodeSocketFloat", 1): ST.PhysicalLength,
("Area", "NodeSocketFloat", 1): ST.PhysicalArea,
("Volume", "NodeSocketFloat", 1): ST.PhysicalVolume,
("Disp", "NodeSocketVector", 2): ST.PhysicalDisp2D,
("Disp", "NodeSocketVector", 3): ST.PhysicalDisp3D,
("Point", "NodeSocketFloat", 1): ST.PhysicalPoint1D,
("Point", "NodeSocketVector", 2): ST.PhysicalPoint2D,
("Point", "NodeSocketVector", 3): ST.PhysicalPoint3D,
("Size", "NodeSocketVector", 2): ST.PhysicalSize2D,
("Size", "NodeSocketVector", 3): ST.PhysicalSize3D,
('Length', 'NodeSocketFloat', 1): ST.PhysicalLength,
('Area', 'NodeSocketFloat', 1): ST.PhysicalArea,
('Volume', 'NodeSocketFloat', 1): ST.PhysicalVolume,
('Disp', 'NodeSocketVector', 2): ST.PhysicalDisp2D,
('Disp', 'NodeSocketVector', 3): ST.PhysicalDisp3D,
('Point', 'NodeSocketFloat', 1): ST.PhysicalPoint1D,
('Point', 'NodeSocketVector', 2): ST.PhysicalPoint2D,
('Point', 'NodeSocketVector', 3): ST.PhysicalPoint3D,
('Size', 'NodeSocketVector', 2): ST.PhysicalSize2D,
('Size', 'NodeSocketVector', 3): ST.PhysicalSize3D,
## Mechanical
("Mass", "NodeSocketFloat", 1): ST.PhysicalMass,
("Speed", "NodeSocketFloat", 1): ST.PhysicalSpeed,
("Vel", "NodeSocketVector", 2): ST.PhysicalVel2D,
("Vel", "NodeSocketVector", 3): ST.PhysicalVel3D,
("Accel", "NodeSocketFloat", 1): ST.PhysicalAccelScalar,
("Accel", "NodeSocketVector", 2): ST.PhysicalAccel2D,
("Accel", "NodeSocketVector", 3): ST.PhysicalAccel3D,
("Force", "NodeSocketFloat", 1): ST.PhysicalForceScalar,
("Force", "NodeSocketVector", 2): ST.PhysicalForce2D,
("Force", "NodeSocketVector", 3): ST.PhysicalForce3D,
("Pressure", "NodeSocketFloat", 1): ST.PhysicalPressure,
('Mass', 'NodeSocketFloat', 1): ST.PhysicalMass,
('Speed', 'NodeSocketFloat', 1): ST.PhysicalSpeed,
('Vel', 'NodeSocketVector', 2): ST.PhysicalVel2D,
('Vel', 'NodeSocketVector', 3): ST.PhysicalVel3D,
('Accel', 'NodeSocketFloat', 1): ST.PhysicalAccelScalar,
('Accel', 'NodeSocketVector', 2): ST.PhysicalAccel2D,
('Accel', 'NodeSocketVector', 3): ST.PhysicalAccel3D,
('Force', 'NodeSocketFloat', 1): ST.PhysicalForceScalar,
('Force', 'NodeSocketVector', 2): ST.PhysicalForce2D,
('Force', 'NodeSocketVector', 3): ST.PhysicalForce3D,
('Pressure', 'NodeSocketFloat', 1): ST.PhysicalPressure,
## Energetic
("Energy", "NodeSocketFloat", 1): ST.PhysicalEnergy,
("Power", "NodeSocketFloat", 1): ST.PhysicalPower,
("Temp", "NodeSocketFloat", 1): ST.PhysicalTemp,
('Energy', 'NodeSocketFloat', 1): ST.PhysicalEnergy,
('Power', 'NodeSocketFloat', 1): ST.PhysicalPower,
('Temp', 'NodeSocketFloat', 1): ST.PhysicalTemp,
## ELectrodynamical
("Curr", "NodeSocketFloat", 1): ST.PhysicalCurr,
("CurrDens", "NodeSocketVector", 2): ST.PhysicalCurrDens2D,
("CurrDens", "NodeSocketVector", 3): ST.PhysicalCurrDens3D,
("Charge", "NodeSocketFloat", 1): ST.PhysicalCharge,
("Voltage", "NodeSocketFloat", 1): ST.PhysicalVoltage,
("Capacitance", "NodeSocketFloat", 1): ST.PhysicalCapacitance,
("Resistance", "NodeSocketFloat", 1): ST.PhysicalResistance,
("Conductance", "NodeSocketFloat", 1): ST.PhysicalConductance,
("MagFlux", "NodeSocketFloat", 1): ST.PhysicalMagFlux,
("MagFluxDens", "NodeSocketFloat", 1): ST.PhysicalMagFluxDens,
("Inductance", "NodeSocketFloat", 1): ST.PhysicalInductance,
("EField", "NodeSocketFloat", 2): ST.PhysicalEField3D,
("EField", "NodeSocketFloat", 3): ST.PhysicalEField2D,
("HField", "NodeSocketFloat", 2): ST.PhysicalHField3D,
("HField", "NodeSocketFloat", 3): ST.PhysicalHField2D,
('Curr', 'NodeSocketFloat', 1): ST.PhysicalCurr,
('CurrDens', 'NodeSocketVector', 2): ST.PhysicalCurrDens2D,
('CurrDens', 'NodeSocketVector', 3): ST.PhysicalCurrDens3D,
('Charge', 'NodeSocketFloat', 1): ST.PhysicalCharge,
('Voltage', 'NodeSocketFloat', 1): ST.PhysicalVoltage,
('Capacitance', 'NodeSocketFloat', 1): ST.PhysicalCapacitance,
('Resistance', 'NodeSocketFloat', 1): ST.PhysicalResistance,
('Conductance', 'NodeSocketFloat', 1): ST.PhysicalConductance,
('MagFlux', 'NodeSocketFloat', 1): ST.PhysicalMagFlux,
('MagFluxDens', 'NodeSocketFloat', 1): ST.PhysicalMagFluxDens,
('Inductance', 'NodeSocketFloat', 1): ST.PhysicalInductance,
('EField', 'NodeSocketFloat', 2): ST.PhysicalEField3D,
('EField', 'NodeSocketFloat', 3): ST.PhysicalEField2D,
('HField', 'NodeSocketFloat', 2): ST.PhysicalHField3D,
('HField', 'NodeSocketFloat', 3): ST.PhysicalHField2D,
## Luminal
("LumIntensity", "NodeSocketFloat", 1): ST.PhysicalLumIntensity,
("LumFlux", "NodeSocketFloat", 1): ST.PhysicalLumFlux,
("Illuminance", "NodeSocketFloat", 1): ST.PhysicalIlluminance,
('LumIntensity', 'NodeSocketFloat', 1): ST.PhysicalLumIntensity,
('LumFlux', 'NodeSocketFloat', 1): ST.PhysicalLumFlux,
('Illuminance', 'NodeSocketFloat', 1): ST.PhysicalIlluminance,
## Optical
("PolJones", "NodeSocketFloat", 2): ST.PhysicalPolJones,
("Pol", "NodeSocketFloat", 4): ST.PhysicalPol,
('PolJones', 'NodeSocketFloat', 2): ST.PhysicalPolJones,
('Pol', 'NodeSocketFloat', 4): ST.PhysicalPol,
}

View File

@ -1,35 +1,32 @@
from .socket_types import SocketType as ST
BL_SOCKET_DIRECT_TYPE_MAP = {
("NodeSocketString", 1): ST.String,
("NodeSocketBool", 1): ST.Bool,
("NodeSocketCollection", 1): ST.BlenderCollection,
("NodeSocketImage", 1): ST.BlenderImage,
("NodeSocketObject", 1): ST.BlenderObject,
("NodeSocketFloat", 1): ST.RealNumber,
('NodeSocketString', 1): ST.String,
('NodeSocketBool', 1): ST.Bool,
('NodeSocketCollection', 1): ST.BlenderCollection,
('NodeSocketImage', 1): ST.BlenderImage,
('NodeSocketObject', 1): ST.BlenderObject,
('NodeSocketFloat', 1): ST.RealNumber,
# ("NodeSocketFloatAngle", 1): ST.PhysicalAngle,
# ("NodeSocketFloatDistance", 1): ST.PhysicalLength,
("NodeSocketFloatFactor", 1): ST.RealNumber,
("NodeSocketFloatPercentage", 1): ST.RealNumber,
('NodeSocketFloatFactor', 1): ST.RealNumber,
('NodeSocketFloatPercentage', 1): ST.RealNumber,
# ("NodeSocketFloatTime", 1): ST.PhysicalTime,
# ("NodeSocketFloatTimeAbsolute", 1): ST.PhysicalTime,
("NodeSocketInt", 1): ST.IntegerNumber,
("NodeSocketIntFactor", 1): ST.IntegerNumber,
("NodeSocketIntPercentage", 1): ST.IntegerNumber,
("NodeSocketIntUnsigned", 1): ST.IntegerNumber,
("NodeSocketRotation", 2): ST.PhysicalRot2D,
("NodeSocketColor", 3): ST.Color,
("NodeSocketVector", 2): ST.Real2DVector,
("NodeSocketVector", 3): ST.Real3DVector,
('NodeSocketInt', 1): ST.IntegerNumber,
('NodeSocketIntFactor', 1): ST.IntegerNumber,
('NodeSocketIntPercentage', 1): ST.IntegerNumber,
('NodeSocketIntUnsigned', 1): ST.IntegerNumber,
('NodeSocketRotation', 2): ST.PhysicalRot2D,
('NodeSocketColor', 3): ST.Color,
('NodeSocketVector', 2): ST.Real2DVector,
('NodeSocketVector', 3): ST.Real3DVector,
# ("NodeSocketVectorAcceleration", 2): ST.PhysicalAccel2D,
# ("NodeSocketVectorAcceleration", 3): ST.PhysicalAccel3D,
# ("NodeSocketVectorDirection", 2): ST.Real2DVectorDir,
# ("NodeSocketVectorDirection", 3): ST.Real3DVectorDir,
("NodeSocketVectorEuler", 2): ST.PhysicalRot2D,
("NodeSocketVectorEuler", 3): ST.PhysicalRot3D,
('NodeSocketVectorEuler', 2): ST.PhysicalRot2D,
('NodeSocketVectorEuler', 3): ST.PhysicalRot3D,
# ("NodeSocketVectorTranslation", 3): ST.PhysicalDisp3D,
# ("NodeSocketVectorVelocity", 3): ST.PhysicalVel3D,
# ("NodeSocketVectorXYZ", 3): ST.PhysicalPoint3D,

View File

@ -2,67 +2,61 @@ from .socket_types import SocketType as ST
SOCKET_SHAPES = {
# Basic
ST.Any: "CIRCLE",
ST.Bool: "CIRCLE",
ST.String: "CIRCLE",
ST.FilePath: "CIRCLE",
ST.Any: 'CIRCLE',
ST.Bool: 'CIRCLE',
ST.String: 'CIRCLE',
ST.FilePath: 'CIRCLE',
# Number
ST.IntegerNumber: "CIRCLE",
ST.RationalNumber: "CIRCLE",
ST.RealNumber: "CIRCLE",
ST.ComplexNumber: "CIRCLE",
ST.IntegerNumber: 'CIRCLE',
ST.RationalNumber: 'CIRCLE',
ST.RealNumber: 'CIRCLE',
ST.ComplexNumber: 'CIRCLE',
# Vector
ST.Integer2DVector: "CIRCLE",
ST.Real2DVector: "CIRCLE",
ST.Complex2DVector: "CIRCLE",
ST.Integer3DVector: "CIRCLE",
ST.Real3DVector: "CIRCLE",
ST.Complex3DVector: "CIRCLE",
ST.Integer2DVector: 'CIRCLE',
ST.Real2DVector: 'CIRCLE',
ST.Complex2DVector: 'CIRCLE',
ST.Integer3DVector: 'CIRCLE',
ST.Real3DVector: 'CIRCLE',
ST.Complex3DVector: 'CIRCLE',
# Physical
ST.PhysicalUnitSystem: "CIRCLE",
ST.PhysicalTime: "CIRCLE",
ST.PhysicalAngle: "CIRCLE",
ST.PhysicalLength: "CIRCLE",
ST.PhysicalArea: "CIRCLE",
ST.PhysicalVolume: "CIRCLE",
ST.PhysicalPoint2D: "CIRCLE",
ST.PhysicalPoint3D: "CIRCLE",
ST.PhysicalSize2D: "CIRCLE",
ST.PhysicalSize3D: "CIRCLE",
ST.PhysicalMass: "CIRCLE",
ST.PhysicalSpeed: "CIRCLE",
ST.PhysicalAccelScalar: "CIRCLE",
ST.PhysicalForceScalar: "CIRCLE",
ST.PhysicalAccel3D: "CIRCLE",
ST.PhysicalForce3D: "CIRCLE",
ST.PhysicalPol: "CIRCLE",
ST.PhysicalFreq: "CIRCLE",
ST.PhysicalUnitSystem: 'CIRCLE',
ST.PhysicalTime: 'CIRCLE',
ST.PhysicalAngle: 'CIRCLE',
ST.PhysicalLength: 'CIRCLE',
ST.PhysicalArea: 'CIRCLE',
ST.PhysicalVolume: 'CIRCLE',
ST.PhysicalPoint2D: 'CIRCLE',
ST.PhysicalPoint3D: 'CIRCLE',
ST.PhysicalSize2D: 'CIRCLE',
ST.PhysicalSize3D: 'CIRCLE',
ST.PhysicalMass: 'CIRCLE',
ST.PhysicalSpeed: 'CIRCLE',
ST.PhysicalAccelScalar: 'CIRCLE',
ST.PhysicalForceScalar: 'CIRCLE',
ST.PhysicalAccel3D: 'CIRCLE',
ST.PhysicalForce3D: 'CIRCLE',
ST.PhysicalPol: 'CIRCLE',
ST.PhysicalFreq: 'CIRCLE',
# Blender
ST.BlenderObject: "DIAMOND",
ST.BlenderCollection: "DIAMOND",
ST.BlenderImage: "DIAMOND",
ST.BlenderGeoNodes: "DIAMOND",
ST.BlenderText: "DIAMOND",
ST.BlenderObject: 'DIAMOND',
ST.BlenderCollection: 'DIAMOND',
ST.BlenderImage: 'DIAMOND',
ST.BlenderGeoNodes: 'DIAMOND',
ST.BlenderText: 'DIAMOND',
# Maxwell
ST.MaxwellSource: "CIRCLE",
ST.MaxwellTemporalShape: "CIRCLE",
ST.MaxwellMedium: "CIRCLE",
ST.MaxwellMediumNonLinearity: "CIRCLE",
ST.MaxwellStructure: "CIRCLE",
ST.MaxwellBoundConds: "CIRCLE",
ST.MaxwellBoundCond: "CIRCLE",
ST.MaxwellMonitor: "CIRCLE",
ST.MaxwellFDTDSim: "CIRCLE",
ST.MaxwellFDTDSimData: "CIRCLE",
ST.MaxwellSimGrid: "CIRCLE",
ST.MaxwellSimGridAxis: "CIRCLE",
ST.MaxwellSimDomain: "CIRCLE",
ST.MaxwellSource: 'CIRCLE',
ST.MaxwellTemporalShape: 'CIRCLE',
ST.MaxwellMedium: 'CIRCLE',
ST.MaxwellMediumNonLinearity: 'CIRCLE',
ST.MaxwellStructure: 'CIRCLE',
ST.MaxwellBoundConds: 'CIRCLE',
ST.MaxwellBoundCond: 'CIRCLE',
ST.MaxwellMonitor: 'CIRCLE',
ST.MaxwellFDTDSim: 'CIRCLE',
ST.MaxwellFDTDSimData: 'CIRCLE',
ST.MaxwellSimGrid: 'CIRCLE',
ST.MaxwellSimGridAxis: 'CIRCLE',
ST.MaxwellSimDomain: 'CIRCLE',
# Tidy3D
ST.Tidy3DCloudTask: "DIAMOND",
ST.Tidy3DCloudTask: 'DIAMOND',
}

View File

@ -1,9 +1,12 @@
import enum
from ....utils.blender_type_enum import (
BlenderTypeEnum, append_cls_name_to_values, wrap_values_in_MT
BlenderTypeEnum,
append_cls_name_to_values,
wrap_values_in_MT,
)
@append_cls_name_to_values
class SocketType(BlenderTypeEnum):
# Base

View File

@ -5,262 +5,255 @@ from .socket_types import SocketType as ST
SOCKET_UNITS = {
ST.PhysicalTime: {
"default": "PS",
"values": {
"FS": spux.femtosecond,
"PS": spu.picosecond,
"NS": spu.nanosecond,
"MS": spu.microsecond,
"MLSEC": spu.millisecond,
"SEC": spu.second,
"MIN": spu.minute,
"HOUR": spu.hour,
"DAY": spu.day,
'default': 'PS',
'values': {
'FS': spux.femtosecond,
'PS': spu.picosecond,
'NS': spu.nanosecond,
'MS': spu.microsecond,
'MLSEC': spu.millisecond,
'SEC': spu.second,
'MIN': spu.minute,
'HOUR': spu.hour,
'DAY': spu.day,
},
},
ST.PhysicalAngle: {
"default": "RADIAN",
"values": {
"RADIAN": spu.radian,
"DEGREE": spu.degree,
"STERAD": spu.steradian,
"ANGMIL": spu.angular_mil,
'default': 'RADIAN',
'values': {
'RADIAN': spu.radian,
'DEGREE': spu.degree,
'STERAD': spu.steradian,
'ANGMIL': spu.angular_mil,
},
},
ST.PhysicalLength: {
"default": "UM",
"values": {
"PM": spu.picometer,
"A": spu.angstrom,
"NM": spu.nanometer,
"UM": spu.micrometer,
"MM": spu.millimeter,
"CM": spu.centimeter,
"M": spu.meter,
"INCH": spu.inch,
"FOOT": spu.foot,
"YARD": spu.yard,
"MILE": spu.mile,
'default': 'UM',
'values': {
'PM': spu.picometer,
'A': spu.angstrom,
'NM': spu.nanometer,
'UM': spu.micrometer,
'MM': spu.millimeter,
'CM': spu.centimeter,
'M': spu.meter,
'INCH': spu.inch,
'FOOT': spu.foot,
'YARD': spu.yard,
'MILE': spu.mile,
},
},
ST.PhysicalArea: {
"default": "UM_SQ",
"values": {
"PM_SQ": spu.picometer**2,
"A_SQ": spu.angstrom**2,
"NM_SQ": spu.nanometer**2,
"UM_SQ": spu.micrometer**2,
"MM_SQ": spu.millimeter**2,
"CM_SQ": spu.centimeter**2,
"M_SQ": spu.meter**2,
"INCH_SQ": spu.inch**2,
"FOOT_SQ": spu.foot**2,
"YARD_SQ": spu.yard**2,
"MILE_SQ": spu.mile**2,
'default': 'UM_SQ',
'values': {
'PM_SQ': spu.picometer**2,
'A_SQ': spu.angstrom**2,
'NM_SQ': spu.nanometer**2,
'UM_SQ': spu.micrometer**2,
'MM_SQ': spu.millimeter**2,
'CM_SQ': spu.centimeter**2,
'M_SQ': spu.meter**2,
'INCH_SQ': spu.inch**2,
'FOOT_SQ': spu.foot**2,
'YARD_SQ': spu.yard**2,
'MILE_SQ': spu.mile**2,
},
},
ST.PhysicalVolume: {
"default": "UM_CB",
"values": {
"PM_CB": spu.picometer**3,
"A_CB": spu.angstrom**3,
"NM_CB": spu.nanometer**3,
"UM_CB": spu.micrometer**3,
"MM_CB": spu.millimeter**3,
"CM_CB": spu.centimeter**3,
"M_CB": spu.meter**3,
"ML": spu.milliliter,
"L": spu.liter,
"INCH_CB": spu.inch**3,
"FOOT_CB": spu.foot**3,
"YARD_CB": spu.yard**3,
"MILE_CB": spu.mile**3,
'default': 'UM_CB',
'values': {
'PM_CB': spu.picometer**3,
'A_CB': spu.angstrom**3,
'NM_CB': spu.nanometer**3,
'UM_CB': spu.micrometer**3,
'MM_CB': spu.millimeter**3,
'CM_CB': spu.centimeter**3,
'M_CB': spu.meter**3,
'ML': spu.milliliter,
'L': spu.liter,
'INCH_CB': spu.inch**3,
'FOOT_CB': spu.foot**3,
'YARD_CB': spu.yard**3,
'MILE_CB': spu.mile**3,
},
},
ST.PhysicalPoint2D: {
"default": "UM",
"values": {
"PM": spu.picometer,
"A": spu.angstrom,
"NM": spu.nanometer,
"UM": spu.micrometer,
"MM": spu.millimeter,
"CM": spu.centimeter,
"M": spu.meter,
"INCH": spu.inch,
"FOOT": spu.foot,
"YARD": spu.yard,
"MILE": spu.mile,
'default': 'UM',
'values': {
'PM': spu.picometer,
'A': spu.angstrom,
'NM': spu.nanometer,
'UM': spu.micrometer,
'MM': spu.millimeter,
'CM': spu.centimeter,
'M': spu.meter,
'INCH': spu.inch,
'FOOT': spu.foot,
'YARD': spu.yard,
'MILE': spu.mile,
},
},
ST.PhysicalPoint3D: {
"default": "UM",
"values": {
"PM": spu.picometer,
"A": spu.angstrom,
"NM": spu.nanometer,
"UM": spu.micrometer,
"MM": spu.millimeter,
"CM": spu.centimeter,
"M": spu.meter,
"INCH": spu.inch,
"FOOT": spu.foot,
"YARD": spu.yard,
"MILE": spu.mile,
'default': 'UM',
'values': {
'PM': spu.picometer,
'A': spu.angstrom,
'NM': spu.nanometer,
'UM': spu.micrometer,
'MM': spu.millimeter,
'CM': spu.centimeter,
'M': spu.meter,
'INCH': spu.inch,
'FOOT': spu.foot,
'YARD': spu.yard,
'MILE': spu.mile,
},
},
ST.PhysicalSize2D: {
"default": "UM",
"values": {
"PM": spu.picometer,
"A": spu.angstrom,
"NM": spu.nanometer,
"UM": spu.micrometer,
"MM": spu.millimeter,
"CM": spu.centimeter,
"M": spu.meter,
"INCH": spu.inch,
"FOOT": spu.foot,
"YARD": spu.yard,
"MILE": spu.mile,
'default': 'UM',
'values': {
'PM': spu.picometer,
'A': spu.angstrom,
'NM': spu.nanometer,
'UM': spu.micrometer,
'MM': spu.millimeter,
'CM': spu.centimeter,
'M': spu.meter,
'INCH': spu.inch,
'FOOT': spu.foot,
'YARD': spu.yard,
'MILE': spu.mile,
},
},
ST.PhysicalSize3D: {
"default": "UM",
"values": {
"PM": spu.picometer,
"A": spu.angstrom,
"NM": spu.nanometer,
"UM": spu.micrometer,
"MM": spu.millimeter,
"CM": spu.centimeter,
"M": spu.meter,
"INCH": spu.inch,
"FOOT": spu.foot,
"YARD": spu.yard,
"MILE": spu.mile,
'default': 'UM',
'values': {
'PM': spu.picometer,
'A': spu.angstrom,
'NM': spu.nanometer,
'UM': spu.micrometer,
'MM': spu.millimeter,
'CM': spu.centimeter,
'M': spu.meter,
'INCH': spu.inch,
'FOOT': spu.foot,
'YARD': spu.yard,
'MILE': spu.mile,
},
},
ST.PhysicalMass: {
"default": "UG",
"values": {
"E_REST": spu.electron_rest_mass,
"DAL": spu.dalton,
"UG": spu.microgram,
"MG": spu.milligram,
"G": spu.gram,
"KG": spu.kilogram,
"TON": spu.metric_ton,
'default': 'UG',
'values': {
'E_REST': spu.electron_rest_mass,
'DAL': spu.dalton,
'UG': spu.microgram,
'MG': spu.milligram,
'G': spu.gram,
'KG': spu.kilogram,
'TON': spu.metric_ton,
},
},
ST.PhysicalSpeed: {
"default": "UM_S",
"values": {
"PM_S": spu.picometer / spu.second,
"NM_S": spu.nanometer / spu.second,
"UM_S": spu.micrometer / spu.second,
"MM_S": spu.millimeter / spu.second,
"M_S": spu.meter / spu.second,
"KM_S": spu.kilometer / spu.second,
"KM_H": spu.kilometer / spu.hour,
"FT_S": spu.feet / spu.second,
"MI_H": spu.mile / spu.hour,
'default': 'UM_S',
'values': {
'PM_S': spu.picometer / spu.second,
'NM_S': spu.nanometer / spu.second,
'UM_S': spu.micrometer / spu.second,
'MM_S': spu.millimeter / spu.second,
'M_S': spu.meter / spu.second,
'KM_S': spu.kilometer / spu.second,
'KM_H': spu.kilometer / spu.hour,
'FT_S': spu.feet / spu.second,
'MI_H': spu.mile / spu.hour,
},
},
ST.PhysicalAccelScalar: {
"default": "UM_S_SQ",
"values": {
"PM_S_SQ": spu.picometer / spu.second**2,
"NM_S_SQ": spu.nanometer / spu.second**2,
"UM_S_SQ": spu.micrometer / spu.second**2,
"MM_S_SQ": spu.millimeter / spu.second**2,
"M_S_SQ": spu.meter / spu.second**2,
"KM_S_SQ": spu.kilometer / spu.second**2,
"FT_S_SQ": spu.feet / spu.second**2,
'default': 'UM_S_SQ',
'values': {
'PM_S_SQ': spu.picometer / spu.second**2,
'NM_S_SQ': spu.nanometer / spu.second**2,
'UM_S_SQ': spu.micrometer / spu.second**2,
'MM_S_SQ': spu.millimeter / spu.second**2,
'M_S_SQ': spu.meter / spu.second**2,
'KM_S_SQ': spu.kilometer / spu.second**2,
'FT_S_SQ': spu.feet / spu.second**2,
},
},
ST.PhysicalForceScalar: {
"default": "UNEWT",
"values": {
"KG_M_S_SQ": spu.kg * spu.m/spu.second**2,
"NNEWT": spux.nanonewton,
"UNEWT": spux.micronewton,
"MNEWT": spux.millinewton,
"NEWT": spu.newton,
'default': 'UNEWT',
'values': {
'KG_M_S_SQ': spu.kg * spu.m / spu.second**2,
'NNEWT': spux.nanonewton,
'UNEWT': spux.micronewton,
'MNEWT': spux.millinewton,
'NEWT': spu.newton,
},
},
ST.PhysicalAccel3D: {
"default": "UM_S_SQ",
"values": {
"PM_S_SQ": spu.picometer / spu.second**2,
"NM_S_SQ": spu.nanometer / spu.second**2,
"UM_S_SQ": spu.micrometer / spu.second**2,
"MM_S_SQ": spu.millimeter / spu.second**2,
"M_S_SQ": spu.meter / spu.second**2,
"KM_S_SQ": spu.kilometer / spu.second**2,
"FT_S_SQ": spu.feet / spu.second**2,
'default': 'UM_S_SQ',
'values': {
'PM_S_SQ': spu.picometer / spu.second**2,
'NM_S_SQ': spu.nanometer / spu.second**2,
'UM_S_SQ': spu.micrometer / spu.second**2,
'MM_S_SQ': spu.millimeter / spu.second**2,
'M_S_SQ': spu.meter / spu.second**2,
'KM_S_SQ': spu.kilometer / spu.second**2,
'FT_S_SQ': spu.feet / spu.second**2,
},
},
ST.PhysicalForce3D: {
"default": "UNEWT",
"values": {
"KG_M_S_SQ": spu.kg * spu.m/spu.second**2,
"NNEWT": spux.nanonewton,
"UNEWT": spux.micronewton,
"MNEWT": spux.millinewton,
"NEWT": spu.newton,
'default': 'UNEWT',
'values': {
'KG_M_S_SQ': spu.kg * spu.m / spu.second**2,
'NNEWT': spux.nanonewton,
'UNEWT': spux.micronewton,
'MNEWT': spux.millinewton,
'NEWT': spu.newton,
},
},
ST.PhysicalFreq: {
"default": "THZ",
"values": {
"HZ": spu.hertz,
"KHZ": spux.kilohertz,
"MHZ": spux.megahertz,
"GHZ": spux.gigahertz,
"THZ": spux.terahertz,
"PHZ": spux.petahertz,
"EHZ": spux.exahertz,
'default': 'THZ',
'values': {
'HZ': spu.hertz,
'KHZ': spux.kilohertz,
'MHZ': spux.megahertz,
'GHZ': spux.gigahertz,
'THZ': spux.terahertz,
'PHZ': spux.petahertz,
'EHZ': spux.exahertz,
},
},
ST.PhysicalPol: {
"default": "RADIAN",
"values": {
"RADIAN": spu.radian,
"DEGREE": spu.degree,
"STERAD": spu.steradian,
"ANGMIL": spu.angular_mil,
'default': 'RADIAN',
'values': {
'RADIAN': spu.radian,
'DEGREE': spu.degree,
'STERAD': spu.steradian,
'ANGMIL': spu.angular_mil,
},
},
ST.MaxwellMedium: {
"default": "NM",
"values": {
"PM": spu.picometer, ## c(vac) = wl*freq
"A": spu.angstrom,
"NM": spu.nanometer,
"UM": spu.micrometer,
"MM": spu.millimeter,
"CM": spu.centimeter,
"M": spu.meter,
'default': 'NM',
'values': {
'PM': spu.picometer, ## c(vac) = wl*freq
'A': spu.angstrom,
'NM': spu.nanometer,
'UM': spu.micrometer,
'MM': spu.millimeter,
'CM': spu.centimeter,
'M': spu.meter,
},
},
ST.MaxwellMonitor: {
"default": "NM",
"values": {
"PM": spu.picometer, ## c(vac) = wl*freq
"A": spu.angstrom,
"NM": spu.nanometer,
"UM": spu.micrometer,
"MM": spu.millimeter,
"CM": spu.centimeter,
"M": spu.meter,
'default': 'NM',
'values': {
'PM': spu.picometer, ## c(vac) = wl*freq
'A': spu.angstrom,
'NM': spu.nanometer,
'UM': spu.micrometer,
'MM': spu.millimeter,
'CM': spu.centimeter,
'M': spu.meter,
},
},
}

View File

@ -1,9 +1,11 @@
import enum
from ....utils.blender_type_enum import (
BlenderTypeEnum, append_cls_name_to_values
BlenderTypeEnum,
append_cls_name_to_values,
)
@append_cls_name_to_values
class TreeType(BlenderTypeEnum):
MaxwellSim = enum.auto()

View File

@ -10,8 +10,9 @@ import bpy
from .. import contracts as ct
AREA_TYPE = "IMAGE_EDITOR"
SPACE_TYPE = "IMAGE_EDITOR"
AREA_TYPE = 'IMAGE_EDITOR'
SPACE_TYPE = 'IMAGE_EDITOR'
class ManagedBLImage(ct.schemas.ManagedObj):
managed_obj_type = ct.ManagedObjType.ManagedBLImage
@ -36,7 +37,7 @@ class ManagedBLImage(ct.schemas.ManagedObj):
# ...AND Desired Image Name is Taken
else:
msg = f"Desired name {value} for BL image is taken"
msg = f'Desired name {value} for BL image is taken'
raise ValueError(msg)
# Object DOES Exist
@ -59,24 +60,21 @@ class ManagedBLImage(ct.schemas.ManagedObj):
self,
width_px: int,
height_px: int,
color_model: typx.Literal["RGB", "RGBA"],
dtype: typx.Literal["uint8", "float32"],
color_model: typx.Literal['RGB', 'RGBA'],
dtype: typx.Literal['uint8', 'float32'],
):
"""Returns the managed blender image.
If the requested image properties are different from the image's, then delete the old image make a new image with correct geometry.
"""
channels = 4 if color_model == "RGBA" else 3
channels = 4 if color_model == 'RGBA' else 3
# Remove Image (if mismatch)
if (
(bl_image := bpy.data.images.get(self.name))
and (
if (bl_image := bpy.data.images.get(self.name)) and (
bl_image.size[0] != width_px
or bl_image.size[1] != height_px
or bl_image.channels != channels
or bl_image.is_float ^ (dtype == "float32")
)
or bl_image.is_float ^ (dtype == 'float32')
):
self.free()
@ -99,9 +97,7 @@ class ManagedBLImage(ct.schemas.ManagedObj):
If none are valid, return None.
"""
valid_areas = [
area
for area in bpy.context.screen.areas
if area.type == AREA_TYPE
area for area in bpy.context.screen.areas if area.type == AREA_TYPE
]
if valid_areas:
return valid_areas[0]
@ -111,7 +107,7 @@ class ManagedBLImage(ct.schemas.ManagedObj):
"""Returns the visible preview space in the visible preview area of
the Blender UI
"""
if (preview_area := self.preview_area):
if preview_area := self.preview_area:
return next(
space
for space in preview_area.spaces
@ -125,7 +121,7 @@ class ManagedBLImage(ct.schemas.ManagedObj):
"""Synchronizes the managed object to the preview, by manipulating
relevant editors.
"""
if (bl_image := bpy.data.images.get(self.name)):
if bl_image := bpy.data.images.get(self.name):
self.preview_space.image = bl_image
####################
@ -142,7 +138,7 @@ class ManagedBLImage(ct.schemas.ManagedObj):
import matplotlib.pyplot as plt
# Compute Image Geometry
if (preview_area := self.preview_area):
if preview_area := self.preview_area:
# Retrieve DPI from Blender Preferences
_dpi = bpy.context.preferences.system.dpi
@ -165,7 +161,7 @@ class ManagedBLImage(ct.schemas.ManagedObj):
height_px = int(_height_inches * _dpi)
else:
msg = f"There must either be a preview area, or defined `width_inches`, `height_inches`, and `dpi`"
msg = f'There must either be a preview area, or defined `width_inches`, `height_inches`, and `dpi`'
raise ValueError(msg)
# Compute Plot Dimensions
@ -196,10 +192,9 @@ class ManagedBLImage(ct.schemas.ManagedObj):
plt.close(fig)
# Optimized Write to Blender Image
bl_image = self.bl_image(cmp_width_px, cmp_height_px, "RGBA", "uint8")
bl_image = self.bl_image(cmp_width_px, cmp_height_px, 'RGBA', 'uint8')
bl_image.pixels.foreach_set(image_data.ravel())
bl_image.update()
if bl_select:
self.bl_select()

View File

@ -13,13 +13,14 @@ import bmesh
from .. import contracts as ct
ModifierType = typx.Literal["NODES", "ARRAY"]
ModifierType = typx.Literal['NODES', 'ARRAY']
MODIFIER_NAMES = {
"NODES": "BLMaxwell_GeoNodes",
"ARRAY": "BLMaxwell_Array",
'NODES': 'BLMaxwell_GeoNodes',
'ARRAY': 'BLMaxwell_Array',
}
MANAGED_COLLECTION_NAME = "BLMaxwell"
PREVIEW_COLLECTION_NAME = "BLMaxwell Visible"
MANAGED_COLLECTION_NAME = 'BLMaxwell'
PREVIEW_COLLECTION_NAME = 'BLMaxwell Visible'
def bl_collection(
collection_name: str, view_layer_exclude: bool
@ -33,13 +34,16 @@ def bl_collection(
collection = bpy.data.collections[collection_name]
## Ensure synced View Layer exclusion
if (layer_collection := bpy.context.view_layer.layer_collection.children[
if (
layer_collection := bpy.context.view_layer.layer_collection.children[
collection_name
]).exclude != view_layer_exclude:
]
).exclude != view_layer_exclude:
layer_collection.exclude = view_layer_exclude
return collection
class ManagedBLObject(ct.schemas.ManagedObj):
managed_obj_type = ct.ManagedObjType.ManagedBLObject
_bl_object_name: str
@ -63,7 +67,7 @@ class ManagedBLObject(ct.schemas.ManagedObj):
# ...AND Desired Object Name is Taken
else:
msg = f"Desired name {value} for BL object is taken"
msg = f'Desired name {value} for BL object is taken'
raise ValueError(msg)
# Object DOES Exist
@ -88,14 +92,14 @@ class ManagedBLObject(ct.schemas.ManagedObj):
# Delete the Underlying Datablock
## This automatically deletes the object too
if bl_object.type == "MESH":
if bl_object.type == 'MESH':
bpy.data.meshes.remove(bl_object.data)
elif bl_object.type == "EMPTY":
elif bl_object.type == 'EMPTY':
bpy.data.meshes.remove(bl_object.data)
elif bl_object.type == "VOLUME":
elif bl_object.type == 'VOLUME':
bpy.data.volumes.remove(bl_object.data)
else:
msg = f"Type of to-delete `bl_object`, {bl_object.type}, is not valid"
msg = f'Type of to-delete `bl_object`, {bl_object.type}, is not valid'
raise ValueError(msg)
####################
@ -103,37 +107,54 @@ class ManagedBLObject(ct.schemas.ManagedObj):
####################
def show_preview(
self,
kind: typx.Literal["MESH", "EMPTY", "VOLUME"],
kind: typx.Literal['MESH', 'EMPTY', 'VOLUME'],
empty_display_type: typx.Literal[
"PLAIN_AXES", "ARROWS", "SINGLE_ARROW", "CIRCLE", "CUBE",
"SPHERE", "CONE", "IMAGE",
] | None = None,
'PLAIN_AXES',
'ARROWS',
'SINGLE_ARROW',
'CIRCLE',
'CUBE',
'SPHERE',
'CONE',
'IMAGE',
]
| None = None,
) -> None:
"""Moves the managed Blender object to the preview collection.
If it's already included, do nothing.
"""
bl_object = self.bl_object(kind)
if bl_object.name not in (preview_collection := bl_collection(
if (
bl_object.name
not in (
preview_collection := bl_collection(
PREVIEW_COLLECTION_NAME, view_layer_exclude=False
)).objects:
)
).objects
):
preview_collection.objects.link(bl_object)
if kind == "EMPTY" and empty_display_type is not None:
if kind == 'EMPTY' and empty_display_type is not None:
bl_object.empty_display_type = empty_display_type
def hide_preview(
self,
kind: typx.Literal["MESH", "EMPTY", "VOLUME"],
kind: typx.Literal['MESH', 'EMPTY', 'VOLUME'],
) -> None:
"""Removes the managed Blender object from the preview collection.
If it's already removed, do nothing.
"""
bl_object = self.bl_object(kind)
if bl_object.name not in (preview_collection := bl_collection(
if (
bl_object.name
not in (
preview_collection := bl_collection(
PREVIEW_COLLECTION_NAME, view_layer_exclude=False
)).objects:
)
).objects
):
preview_collection.objects.unlink(bl_object)
def bl_select(self) -> None:
@ -141,7 +162,7 @@ class ManagedBLObject(ct.schemas.ManagedObj):
outlined in the 3D viewport.
"""
if not (bl_object := bpy.data.objects.get(self.name)):
msg = "Managed BLObject does not exist"
msg = 'Managed BLObject does not exist'
raise ValueError(msg)
bpy.ops.object.select_all(action='DESELECT')
@ -152,7 +173,7 @@ class ManagedBLObject(ct.schemas.ManagedObj):
####################
def bl_object(
self,
kind: typx.Literal["MESH", "EMPTY", "VOLUME"],
kind: typx.Literal['MESH', 'EMPTY', 'VOLUME'],
):
"""Returns the managed blender object.
@ -161,21 +182,22 @@ class ManagedBLObject(ct.schemas.ManagedObj):
"""
# Remove Object (if mismatch)
if (
(bl_object := bpy.data.objects.get(self.name))
and bl_object.type != kind
):
bl_object := bpy.data.objects.get(self.name)
) and bl_object.type != kind:
self.free()
# Create Object w/Appropriate Data Block
if not (bl_object := bpy.data.objects.get(self.name)):
if kind == "MESH":
if kind == 'MESH':
bl_data = bpy.data.meshes.new(self.bl_mesh_name)
elif kind == "EMPTY":
elif kind == 'EMPTY':
bl_data = None
elif kind == "VOLUME":
elif kind == 'VOLUME':
raise NotImplementedError
else:
msg = f"Requested `bl_object` type {bl_object.type} is not valid"
msg = (
f'Requested `bl_object` type {bl_object.type} is not valid'
)
raise ValueError(msg)
bl_object = bpy.data.objects.new(self.name, bl_data)
@ -195,12 +217,11 @@ class ManagedBLObject(ct.schemas.ManagedObj):
Raises an error if the object has no mesh data.
"""
if (
(bl_object := bpy.data.objects.get(self.name))
and bl_object.type == "MESH"
):
bl_object := bpy.data.objects.get(self.name)
) and bl_object.type == 'MESH':
return bl_object.data
msg = f"Requested MESH data from `bl_object` of type {bl_object.type}"
msg = f'Requested MESH data from `bl_object` of type {bl_object.type}'
raise ValueError(msg)
@contextlib.contextmanager
@ -210,9 +231,8 @@ class ManagedBLObject(ct.schemas.ManagedObj):
triangulate: bool = False,
) -> bpy.types.Mesh:
if (
(bl_object := bpy.data.objects.get(self.name))
and bl_object.type == "MESH"
):
bl_object := bpy.data.objects.get(self.name)
) and bl_object.type == 'MESH':
bmesh_mesh = None
try:
bmesh_mesh = bmesh.new()
@ -230,10 +250,11 @@ class ManagedBLObject(ct.schemas.ManagedObj):
yield bmesh_mesh
finally:
if bmesh_mesh: bmesh_mesh.free()
if bmesh_mesh:
bmesh_mesh.free()
else:
msg = f"Requested BMesh from `bl_object` of type {bl_object.type}"
msg = f'Requested BMesh from `bl_object` of type {bl_object.type}'
raise ValueError(msg)
@property
@ -245,7 +266,7 @@ class ManagedBLObject(ct.schemas.ManagedObj):
## TODO: Must we?
# Compute Evaluted + Triangulated Mesh
_mesh = bpy.data.meshes.new(name="TemporaryMesh")
_mesh = bpy.data.meshes.new(name='TemporaryMesh')
with self.mesh_as_bmesh(evaluate=True, triangulate=True) as bmesh_mesh:
bmesh_mesh.to_mesh(_mesh)
@ -265,8 +286,8 @@ class ManagedBLObject(ct.schemas.ManagedObj):
bpy.data.meshes.remove(_mesh)
return {
"verts": verts,
"faces": faces,
'verts': verts,
'faces': faces,
}
####################
@ -299,11 +320,11 @@ class ManagedBLObject(ct.schemas.ManagedObj):
"""
bl_modifier = self.bl_modifier(modifier_type)
if modifier_type == "NODES":
if modifier_type == 'NODES':
return {
"node_group": bl_modifier.node_group,
'node_group': bl_modifier.node_group,
}
elif modifier_type == "ARRAY":
elif modifier_type == 'ARRAY':
raise NotImplementedError
def s_modifier_attrs(
@ -313,10 +334,10 @@ class ManagedBLObject(ct.schemas.ManagedObj):
):
bl_modifier = self.bl_modifier(modifier_type)
if modifier_type == "NODES":
if bl_modifier.node_group != modifier_attrs["node_group"]:
bl_modifier.node_group = modifier_attrs["node_group"]
elif modifier_type == "ARRAY":
if modifier_type == 'NODES':
if bl_modifier.node_group != modifier_attrs['node_group']:
bl_modifier.node_group = modifier_attrs['node_group']
elif modifier_type == 'ARRAY':
raise NotImplementedError
####################
@ -337,27 +358,27 @@ class ManagedBLObject(ct.schemas.ManagedObj):
If the GeoNodes node group doesn't match, it is changed.
Only differing interface values are actually changed.
"""
bl_object = self.bl_object("MESH")
bl_object = self.bl_object('MESH')
# Get (/make) a GeoModes Modifier
bl_modifier = self.bl_modifier("NODES")
bl_modifier = self.bl_modifier('NODES')
# Set GeoNodes Modifier Attributes (specifically, the 'node_group')
self.s_modifier_attrs("NODES", {"node_group": geonodes_node_group})
self.s_modifier_attrs('NODES', {'node_group': geonodes_node_group})
# Set GeoNodes Values
modifier_altered = False
for interface_identifier, value in (
geonodes_identifier_to_value.items()
):
for (
interface_identifier,
value,
) in geonodes_identifier_to_value.items():
if bl_modifier[interface_identifier] != value:
# Quickly Determine if IDPropertyArray is Equal
if hasattr(
bl_modifier[interface_identifier],
"to_list"
) and tuple(
bl_modifier[interface_identifier].to_list()
) == value:
if (
hasattr(bl_modifier[interface_identifier], 'to_list')
and tuple(bl_modifier[interface_identifier].to_list())
== value
):
continue
# Quickly Determine int/float Mismatch
@ -375,7 +396,6 @@ class ManagedBLObject(ct.schemas.ManagedObj):
if modifier_altered:
bl_object.data.update()
# @property
# def volume(self) -> bpy.types.Volume:
# """Returns the object's volume data.

View File

@ -5,6 +5,7 @@ from . import outputs
from . import sources
from . import mediums
from . import structures
# from . import bounds
from . import monitors
from . import simulations

View File

@ -13,11 +13,14 @@ from .. import sockets
CACHE: dict[str, typ.Any] = {} ## By Instance UUID
## NOTE: CACHE does not persist between file loads.
_DEFAULT_LOOSE_SOCKET_SER = json.dumps({
"socket_names": [],
"socket_def_names": [],
"models": [],
})
_DEFAULT_LOOSE_SOCKET_SER = json.dumps(
{
'socket_names': [],
'socket_def_names': [],
'models': [],
}
)
class MaxwellSimNode(bpy.types.Node):
# Fundamentals
@ -28,7 +31,7 @@ class MaxwellSimNode(bpy.types.Node):
# draw_label(self) -> str: pass
# Style
bl_description: str = ""
bl_description: str = ''
# bl_width_default: float = 0.0
# bl_width_min: float = 0.0
@ -55,35 +58,35 @@ class MaxwellSimNode(bpy.types.Node):
super().__init_subclass__(**kwargs)
# Setup Blender ID for Node
if not hasattr(cls, "node_type"):
if not hasattr(cls, 'node_type'):
msg = f"Node class {cls} does not define 'node_type', or it is does not have the type {ct.NodeType}"
raise ValueError(msg)
cls.bl_idname = str(cls.node_type.value)
# Setup Instance ID for Node
cls.__annotations__["instance_id"] = bpy.props.StringProperty(
name="Instance ID",
description="The instance ID of a particular MaxwellSimNode instance, used to index caches",
default="",
cls.__annotations__['instance_id'] = bpy.props.StringProperty(
name='Instance ID',
description='The instance ID of a particular MaxwellSimNode instance, used to index caches',
default='',
)
# Setup Name Property for Node
cls.__annotations__["sim_node_name"] = bpy.props.StringProperty(
name="Sim Node Name",
description="The name of a particular MaxwellSimNode node, which can be used to help identify data managed by the node",
default="",
update=(lambda self, context: self.sync_sim_node_name(context))
cls.__annotations__['sim_node_name'] = bpy.props.StringProperty(
name='Sim Node Name',
description='The name of a particular MaxwellSimNode node, which can be used to help identify data managed by the node',
default='',
update=(lambda self, context: self.sync_sim_node_name(context)),
)
# Setup Locked Property for Node
cls.__annotations__["locked"] = bpy.props.BoolProperty(
name="Locked State",
cls.__annotations__['locked'] = bpy.props.BoolProperty(
name='Locked State',
description="The lock-state of a particular MaxwellSimNode instance, which determines the node's user editability",
default=False,
)
# Setup Blender Label for Node
if not hasattr(cls, "bl_label"):
if not hasattr(cls, 'bl_label'):
msg = f"Node class {cls} does not define 'bl_label'"
raise ValueError(msg)
@ -91,42 +94,32 @@ class MaxwellSimNode(bpy.types.Node):
cls._output_socket_methods = {
method._index_by: method
for attr_name in dir(cls)
if hasattr(
method := getattr(cls, attr_name),
"_callback_type"
) and method._callback_type == "computes_output_socket"
if hasattr(method := getattr(cls, attr_name), '_callback_type')
and method._callback_type == 'computes_output_socket'
}
cls._on_value_changed_methods = {
method
for attr_name in dir(cls)
if hasattr(
method := getattr(cls, attr_name),
"_callback_type"
) and method._callback_type == "on_value_changed"
if hasattr(method := getattr(cls, attr_name), '_callback_type')
and method._callback_type == 'on_value_changed'
}
cls._on_show_preview = {
method
for attr_name in dir(cls)
if hasattr(
method := getattr(cls, attr_name),
"_callback_type"
) and method._callback_type == "on_show_preview"
if hasattr(method := getattr(cls, attr_name), '_callback_type')
and method._callback_type == 'on_show_preview'
}
cls._on_show_plot = {
method
for attr_name in dir(cls)
if hasattr(
method := getattr(cls, attr_name),
"_callback_type"
) and method._callback_type == "on_show_plot"
if hasattr(method := getattr(cls, attr_name), '_callback_type')
and method._callback_type == 'on_show_plot'
}
cls._on_init = {
method
for attr_name in dir(cls)
if hasattr(
method := getattr(cls, attr_name),
"_callback_type"
) and method._callback_type == "on_init"
if hasattr(method := getattr(cls, attr_name), '_callback_type')
and method._callback_type == 'on_init'
}
# Setup Socket Set Dropdown
@ -135,23 +128,22 @@ class MaxwellSimNode(bpy.types.Node):
else:
## Add Active Socket Set Enum
socket_set_names = (
(_input_socket_set_names := list(cls.input_socket_sets.keys()))
+ [
_input_socket_set_names := list(cls.input_socket_sets.keys())
) + [
output_socket_set_name
for output_socket_set_name in cls.output_socket_sets.keys()
if output_socket_set_name not in _input_socket_set_names
]
)
socket_set_ids = [
socket_set_name.replace(" ", "_").upper()
socket_set_name.replace(' ', '_').upper()
for socket_set_name in socket_set_names
]
## TODO: Better deriv. of sock.set. ID, ex. ( is currently invalid.
## Add Active Socket Set Enum
cls.__annotations__["active_socket_set"] = bpy.props.EnumProperty(
name="Active Socket Set",
description="The active socket set",
cls.__annotations__['active_socket_set'] = bpy.props.EnumProperty(
name='Active Socket Set',
description='The active socket set',
items=[
(
socket_set_name,
@ -164,7 +156,9 @@ class MaxwellSimNode(bpy.types.Node):
)
],
default=socket_set_names[0],
update=lambda self, context: self.sync_active_socket_set(context),
update=lambda self, context: self.sync_active_socket_set(
context
),
)
# Setup Preset Dropdown
@ -172,9 +166,9 @@ class MaxwellSimNode(bpy.types.Node):
cls.active_preset = None
else:
## TODO: Check that presets are represented in a socket that is guaranteed to be always available, specifically either a static socket or ALL static socket sets.
cls.__annotations__["active_preset"] = bpy.props.EnumProperty(
name="Active Preset",
description="The active preset",
cls.__annotations__['active_preset'] = bpy.props.EnumProperty(
name='Active Preset',
description='The active preset',
items=[
(
preset_name,
@ -184,9 +178,7 @@ class MaxwellSimNode(bpy.types.Node):
for preset_name, preset_def in cls.presets.items()
],
default=list(cls.presets.keys())[0],
update=lambda self, context: (
self.sync_active_preset()()
),
update=lambda self, context: (self.sync_active_preset()()),
)
####################
@ -194,10 +186,10 @@ class MaxwellSimNode(bpy.types.Node):
####################
def sync_active_socket_set(self, context):
self.sync_sockets()
self.sync_prop("active_socket_set", context)
self.sync_prop('active_socket_set', context)
def sync_sim_node_name(self, context):
if (mobjs := CACHE[self.instance_id].get("managed_objs")) is None:
if (mobjs := CACHE[self.instance_id].get('managed_objs')) is None:
return
for mobj_id, mobj in mobjs.items():
@ -226,61 +218,60 @@ class MaxwellSimNode(bpy.types.Node):
## - We sync our 'sim_node_name' with all managed objects.
## - (There is also a class-defined 'name_prefix' to differentiate)
## - See the 'sim_node_name' w/its sync function.
if CACHE[self.instance_id].get("managed_objs") is None:
if CACHE[self.instance_id].get('managed_objs') is None:
# Initialize the Managed Object Instance Cache
CACHE[self.instance_id]["managed_objs"] = {}
CACHE[self.instance_id]['managed_objs'] = {}
# Fill w/Managed Objects by Name Socket
for mobj_id, mobj_def in self.managed_obj_defs.items():
name = mobj_def.name_prefix + self.sim_node_name
CACHE[self.instance_id]["managed_objs"][mobj_id] = (
mobj_def.mk(name)
CACHE[self.instance_id]['managed_objs'][mobj_id] = mobj_def.mk(
name
)
return CACHE[self.instance_id]["managed_objs"]
return CACHE[self.instance_id]['managed_objs']
return CACHE[self.instance_id]["managed_objs"]
return CACHE[self.instance_id]['managed_objs']
####################
# - Socket Properties
####################
def active_bl_sockets(self, direc: typx.Literal["input", "output"]):
return self.inputs if direc == "input" else self.outputs
def active_bl_sockets(self, direc: typx.Literal['input', 'output']):
return self.inputs if direc == 'input' else self.outputs
def active_socket_set_sockets(
self,
direc: typx.Literal["input", "output"],
direc: typx.Literal['input', 'output'],
) -> dict:
# No Active Socket Set: Return Nothing
if not self.active_socket_set: return {}
if not self.active_socket_set:
return {}
# Retrieve Active Socket Set Sockets
socket_sets = (
self.input_socket_sets
if direc == "input" else self.output_socket_sets
)
active_socket_set_sockets = socket_sets.get(
self.active_socket_set
if direc == 'input'
else self.output_socket_sets
)
active_socket_set_sockets = socket_sets.get(self.active_socket_set)
# Return Active Socket Set Sockets (if any)
if not active_socket_set_sockets: return {}
if not active_socket_set_sockets:
return {}
return active_socket_set_sockets
def active_sockets(self, direc: typx.Literal["input", "output"]):
def active_sockets(self, direc: typx.Literal['input', 'output']):
static_sockets = (
self.input_sockets
if direc == "input"
else self.output_sockets
self.input_sockets if direc == 'input' else self.output_sockets
)
socket_sets = (
self.input_socket_sets
if direc == "input"
if direc == 'input'
else self.output_socket_sets
)
loose_sockets = (
self.loose_input_sockets
if direc == "input"
if direc == 'input'
else self.loose_output_sockets
)
@ -296,42 +287,50 @@ class MaxwellSimNode(bpy.types.Node):
# Loose Sockets
## Only Blender props persist as instance data
ser_loose_input_sockets: bpy.props.StringProperty(
name="Serialized Loose Input Sockets",
description="JSON-serialized representation of loose input sockets.",
name='Serialized Loose Input Sockets',
description='JSON-serialized representation of loose input sockets.',
default=_DEFAULT_LOOSE_SOCKET_SER,
)
ser_loose_output_sockets: bpy.props.StringProperty(
name="Serialized Loose Input Sockets",
description="JSON-serialized representation of loose input sockets.",
name='Serialized Loose Input Sockets',
description='JSON-serialized representation of loose input sockets.',
default=_DEFAULT_LOOSE_SOCKET_SER,
)
## Internal Serialization/Deserialization Methods (yuck)
def _ser_loose_sockets(self, deser: dict[str, ct.schemas.SocketDef]) -> str:
if not all(isinstance(model, pyd.BaseModel) for model in deser.values()):
msg = "Trying to deserialize loose sockets with invalid SocketDefs (they must be `pydantic` BaseModels)."
def _ser_loose_sockets(
self, deser: dict[str, ct.schemas.SocketDef]
) -> str:
if not all(
isinstance(model, pyd.BaseModel) for model in deser.values()
):
msg = 'Trying to deserialize loose sockets with invalid SocketDefs (they must be `pydantic` BaseModels).'
raise ValueError(msg)
return json.dumps({
"socket_names": list(deser.keys()),
"socket_def_names": [
model.__class__.__name__
for model in deser.values()
return json.dumps(
{
'socket_names': list(deser.keys()),
'socket_def_names': [
model.__class__.__name__ for model in deser.values()
],
"models": [
'models': [
model.model_dump()
for model in deser.values()
if isinstance(model, pyd.BaseModel)
],
}) ## Big reliance on order-preservation of dicts here.)
def _deser_loose_sockets(self, ser: str) -> dict[str, ct.schemas.SocketDef]:
}
) ## Big reliance on order-preservation of dicts here.)
def _deser_loose_sockets(
self, ser: str
) -> dict[str, ct.schemas.SocketDef]:
semi_deser = json.loads(ser)
return {
socket_name: getattr(sockets, socket_def_name)(**model_kwargs)
for socket_name, socket_def_name, model_kwargs in zip(
semi_deser["socket_names"],
semi_deser["socket_def_names"],
semi_deser["models"],
semi_deser['socket_names'],
semi_deser['socket_def_names'],
semi_deser['models'],
)
if hasattr(sockets, socket_def_name)
}
@ -339,17 +338,22 @@ class MaxwellSimNode(bpy.types.Node):
@property
def loose_input_sockets(self) -> dict[str, ct.schemas.SocketDef]:
return self._deser_loose_sockets(self.ser_loose_input_sockets)
@property
def loose_output_sockets(self) -> dict[str, ct.schemas.SocketDef]:
return self._deser_loose_sockets(self.ser_loose_output_sockets)
## TODO: Some caching may play a role if this is all too slow.
@loose_input_sockets.setter
def loose_input_sockets(
self, value: dict[str, ct.schemas.SocketDef],
self,
value: dict[str, ct.schemas.SocketDef],
) -> None:
if not value: self.ser_loose_input_sockets = _DEFAULT_LOOSE_SOCKET_SER
else: self.ser_loose_input_sockets = self._ser_loose_sockets(value)
if not value:
self.ser_loose_input_sockets = _DEFAULT_LOOSE_SOCKET_SER
else:
self.ser_loose_input_sockets = self._ser_loose_sockets(value)
# Synchronize Sockets
self.sync_sockets()
@ -357,10 +361,13 @@ class MaxwellSimNode(bpy.types.Node):
@loose_output_sockets.setter
def loose_output_sockets(
self, value: dict[str, ct.schemas.SocketDef],
self,
value: dict[str, ct.schemas.SocketDef],
) -> None:
if not value: self.ser_loose_output_sockets = _DEFAULT_LOOSE_SOCKET_SER
else: self.ser_loose_output_sockets = self._ser_loose_sockets(value)
if not value:
self.ser_loose_output_sockets = _DEFAULT_LOOSE_SOCKET_SER
else:
self.ser_loose_output_sockets = self._ser_loose_sockets(value)
# Synchronize Sockets
self.sync_sockets()
@ -374,7 +381,7 @@ class MaxwellSimNode(bpy.types.Node):
**NOTE**: Socket names must be unique within direction, active socket set, and loose socket set.
"""
for direc in ["input", "output"]:
for direc in ['input', 'output']:
sockets = self.active_sockets(direc)
bl_sockets = self.active_bl_sockets(direc)
@ -394,7 +401,7 @@ class MaxwellSimNode(bpy.types.Node):
Existing sockets within the given direction are not re-created.
"""
for direc in ["input", "output"]:
for direc in ['input', 'output']:
sockets = self.active_sockets(direc)
bl_sockets = self.active_bl_sockets(direc)
@ -402,7 +409,8 @@ class MaxwellSimNode(bpy.types.Node):
created_sockets = {}
for socket_name, socket_def in sockets.items():
# Skip Existing Sockets
if socket_name in bl_sockets: continue
if socket_name in bl_sockets:
continue
# Create BL Socket from Socket
bl_socket = bl_sockets.new(
@ -440,12 +448,12 @@ class MaxwellSimNode(bpy.types.Node):
preset-defined input sockets.
"""
if not (preset_def := self.presets.get(self.active_preset)):
msg = f"Tried to apply active preset, but the active preset ({self.active_preset}) is not in presets ({self.presets})"
msg = f'Tried to apply active preset, but the active preset ({self.active_preset}) is not in presets ({self.presets})'
raise RuntimeError(msg)
for socket_name, socket_value in preset_def.values.items():
if not (bl_socket := self.inputs.get(socket_name)):
msg = f"Tried to set preset socket/value pair ({socket_name}={socket_value}), but socket is not in active input sockets ({self.inputs})"
msg = f'Tried to set preset socket/value pair ({socket_name}={socket_value}), but socket is not in active input sockets ({self.inputs})'
raise ValueError(msg)
bl_socket.value = socket_value
@ -459,20 +467,21 @@ class MaxwellSimNode(bpy.types.Node):
context: bpy.types.Context,
layout: bpy.types.UILayout,
) -> None:
if self.locked: layout.enabled = False
if self.locked:
layout.enabled = False
if self.active_preset:
layout.prop(self, "active_preset", text="")
layout.prop(self, 'active_preset', text='')
if self.active_socket_set:
layout.prop(self, "active_socket_set", text="")
layout.prop(self, 'active_socket_set', text='')
# Draw Name
col = layout.column(align=False)
if self.use_sim_node_name:
row = col.row(align=True)
row.label(text="", icon="FILE_TEXT")
row.prop(self, "sim_node_name", text="")
row.label(text='', icon='FILE_TEXT')
row.prop(self, 'sim_node_name', text='')
# Draw Name
self.draw_props(context, col)
@ -480,15 +489,23 @@ class MaxwellSimNode(bpy.types.Node):
self.draw_info(context, col)
## TODO: Managed Operators instead of this shit
def draw_props(self, context, layout): pass
def draw_operators(self, context, layout): pass
def draw_info(self, context, layout): pass
def draw_props(self, context, layout):
pass
def draw_operators(self, context, layout):
pass
def draw_info(self, context, layout):
pass
def draw_buttons_ext(self, context, layout):
pass
def draw_buttons_ext(self, context, layout): pass
## TODO: Side panel buttons for fanciness.
def draw_plot_settings(self, context, layout):
if self.locked: layout.enabled = False
if self.locked:
layout.enabled = False
####################
# - Data Flow
@ -535,7 +552,7 @@ class MaxwellSimNode(bpy.types.Node):
(output_socket_name, kind)
)
):
msg = f"No output method for ({output_socket_name}, {str(kind.value)}"
msg = f'No output method for ({output_socket_name}, {str(kind.value)}'
raise ValueError(msg)
return output_socket_method(self)
@ -544,17 +561,22 @@ class MaxwellSimNode(bpy.types.Node):
# - Action Chain
####################
def sync_prop(self, prop_name: str, context: bpy.types.Context):
"""Called when a property has been updated.
"""
"""Called when a property has been updated."""
if not hasattr(self, prop_name):
msg = f"Property {prop_name} not defined on socket {self}"
msg = f'Property {prop_name} not defined on socket {self}'
raise RuntimeError(msg)
self.trigger_action("value_changed", prop_name=prop_name)
self.trigger_action('value_changed', prop_name=prop_name)
def trigger_action(
self,
action: typx.Literal["enable_lock", "disable_lock", "value_changed", "show_preview", "show_plot"],
action: typx.Literal[
'enable_lock',
'disable_lock',
'value_changed',
'show_preview',
'show_plot',
],
socket_name: ct.SocketName | None = None,
prop_name: ct.SocketName | None = None,
) -> None:
@ -564,63 +586,69 @@ class MaxwellSimNode(bpy.types.Node):
output socket method that implicitly depends on this input socket.
"""
# Forwards Chains
if action == "value_changed":
if action == 'value_changed':
# Run User Callbacks
## Careful with these, they run BEFORE propagation...
## ...because later-chain methods may rely on the results of this.
for method in self._on_value_changed_methods:
if (
(
socket_name
and socket_name in method._extra_data.get("changed_sockets")
) or (
and socket_name
in method._extra_data.get('changed_sockets')
)
or (
prop_name
and prop_name in method._extra_data.get("changed_props")
) or (
and prop_name
in method._extra_data.get('changed_props')
)
or (
socket_name
and method._extra_data["changed_loose_input"]
and method._extra_data['changed_loose_input']
and socket_name in self.loose_input_sockets
)
):
method(self)
# Propagate via Output Sockets
for bl_socket in self.active_bl_sockets("output"):
for bl_socket in self.active_bl_sockets('output'):
bl_socket.trigger_action(action)
# Backwards Chains
elif action == "enable_lock":
elif action == 'enable_lock':
self.locked = True
## Propagate via Input Sockets
for bl_socket in self.active_bl_sockets("input"):
for bl_socket in self.active_bl_sockets('input'):
bl_socket.trigger_action(action)
elif action == "disable_lock":
elif action == 'disable_lock':
self.locked = False
## Propagate via Input Sockets
for bl_socket in self.active_bl_sockets("input"):
for bl_socket in self.active_bl_sockets('input'):
bl_socket.trigger_action(action)
elif action == "show_preview":
elif action == 'show_preview':
# Run User Callbacks
for method in self._on_show_preview:
method(self)
## Propagate via Input Sockets
for bl_socket in self.active_bl_sockets("input"):
for bl_socket in self.active_bl_sockets('input'):
bl_socket.trigger_action(action)
elif action == "show_plot":
elif action == 'show_plot':
# Run User Callbacks
## These shouldn't change any data, BUT...
## ...because they can stop propagation, they should go first.
for method in self._on_show_plot:
method(self)
if method._extra_data["stop_propagation"]:
if method._extra_data['stop_propagation']:
return
## Propagate via Input Sockets
for bl_socket in self.active_bl_sockets("input"):
for bl_socket in self.active_bl_sockets('input'):
bl_socket.trigger_action(action)
####################
@ -636,8 +664,7 @@ class MaxwellSimNode(bpy.types.Node):
return node_tree.bl_idname == ct.TreeType.MaxwellSim.value
def init(self, context: bpy.types.Context):
"""Run (by Blender) on node creation.
"""
"""Run (by Blender) on node creation."""
global CACHE
# Initialize Cache and Instance ID
@ -652,7 +679,8 @@ class MaxwellSimNode(bpy.types.Node):
self.sync_sockets()
# Apply Default Preset
if self.active_preset: self.sync_active_preset()
if self.active_preset:
self.sync_active_preset()
# Callbacks
for method in self._on_init:
@ -662,8 +690,7 @@ class MaxwellSimNode(bpy.types.Node):
pass
def free(self) -> None:
"""Run (by Blender) when deleting the node.
"""
"""Run (by Blender) when deleting the node."""
global CACHE
if not CACHE.get(self.instance_id):
CACHE[self.instance_id] = {}
@ -679,7 +706,7 @@ class MaxwellSimNode(bpy.types.Node):
bl_socket.is_linked and bl_socket.locked
for bl_socket in self.inputs.values()
):
self.trigger_action("disable_lock")
self.trigger_action('disable_lock')
# Free Managed Objects
for managed_obj in self.managed_objs.values():
@ -696,18 +723,16 @@ class MaxwellSimNode(bpy.types.Node):
del CACHE[self.instance_id]
def chain_event_decorator(
callback_type: typ.Literal[
"computes_output_socket",
"on_value_changed",
"on_show_preview",
"on_show_plot",
"on_init",
'computes_output_socket',
'on_value_changed',
'on_show_preview',
'on_show_plot',
'on_init',
],
index_by: typ.Any | None = None,
extra_data: dict[str, typ.Any] | None = None,
kind: ct.DataFlowKind = ct.DataFlowKind.Value,
input_sockets: set[str] = set(), ## For now, presume
output_sockets: set[str] = set(), ## For now, presume
@ -715,8 +740,7 @@ def chain_event_decorator(
loose_output_sockets: bool = False,
props: set[str] = set(),
managed_objs: set[str] = set(),
req_params: set[str] = set()
req_params: set[str] = set(),
):
def decorator(method: typ.Callable) -> typ.Callable:
# Check Function Signature Validity
@ -724,11 +748,11 @@ def chain_event_decorator(
## Too Little
if func_sig != req_params and func_sig.issubset(req_params):
msg = f"Decorated method {method.__name__} is missing arguments {req_params - func_sig}"
msg = f'Decorated method {method.__name__} is missing arguments {req_params - func_sig}'
## Too Much
if func_sig != req_params and func_sig.issuperset(req_params):
msg = f"Decorated method {method.__name__} has superfluous arguments {func_sig - req_params}"
msg = f'Decorated method {method.__name__} has superfluous arguments {func_sig - req_params}'
raise ValueError(msg)
## Just Right :)
@ -743,7 +767,9 @@ def chain_event_decorator(
## Add Input Sockets
if input_sockets:
_input_sockets = {
input_socket_name: node._compute_input(input_socket_name, kind)
input_socket_name: node._compute_input(
input_socket_name, kind
)
for input_socket_name in input_sockets
}
method_kw_args |= dict(input_sockets=_input_sockets)
@ -751,7 +777,9 @@ def chain_event_decorator(
## Add Output Sockets
if output_sockets:
_output_sockets = {
output_socket_name: node.compute_output(output_socket_name, kind)
output_socket_name: node.compute_output(
output_socket_name, kind
)
for output_socket_name in output_sockets
}
method_kw_args |= dict(output_sockets=_output_sockets)
@ -759,7 +787,9 @@ def chain_event_decorator(
## Add Loose Sockets
if loose_input_sockets:
_loose_input_sockets = {
input_socket_name: node._compute_input(input_socket_name, kind)
input_socket_name: node._compute_input(
input_socket_name, kind
)
for input_socket_name in node.loose_input_sockets
}
method_kw_args |= dict(
@ -767,7 +797,9 @@ def chain_event_decorator(
)
if loose_output_sockets:
_loose_output_sockets = {
output_socket_name: node.compute_output(output_socket_name, kind)
output_socket_name: node.compute_output(
output_socket_name, kind
)
for output_socket_name in node.loose_output_sockets
}
method_kw_args |= dict(
@ -777,8 +809,7 @@ def chain_event_decorator(
## Add Props
if props:
_props = {
prop_name: getattr(node, prop_name)
for prop_name in props
prop_name: getattr(node, prop_name) for prop_name in props
}
method_kw_args |= dict(props=_props)
@ -804,6 +835,7 @@ def chain_event_decorator(
decorated._extra_data = extra_data
return decorated
return decorator
@ -841,16 +873,15 @@ def computes_output_socket(
and returns a new output-socket-computing method, now annotated
and discoverable by the `MaxwellSimTreeNode`.
"""
req_params = {"self"} | (
{"input_sockets"} if input_sockets else set()
) | (
{"props"} if props else set()
) | (
{"managed_objs"} if managed_objs else set()
req_params = (
{'self'}
| ({'input_sockets'} if input_sockets else set())
| ({'props'} if props else set())
| ({'managed_objs'} if managed_objs else set())
)
return chain_event_decorator(
callback_type="computes_output_socket",
callback_type='computes_output_socket',
index_by=(output_socket_name, kind),
kind=kind,
input_sockets=input_sockets,
@ -860,7 +891,6 @@ def computes_output_socket(
)
####################
# - Decorator: On Show Preview
####################
@ -868,40 +898,42 @@ def on_value_changed(
socket_name: set[ct.SocketName] | ct.SocketName | None = None,
prop_name: set[str] | str | None = None,
any_loose_input_socket: bool = False,
kind: ct.DataFlowKind = ct.DataFlowKind.Value,
input_sockets: set[str] = set(),
props: set[str] = set(),
managed_objs: set[str] = set(),
):
if sum([
if (
sum(
[
int(socket_name is not None),
int(prop_name is not None),
int(any_loose_input_socket),
]) > 1:
msg = "Define only one of socket_name, prop_name or any_loose_input_socket"
]
)
> 1
):
msg = 'Define only one of socket_name, prop_name or any_loose_input_socket'
raise ValueError(msg)
req_params = {"self"} | (
{"input_sockets"} if input_sockets else set()
) | (
{"loose_input_sockets"} if any_loose_input_socket else set()
) | (
{"props"} if props else set()
) | (
{"managed_objs"} if managed_objs else set()
req_params = (
{'self'}
| ({'input_sockets'} if input_sockets else set())
| ({'loose_input_sockets'} if any_loose_input_socket else set())
| ({'props'} if props else set())
| ({'managed_objs'} if managed_objs else set())
)
return chain_event_decorator(
callback_type="on_value_changed",
callback_type='on_value_changed',
extra_data={
"changed_sockets": (
'changed_sockets': (
socket_name if isinstance(socket_name, set) else {socket_name}
),
"changed_props": (
'changed_props': (
prop_name if isinstance(prop_name, set) else {prop_name}
),
"changed_loose_input": any_loose_input_socket,
'changed_loose_input': any_loose_input_socket,
},
kind=kind,
input_sockets=input_sockets,
@ -911,6 +943,7 @@ def on_value_changed(
req_params=req_params,
)
def on_show_preview(
kind: ct.DataFlowKind = ct.DataFlowKind.Value,
input_sockets: set[str] = set(), ## For now, presume only same kind
@ -918,18 +951,16 @@ def on_show_preview(
props: set[str] = set(),
managed_objs: set[str] = set(),
):
req_params = {"self"} | (
{"input_sockets"} if input_sockets else set()
) | (
{"output_sockets"} if output_sockets else set()
) | (
{"props"} if props else set()
) | (
{"managed_objs"} if managed_objs else set()
req_params = (
{'self'}
| ({'input_sockets'} if input_sockets else set())
| ({'output_sockets'} if output_sockets else set())
| ({'props'} if props else set())
| ({'managed_objs'} if managed_objs else set())
)
return chain_event_decorator(
callback_type="on_show_preview",
callback_type='on_show_preview',
kind=kind,
input_sockets=input_sockets,
output_sockets=output_sockets,
@ -938,6 +969,7 @@ def on_show_preview(
req_params=req_params,
)
def on_show_plot(
kind: ct.DataFlowKind = ct.DataFlowKind.Value,
input_sockets: set[str] = set(),
@ -946,20 +978,18 @@ def on_show_plot(
managed_objs: set[str] = set(),
stop_propagation: bool = False,
):
req_params = {"self"} | (
{"input_sockets"} if input_sockets else set()
) | (
{"output_sockets"} if output_sockets else set()
) | (
{"props"} if props else set()
) | (
{"managed_objs"} if managed_objs else set()
req_params = (
{'self'}
| ({'input_sockets'} if input_sockets else set())
| ({'output_sockets'} if output_sockets else set())
| ({'props'} if props else set())
| ({'managed_objs'} if managed_objs else set())
)
return chain_event_decorator(
callback_type="on_show_plot",
callback_type='on_show_plot',
extra_data={
"stop_propagation": stop_propagation,
'stop_propagation': stop_propagation,
},
kind=kind,
input_sockets=input_sockets,
@ -969,6 +999,7 @@ def on_show_plot(
req_params=req_params,
)
def on_init(
kind: ct.DataFlowKind = ct.DataFlowKind.Value,
input_sockets: set[str] = set(),
@ -976,18 +1007,16 @@ def on_init(
props: set[str] = set(),
managed_objs: set[str] = set(),
):
req_params = {"self"} | (
{"input_sockets"} if input_sockets else set()
) | (
{"output_sockets"} if output_sockets else set()
) | (
{"props"} if props else set()
) | (
{"managed_objs"} if managed_objs else set()
req_params = (
{'self'}
| ({'input_sockets'} if input_sockets else set())
| ({'output_sockets'} if output_sockets else set())
| ({'props'} if props else set())
| ({'managed_objs'} if managed_objs else set())
)
return chain_event_decorator(
callback_type="on_init",
callback_type='on_init',
kind=kind,
input_sockets=input_sockets,
output_sockets=output_sockets,

View File

@ -6,40 +6,40 @@ from ... import contracts as ct
from ... import sockets
from .. import base
class BoundCondsNode(base.MaxwellSimNode):
node_type = ct.NodeType.BoundConds
bl_label = "Bound Box"
bl_label = 'Bound Box'
# bl_icon = ...
####################
# - Sockets
####################
input_sockets = {
"+X": sockets.MaxwellBoundCondSocketDef(),
"-X": sockets.MaxwellBoundCondSocketDef(),
"+Y": sockets.MaxwellBoundCondSocketDef(),
"-Y": sockets.MaxwellBoundCondSocketDef(),
"+Z": sockets.MaxwellBoundCondSocketDef(),
"-Z": sockets.MaxwellBoundCondSocketDef(),
'+X': sockets.MaxwellBoundCondSocketDef(),
'-X': sockets.MaxwellBoundCondSocketDef(),
'+Y': sockets.MaxwellBoundCondSocketDef(),
'-Y': sockets.MaxwellBoundCondSocketDef(),
'+Z': sockets.MaxwellBoundCondSocketDef(),
'-Z': sockets.MaxwellBoundCondSocketDef(),
}
output_sockets = {
"BCs": sockets.MaxwellBoundCondsSocketDef(),
'BCs': sockets.MaxwellBoundCondsSocketDef(),
}
####################
# - Output Socket Computation
####################
@base.computes_output_socket(
"BCs",
input_sockets={"+X", "-X", "+Y", "-Y", "+Z", "-Z"}
'BCs', input_sockets={'+X', '-X', '+Y', '-Y', '+Z', '-Z'}
)
def compute_simulation(self, input_sockets) -> td.BoundarySpec:
x_pos = input_sockets["+X"]
x_neg = input_sockets["-X"]
y_pos = input_sockets["+Y"]
y_neg = input_sockets["-Y"]
z_pos = input_sockets["+Z"]
z_neg = input_sockets["-Z"]
x_pos = input_sockets['+X']
x_neg = input_sockets['-X']
y_pos = input_sockets['+Y']
y_neg = input_sockets['-Y']
z_pos = input_sockets['+Z']
z_neg = input_sockets['-Z']
return td.BoundarySpec(
x=td.Boundary(
@ -57,15 +57,10 @@ class BoundCondsNode(base.MaxwellSimNode):
)
####################
# - Blender Registration
####################
BL_REGISTER = [
BoundCondsNode,
]
BL_NODES = {
ct.NodeType.BoundConds: (
ct.NodeCategory.MAXWELLSIM_BOUNDS
)
}
BL_NODES = {ct.NodeType.BoundConds: (ct.NodeCategory.MAXWELLSIM_BOUNDS)}

View File

@ -10,7 +10,6 @@ BL_REGISTER = [
*pml_bound_face.BL_REGISTER,
*pec_bound_face.BL_REGISTER,
*pmc_bound_face.BL_REGISTER,
*bloch_bound_face.BL_REGISTER,
*periodic_bound_face.BL_REGISTER,
*absorbing_bound_face.BL_REGISTER,
@ -19,7 +18,6 @@ BL_NODES = {
**pml_bound_face.BL_NODES,
**pec_bound_face.BL_NODES,
**pmc_bound_face.BL_NODES,
**bloch_bound_face.BL_NODES,
**periodic_bound_face.BL_NODES,
**absorbing_bound_face.BL_NODES,

View File

@ -9,18 +9,14 @@ from . import web_importers
BL_REGISTER = [
*wave_constant.BL_REGISTER,
# *unit_system.BL_REGISTER,
*constants.BL_REGISTER,
*web_importers.BL_REGISTER,
# *file_importers.BL_REGISTER,
]
BL_NODES = {
**wave_constant.BL_NODES,
# **unit_system.BL_NODES,
**constants.BL_NODES,
**web_importers.BL_NODES,
# *file_importers.BL_REGISTER,
}

View File

@ -1,5 +1,6 @@
# from . import scientific_constant
from . import number_constant
# from . import physical_constant
from . import blender_constant

View File

@ -4,25 +4,26 @@ from .... import contracts as ct
from .... import sockets
from ... import base
class BlenderConstantNode(base.MaxwellSimNode):
node_type = ct.NodeType.BlenderConstant
bl_label = "Blender Constant"
bl_label = 'Blender Constant'
input_socket_sets = {
"Object": {
"Value": sockets.BlenderObjectSocketDef(),
'Object': {
'Value': sockets.BlenderObjectSocketDef(),
},
"Collection": {
"Value": sockets.BlenderCollectionSocketDef(),
'Collection': {
'Value': sockets.BlenderCollectionSocketDef(),
},
"Text": {
"Value": sockets.BlenderTextSocketDef(),
'Text': {
'Value': sockets.BlenderTextSocketDef(),
},
"Image": {
"Value": sockets.BlenderImageSocketDef(),
'Image': {
'Value': sockets.BlenderImageSocketDef(),
},
"GeoNode Tree": {
"Value": sockets.BlenderGeoNodesSocketDef(),
'GeoNode Tree': {
'Value': sockets.BlenderGeoNodesSocketDef(),
},
}
output_socket_sets = input_socket_sets
@ -30,13 +31,9 @@ class BlenderConstantNode(base.MaxwellSimNode):
####################
# - Callbacks
####################
@base.computes_output_socket(
"Value",
input_sockets={"Value"}
)
@base.computes_output_socket('Value', input_sockets={'Value'})
def compute_value(self, input_sockets) -> typ.Any:
return input_sockets["Value"]
return input_sockets['Value']
####################
@ -46,7 +43,5 @@ BL_REGISTER = [
BlenderConstantNode,
]
BL_NODES = {
ct.NodeType.BlenderConstant: (
ct.NodeCategory.MAXWELLSIM_INPUTS_CONSTANTS
)
ct.NodeType.BlenderConstant: (ct.NodeCategory.MAXWELLSIM_INPUTS_CONSTANTS)
}

View File

@ -7,22 +7,23 @@ from .... import contracts as ct
from .... import sockets
from ... import base
class NumberConstantNode(base.MaxwellSimNode):
node_type = ct.NodeType.NumberConstant
bl_label = "Numerical Constant"
bl_label = 'Numerical Constant'
input_socket_sets = {
"Integer": {
"Value": sockets.IntegerNumberSocketDef(),
'Integer': {
'Value': sockets.IntegerNumberSocketDef(),
},
"Rational": {
"Value": sockets.RationalNumberSocketDef(),
'Rational': {
'Value': sockets.RationalNumberSocketDef(),
},
"Real": {
"Value": sockets.RealNumberSocketDef(),
'Real': {
'Value': sockets.RealNumberSocketDef(),
},
"Complex": {
"Value": sockets.ComplexNumberSocketDef(),
'Complex': {
'Value': sockets.ComplexNumberSocketDef(),
},
}
output_socket_sets = input_socket_sets
@ -30,13 +31,9 @@ class NumberConstantNode(base.MaxwellSimNode):
####################
# - Callbacks
####################
@base.computes_output_socket(
"Value",
input_sockets={"Value"}
)
@base.computes_output_socket('Value', input_sockets={'Value'})
def compute_value(self, input_sockets) -> typ.Any:
return input_sockets["Value"]
return input_sockets['Value']
####################
@ -46,7 +43,5 @@ BL_REGISTER = [
NumberConstantNode,
]
BL_NODES = {
ct.NodeType.NumberConstant: (
ct.NodeCategory.MAXWELLSIM_INPUTS_CONSTANTS
)
ct.NodeType.NumberConstant: (ct.NodeCategory.MAXWELLSIM_INPUTS_CONSTANTS)
}

View File

@ -5,47 +5,48 @@ from .... import contracts
from .... import sockets
from ... import base
class PhysicalConstantNode(base.MaxwellSimTreeNode):
node_type = contracts.NodeType.PhysicalConstant
bl_label = "Physical Constant"
bl_label = 'Physical Constant'
# bl_icon = constants.ICON_SIM_INPUT
input_sockets = {}
input_socket_sets = {
"time": {
"value": sockets.PhysicalTimeSocketDef(
label="Time",
'time': {
'value': sockets.PhysicalTimeSocketDef(
label='Time',
),
},
"angle": {
"value": sockets.PhysicalAngleSocketDef(
label="Angle",
'angle': {
'value': sockets.PhysicalAngleSocketDef(
label='Angle',
),
},
"length": {
"value": sockets.PhysicalLengthSocketDef(
label="Length",
'length': {
'value': sockets.PhysicalLengthSocketDef(
label='Length',
),
},
"area": {
"value": sockets.PhysicalAreaSocketDef(
label="Area",
'area': {
'value': sockets.PhysicalAreaSocketDef(
label='Area',
),
},
"volume": {
"value": sockets.PhysicalVolumeSocketDef(
label="Volume",
'volume': {
'value': sockets.PhysicalVolumeSocketDef(
label='Volume',
),
},
"point_3d": {
"value": sockets.PhysicalPoint3DSocketDef(
label="3D Point",
'point_3d': {
'value': sockets.PhysicalPoint3DSocketDef(
label='3D Point',
),
},
"size_3d": {
"value": sockets.PhysicalSize3DSocketDef(
label="3D Size",
'size_3d': {
'value': sockets.PhysicalSize3DSocketDef(
label='3D Size',
),
},
## I got bored so maybe the rest later
@ -56,10 +57,9 @@ class PhysicalConstantNode(base.MaxwellSimTreeNode):
####################
# - Callbacks
####################
@base.computes_output_socket("value")
@base.computes_output_socket('value')
def compute_value(self: contracts.NodeTypeProtocol) -> sp.Expr:
return self.compute_input("value")
return self.compute_input('value')
####################

View File

@ -5,29 +5,29 @@ from ... import contracts as ct
from ... import sockets
from .. import base
class PhysicalUnitSystemNode(base.MaxwellSimNode):
node_type = ct.NodeType.UnitSystem
bl_label = "Unit System"
bl_label = 'Unit System'
input_sockets = {
"Unit System": sockets.PhysicalUnitSystemSocketDef(
'Unit System': sockets.PhysicalUnitSystemSocketDef(
show_by_default=True,
),
}
output_sockets = {
"Unit System": sockets.PhysicalUnitSystemSocketDef(),
'Unit System': sockets.PhysicalUnitSystemSocketDef(),
}
####################
# - Callbacks
####################
@base.computes_output_socket(
"Unit System",
input_sockets = {"Unit System"},
'Unit System',
input_sockets={'Unit System'},
)
def compute_value(self, input_sockets) -> dict:
return input_sockets["Unit System"]
return input_sockets['Unit System']
####################
@ -36,8 +36,4 @@ class PhysicalUnitSystemNode(base.MaxwellSimNode):
BL_REGISTER = [
PhysicalUnitSystemNode,
]
BL_NODES = {
ct.NodeType.UnitSystem: (
ct.NodeCategory.MAXWELLSIM_INPUTS
)
}
BL_NODES = {ct.NodeType.UnitSystem: (ct.NodeCategory.MAXWELLSIM_INPUTS)}

View File

@ -8,39 +8,36 @@ from ... import contracts as ct
from ... import sockets
from .. import base
VAC_SPEED_OF_LIGHT = (
sc.constants.speed_of_light
* spu.meter/spu.second
)
VAC_SPEED_OF_LIGHT = sc.constants.speed_of_light * spu.meter / spu.second
class WaveConstantNode(base.MaxwellSimNode):
node_type = ct.NodeType.WaveConstant
bl_label = "Wave Constant"
bl_label = 'Wave Constant'
input_socket_sets = {
# Single
"Vacuum WL": {
"WL": sockets.PhysicalLengthSocketDef(
'Vacuum WL': {
'WL': sockets.PhysicalLengthSocketDef(
default_value=500 * spu.nm,
default_unit=spu.nm,
),
},
"Frequency": {
"Freq": sockets.PhysicalFreqSocketDef(
'Frequency': {
'Freq': sockets.PhysicalFreqSocketDef(
default_value=500 * spux.THz,
default_unit=spux.THz,
),
},
# Listy
"Vacuum WLs": {
"WLs": sockets.PhysicalLengthSocketDef(
'Vacuum WLs': {
'WLs': sockets.PhysicalLengthSocketDef(
is_list=True,
),
},
"Frequencies": {
"Freqs": sockets.PhysicalFreqSocketDef(
'Frequencies': {
'Freqs': sockets.PhysicalFreqSocketDef(
is_list=True,
),
},
@ -50,47 +47,47 @@ class WaveConstantNode(base.MaxwellSimNode):
# - Callbacks
####################
@base.computes_output_socket(
"WL",
input_sockets={"WL", "Freq"},
'WL',
input_sockets={'WL', 'Freq'},
)
def compute_vac_wl(self, input_sockets: dict) -> sp.Expr:
if (vac_wl := input_sockets["WL"]) is not None:
if (vac_wl := input_sockets['WL']) is not None:
return vac_wl
elif (freq := input_sockets["Freq"]) is not None:
elif (freq := input_sockets['Freq']) is not None:
return spu.convert_to(
VAC_SPEED_OF_LIGHT / freq,
spu.meter,
)
raise RuntimeError("Vac WL and Freq are both None")
raise RuntimeError('Vac WL and Freq are both None')
@base.computes_output_socket(
"Freq",
input_sockets={"WL", "Freq"},
'Freq',
input_sockets={'WL', 'Freq'},
)
def compute_freq(self, input_sockets: dict) -> sp.Expr:
if (vac_wl := input_sockets["WL"]) is not None:
if (vac_wl := input_sockets['WL']) is not None:
return spu.convert_to(
VAC_SPEED_OF_LIGHT / vac_wl,
spu.hertz,
)
elif (freq := input_sockets["Freq"]) is not None:
elif (freq := input_sockets['Freq']) is not None:
return freq
raise RuntimeError("Vac WL and Freq are both None")
raise RuntimeError('Vac WL and Freq are both None')
####################
# - Listy Callbacks
####################
@base.computes_output_socket(
"WLs",
input_sockets={"WLs", "Freqs"},
'WLs',
input_sockets={'WLs', 'Freqs'},
)
def compute_vac_wls(self, input_sockets: dict) -> sp.Expr:
if (vac_wls := input_sockets["WLs"]) is not None:
if (vac_wls := input_sockets['WLs']) is not None:
return vac_wls
elif (freqs := input_sockets["Freqs"]) is not None:
elif (freqs := input_sockets['Freqs']) is not None:
return [
spu.convert_to(
VAC_SPEED_OF_LIGHT / freq,
@ -99,14 +96,14 @@ class WaveConstantNode(base.MaxwellSimNode):
for freq in freqs
][::-1]
raise RuntimeError("Vac WLs and Freqs are both None")
raise RuntimeError('Vac WLs and Freqs are both None')
@base.computes_output_socket(
"Freqs",
input_sockets={"WLs", "Freqs"},
'Freqs',
input_sockets={'WLs', 'Freqs'},
)
def compute_freqs(self, input_sockets: dict) -> sp.Expr:
if (vac_wls := input_sockets["WLs"]) is not None:
if (vac_wls := input_sockets['WLs']) is not None:
return [
spu.convert_to(
VAC_SPEED_OF_LIGHT / vac_wl,
@ -114,33 +111,32 @@ class WaveConstantNode(base.MaxwellSimNode):
)
for vac_wl in vac_wls
][::-1]
elif (freqs := input_sockets["Freqs"]) is not None:
elif (freqs := input_sockets['Freqs']) is not None:
return freqs
raise RuntimeError("Vac WLs and Freqs are both None")
raise RuntimeError('Vac WLs and Freqs are both None')
####################
# - Callbacks
####################
@base.on_value_changed(
prop_name="active_socket_set",
props={"active_socket_set"}
prop_name='active_socket_set', props={'active_socket_set'}
)
def on_value_changed__active_socket_set(self, props: dict):
# Singular: Normal Output Sockets
if props["active_socket_set"] in {"Vacuum WL", "Frequency"}:
if props['active_socket_set'] in {'Vacuum WL', 'Frequency'}:
self.loose_output_sockets = {}
self.loose_output_sockets = {
"Freq": sockets.PhysicalFreqSocketDef(),
"WL": sockets.PhysicalLengthSocketDef(),
'Freq': sockets.PhysicalFreqSocketDef(),
'WL': sockets.PhysicalLengthSocketDef(),
}
# Plural: Listy Output Sockets
elif props["active_socket_set"] in {"Vacuum WLs", "Frequencies"}:
elif props['active_socket_set'] in {'Vacuum WLs', 'Frequencies'}:
self.loose_output_sockets = {}
self.loose_output_sockets = {
"Freqs": sockets.PhysicalFreqSocketDef(is_list=True),
"WLs": sockets.PhysicalLengthSocketDef(is_list=True),
'Freqs': sockets.PhysicalFreqSocketDef(is_list=True),
'WLs': sockets.PhysicalLengthSocketDef(is_list=True),
}
else:
@ -158,8 +154,4 @@ class WaveConstantNode(base.MaxwellSimNode):
BL_REGISTER = [
WaveConstantNode,
]
BL_NODES = {
ct.NodeType.WaveConstant: (
ct.NodeCategory.MAXWELLSIM_INPUTS
)
}
BL_NODES = {ct.NodeType.WaveConstant: (ct.NodeCategory.MAXWELLSIM_INPUTS)}

View File

@ -17,49 +17,50 @@ from ... import base
CACHE = {}
####################
# - Node
####################
class Tidy3DWebImporterNode(base.MaxwellSimNode):
node_type = ct.NodeType.Tidy3DWebImporter
bl_label = "Tidy3DWebImporter"
bl_label = 'Tidy3DWebImporter'
input_sockets = {
"Cloud Task": sockets.Tidy3DCloudTaskSocketDef(
'Cloud Task': sockets.Tidy3DCloudTaskSocketDef(
should_exist=True,
),
"Cache Path": sockets.FilePathSocketDef(
default_path=Path("loaded_simulation.hdf5")
)
'Cache Path': sockets.FilePathSocketDef(
default_path=Path('loaded_simulation.hdf5')
),
}
####################
# - Output Methods
####################
@base.computes_output_socket(
"FDTD Sim Data",
input_sockets={"Cloud Task", "Cache Path"},
'FDTD Sim Data',
input_sockets={'Cloud Task', 'Cache Path'},
)
def compute_fdtd_sim_data(self, input_sockets: dict) -> str:
global CACHE
if not CACHE.get(self.instance_id):
CACHE[self.instance_id] = {"fdtd_sim_data": None}
CACHE[self.instance_id] = {'fdtd_sim_data': None}
if CACHE[self.instance_id]["fdtd_sim_data"] is not None:
return CACHE[self.instance_id]["fdtd_sim_data"]
if CACHE[self.instance_id]['fdtd_sim_data'] is not None:
return CACHE[self.instance_id]['fdtd_sim_data']
if not (
(cloud_task := input_sockets["Cloud Task"]) is not None
(cloud_task := input_sockets['Cloud Task']) is not None
and isinstance(cloud_task, tdcloud.CloudTask)
and cloud_task.status == "success"
and cloud_task.status == 'success'
):
msg = "Won't attempt getting SimData"
raise RuntimeError(msg)
# Load the Simulation
cache_path = input_sockets["Cache Path"]
cache_path = input_sockets['Cache Path']
if cache_path is None:
print("CACHE PATH IS NONE WHY")
print('CACHE PATH IS NONE WHY')
return ## I guess?
if cache_path.is_file():
sim_data = td.SimulationData.from_file(str(cache_path))
@ -70,26 +71,25 @@ class Tidy3DWebImporterNode(base.MaxwellSimNode):
path=str(cache_path),
)
CACHE[self.instance_id]["fdtd_sim_data"] = sim_data
CACHE[self.instance_id]['fdtd_sim_data'] = sim_data
return sim_data
@base.computes_output_socket(
"FDTD Sim",
input_sockets={"Cloud Task"},
'FDTD Sim',
input_sockets={'Cloud Task'},
)
def compute_fdtd_sim(self, input_sockets: dict) -> str:
if not isinstance(
cloud_task := input_sockets["Cloud Task"],
tdcloud.CloudTask
cloud_task := input_sockets['Cloud Task'], tdcloud.CloudTask
):
msg ="Input cloud task does not exist"
msg = 'Input cloud task does not exist'
raise RuntimeError(msg)
# Load the Simulation
with tempfile.NamedTemporaryFile(delete=False) as f:
_path_tmp = Path(f.name)
_path_tmp.rename(f.name + ".json")
path_tmp = Path(f.name + ".json")
_path_tmp.rename(f.name + '.json')
path_tmp = Path(f.name + '.json')
sim = td_web.api.webapi.load_simulation(
cloud_task.task_id,
@ -103,18 +103,17 @@ class Tidy3DWebImporterNode(base.MaxwellSimNode):
# - Update
####################
@base.on_value_changed(
socket_name="Cloud Task",
input_sockets={"Cloud Task"}
socket_name='Cloud Task', input_sockets={'Cloud Task'}
)
def on_value_changed__cloud_task(self, input_sockets: dict):
if (
(cloud_task := input_sockets["Cloud Task"]) is not None
(cloud_task := input_sockets['Cloud Task']) is not None
and isinstance(cloud_task, tdcloud.CloudTask)
and cloud_task.status == "success"
and cloud_task.status == 'success'
):
self.loose_output_sockets = {
"FDTD Sim Data": sockets.MaxwellFDTDSimDataSocketDef(),
"FDTD Sim": sockets.MaxwellFDTDSimSocketDef(),
'FDTD Sim Data': sockets.MaxwellFDTDSimDataSocketDef(),
'FDTD Sim': sockets.MaxwellFDTDSimSocketDef(),
}
return

View File

@ -8,95 +8,91 @@ from .. import contracts as ct
from .. import sockets
from . import base
class KitchenSinkNode(base.MaxwellSimNode):
node_type = ct.NodeType.KitchenSink
bl_label = "Kitchen Sink"
bl_label = 'Kitchen Sink'
# bl_icon = ...
####################
# - Sockets
####################
input_sockets = {
"Static Data": sockets.AnySocketDef(),
'Static Data': sockets.AnySocketDef(),
}
input_socket_sets = {
"Basic": {
"Any": sockets.AnySocketDef(),
"Bool": sockets.BoolSocketDef(),
"FilePath": sockets.FilePathSocketDef(),
"Text": sockets.TextSocketDef(),
'Basic': {
'Any': sockets.AnySocketDef(),
'Bool': sockets.BoolSocketDef(),
'FilePath': sockets.FilePathSocketDef(),
'Text': sockets.TextSocketDef(),
},
"Number": {
"Integer": sockets.IntegerNumberSocketDef(),
"Rational": sockets.RationalNumberSocketDef(),
"Real": sockets.RealNumberSocketDef(),
"Complex": sockets.ComplexNumberSocketDef(),
'Number': {
'Integer': sockets.IntegerNumberSocketDef(),
'Rational': sockets.RationalNumberSocketDef(),
'Real': sockets.RealNumberSocketDef(),
'Complex': sockets.ComplexNumberSocketDef(),
},
"Vector": {
"Real 2D": sockets.Real2DVectorSocketDef(),
"Real 3D": sockets.Real3DVectorSocketDef(
'Vector': {
'Real 2D': sockets.Real2DVectorSocketDef(),
'Real 3D': sockets.Real3DVectorSocketDef(
default_value=sp.Matrix([0.0, 0.0, 0.0])
),
"Complex 2D": sockets.Complex2DVectorSocketDef(),
"Complex 3D": sockets.Complex3DVectorSocketDef(),
'Complex 2D': sockets.Complex2DVectorSocketDef(),
'Complex 3D': sockets.Complex3DVectorSocketDef(),
},
"Physical": {
"Time": sockets.PhysicalTimeSocketDef(),
'Physical': {
'Time': sockets.PhysicalTimeSocketDef(),
# "physical_point_2d": sockets.PhysicalPoint2DSocketDef(),
"Angle": sockets.PhysicalAngleSocketDef(),
"Length": sockets.PhysicalLengthSocketDef(),
"Area": sockets.PhysicalAreaSocketDef(),
"Volume": sockets.PhysicalVolumeSocketDef(),
"Point 3D": sockets.PhysicalPoint3DSocketDef(),
'Angle': sockets.PhysicalAngleSocketDef(),
'Length': sockets.PhysicalLengthSocketDef(),
'Area': sockets.PhysicalAreaSocketDef(),
'Volume': sockets.PhysicalVolumeSocketDef(),
'Point 3D': sockets.PhysicalPoint3DSocketDef(),
##"physical_size_2d": sockets.PhysicalSize2DSocketDef(),
"Size 3D": sockets.PhysicalSize3DSocketDef(),
"Mass": sockets.PhysicalMassSocketDef(),
"Speed": sockets.PhysicalSpeedSocketDef(),
"Accel Scalar": sockets.PhysicalAccelScalarSocketDef(),
"Force Scalar": sockets.PhysicalForceScalarSocketDef(),
'Size 3D': sockets.PhysicalSize3DSocketDef(),
'Mass': sockets.PhysicalMassSocketDef(),
'Speed': sockets.PhysicalSpeedSocketDef(),
'Accel Scalar': sockets.PhysicalAccelScalarSocketDef(),
'Force Scalar': sockets.PhysicalForceScalarSocketDef(),
# "physical_accel_3dvector": sockets.PhysicalAccel3DVectorSocketDef(),
##"physical_force_3dvector": sockets.PhysicalForce3DVectorSocketDef(),
"Pol": sockets.PhysicalPolSocketDef(),
"Freq": sockets.PhysicalFreqSocketDef(),
'Pol': sockets.PhysicalPolSocketDef(),
'Freq': sockets.PhysicalFreqSocketDef(),
},
"Blender": {
"Object": sockets.BlenderObjectSocketDef(),
"Collection": sockets.BlenderCollectionSocketDef(),
"Image": sockets.BlenderImageSocketDef(),
"GeoNodes": sockets.BlenderGeoNodesSocketDef(),
"Text": sockets.BlenderTextSocketDef(),
'Blender': {
'Object': sockets.BlenderObjectSocketDef(),
'Collection': sockets.BlenderCollectionSocketDef(),
'Image': sockets.BlenderImageSocketDef(),
'GeoNodes': sockets.BlenderGeoNodesSocketDef(),
'Text': sockets.BlenderTextSocketDef(),
},
"Maxwell": {
"Source": sockets.MaxwellSourceSocketDef(),
"Temporal Shape": sockets.MaxwellTemporalShapeSocketDef(),
"Medium": sockets.MaxwellMediumSocketDef(),
"Medium Non-Linearity": sockets.MaxwellMediumNonLinearitySocketDef(),
"Structure": sockets.MaxwellStructureSocketDef(),
"Bound Box": sockets.MaxwellBoundBoxSocketDef(),
"Bound Face": sockets.MaxwellBoundFaceSocketDef(),
"Monitor": sockets.MaxwellMonitorSocketDef(),
"FDTD Sim": sockets.MaxwellFDTDSimSocketDef(),
"Sim Grid": sockets.MaxwellSimGridSocketDef(),
"Sim Grid Axis": sockets.MaxwellSimGridAxisSocketDef(),
'Maxwell': {
'Source': sockets.MaxwellSourceSocketDef(),
'Temporal Shape': sockets.MaxwellTemporalShapeSocketDef(),
'Medium': sockets.MaxwellMediumSocketDef(),
'Medium Non-Linearity': sockets.MaxwellMediumNonLinearitySocketDef(),
'Structure': sockets.MaxwellStructureSocketDef(),
'Bound Box': sockets.MaxwellBoundBoxSocketDef(),
'Bound Face': sockets.MaxwellBoundFaceSocketDef(),
'Monitor': sockets.MaxwellMonitorSocketDef(),
'FDTD Sim': sockets.MaxwellFDTDSimSocketDef(),
'Sim Grid': sockets.MaxwellSimGridSocketDef(),
'Sim Grid Axis': sockets.MaxwellSimGridAxisSocketDef(),
},
}
output_sockets = {
"Static Data": sockets.AnySocketDef(),
'Static Data': sockets.AnySocketDef(),
}
output_socket_sets = input_socket_sets
####################
# - Blender Registration
####################
BL_REGISTER = [
KitchenSinkNode,
]
BL_NODES = {
ct.NodeType.KitchenSink: (
ct.NodeCategory.MAXWELLSIM_INPUTS
)
}
BL_NODES = {ct.NodeType.KitchenSink: (ct.NodeCategory.MAXWELLSIM_INPUTS)}

View File

@ -15,7 +15,6 @@ from . import library_medium
BL_REGISTER = [
*library_medium.BL_REGISTER,
# *pec_medium.BL_REGISTER,
# *isotropic_medium.BL_REGISTER,
# *anisotropic_medium.BL_REGISTER,
@ -31,7 +30,6 @@ BL_REGISTER = [
]
BL_NODES = {
**library_medium.BL_NODES,
# **pec_medium.BL_NODES,
# **isotropic_medium.BL_NODES,
# **anisotropic_medium.BL_NODES,

View File

@ -3,4 +3,3 @@
####################
BL_REGISTER = []
BL_NODES = {}

View File

@ -6,67 +6,72 @@ from ... import contracts
from ... import sockets
from .. import base
class DrudeLorentzMediumNode(base.MaxwellSimTreeNode):
node_type = contracts.NodeType.DrudeLorentzMedium
bl_label = "Drude-Lorentz Medium"
bl_label = 'Drude-Lorentz Medium'
# bl_icon = ...
####################
# - Sockets
####################
input_sockets = {
"eps_inf": sockets.RealNumberSocketDef(
label=f"εr_∞",
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}",
}
| {
f'del_eps{i}': sockets.RealNumberSocketDef(
label=f'Δεr_{i}',
)
for i in [1, 2, 3]
}
| {
f'f{i}': sockets.PhysicalFreqSocketDef(
label=f'f_{i}',
)
for i in [1, 2, 3]
}
| {
f'delta{i}': sockets.PhysicalFreqSocketDef(
label=f'δ_{i}',
)
for i in [1, 2, 3]
}
)
output_sockets = {
"medium": sockets.MaxwellMediumSocketDef(
label="Medium"
),
'medium': sockets.MaxwellMediumSocketDef(label='Medium'),
}
####################
# - Output Socket Computation
####################
@base.computes_output_socket("medium")
@base.computes_output_socket('medium')
def compute_medium(self: contracts.NodeTypeProtocol) -> td.Sellmeier:
## Retrieval
return td.Lorentz(
eps_inf=self.compute_input(f"eps_inf"),
eps_inf=self.compute_input(f'eps_inf'),
coeffs=[
(
self.compute_input(f"del_eps{i}"),
self.compute_input(f'del_eps{i}'),
spu.convert_to(
self.compute_input(f"f{i}"),
self.compute_input(f'f{i}'),
spu.hertz,
) / spu.hertz,
)
/ spu.hertz,
spu.convert_to(
self.compute_input(f"delta{i}"),
self.compute_input(f'delta{i}'),
spu.hertz,
) / spu.hertz,
)
/ spu.hertz,
)
for i in [1, 2, 3]
]
],
)
####################
# - Blender Registration
####################

View File

@ -3,4 +3,3 @@
####################
BL_REGISTER = []
BL_NODES = {}

View File

@ -14,27 +14,25 @@ from ... import sockets
from ... import managed_objs
from .. import base
VAC_SPEED_OF_LIGHT = (
sc.constants.speed_of_light
* spu.meter/spu.second
)
VAC_SPEED_OF_LIGHT = sc.constants.speed_of_light * spu.meter / spu.second
class LibraryMediumNode(base.MaxwellSimNode):
node_type = ct.NodeType.LibraryMedium
bl_label = "Library Medium"
bl_label = 'Library Medium'
####################
# - Sockets
####################
input_sockets = {}
output_sockets = {
"Medium": sockets.MaxwellMediumSocketDef(),
'Medium': sockets.MaxwellMediumSocketDef(),
}
managed_obj_defs = {
"nk_plot": ct.schemas.ManagedObjDef(
'nk_plot': ct.schemas.ManagedObjDef(
mk=lambda name: managed_objs.ManagedBLImage(name),
name_prefix="",
name_prefix='',
)
}
@ -42,25 +40,27 @@ class LibraryMediumNode(base.MaxwellSimNode):
# - Properties
####################
material: bpy.props.EnumProperty(
name="",
description="",
name='',
description='',
# icon="NODE_MATERIAL",
items=[
(
mat_key,
td.material_library[mat_key].name,
", ".join([
', '.join(
[
ref.journal
for ref in td.material_library[mat_key].variants[
td.material_library[mat_key].default
].reference
])
for ref in td.material_library[mat_key]
.variants[td.material_library[mat_key].default]
.reference
]
),
)
for mat_key in td.material_library
if mat_key != "graphene" ## For some reason, it's unique...
if mat_key != 'graphene' ## For some reason, it's unique...
],
default="Au",
update=(lambda self, context: self.sync_prop("material", context)),
default='Au',
update=(lambda self, context: self.sync_prop('material', context)),
)
@property
@ -71,12 +71,12 @@ class LibraryMediumNode(base.MaxwellSimNode):
spu.convert_to(
val * spu.hertz,
spuex.terahertz,
) / spuex.terahertz
)
/ spuex.terahertz
for val in mat.medium.frequency_range
]
return sp.pretty(
[freq_range[0].n(4), freq_range[1].n(4)],
use_unicode=True
[freq_range[0].n(4), freq_range[1].n(4)], use_unicode=True
)
@property
@ -87,38 +87,38 @@ class LibraryMediumNode(base.MaxwellSimNode):
spu.convert_to(
VAC_SPEED_OF_LIGHT / (val * spu.hertz),
spu.nanometer,
) / spu.nanometer
)
/ spu.nanometer
for val in reversed(mat.medium.frequency_range)
]
return sp.pretty(
[nm_range[0].n(4), nm_range[1].n(4)],
use_unicode=True
[nm_range[0].n(4), nm_range[1].n(4)], use_unicode=True
)
####################
# - UI
####################
def draw_props(self, context, layout):
layout.prop(self, "material", text="")
layout.prop(self, 'material', text='')
def draw_info(self, context, col):
# UI Drawing
split = col.split(factor=0.23, align=True)
_col = split.column(align=True)
_col.alignment = "LEFT"
_col.label(text="nm")
_col.label(text="THz")
_col.alignment = 'LEFT'
_col.label(text='nm')
_col.label(text='THz')
_col = split.column(align=True)
_col.alignment = "RIGHT"
_col.alignment = 'RIGHT'
_col.label(text=self.nm_range_str)
_col.label(text=self.freq_range_str)
####################
# - Output Sockets
####################
@base.computes_output_socket("Medium")
@base.computes_output_socket('Medium')
def compute_vac_wl(self) -> sp.Expr:
return td.material_library[self.material].medium
@ -126,8 +126,8 @@ class LibraryMediumNode(base.MaxwellSimNode):
# - Event Callbacks
####################
@base.on_show_plot(
managed_objs={"nk_plot"},
props={"material"},
managed_objs={'nk_plot'},
props={'material'},
stop_propagation=True, ## Plot only the first plottable node
)
def on_show_plot(
@ -135,28 +135,26 @@ class LibraryMediumNode(base.MaxwellSimNode):
managed_objs: dict[str, ct.schemas.ManagedObj],
props: dict[str, typ.Any],
):
medium = td.material_library[props["material"]].medium
medium = td.material_library[props['material']].medium
freq_range = [
spu.convert_to(
val * spu.hertz,
spuex.terahertz,
) / spu.hertz
)
/ spu.hertz
for val in medium.frequency_range
]
managed_objs["nk_plot"].mpl_plot_to_image(
managed_objs['nk_plot'].mpl_plot_to_image(
lambda ax: medium.plot(medium.frequency_range, ax=ax),
bl_select=True,
)
####################
# - Blender Registration
####################
BL_REGISTER = [
LibraryMediumNode,
]
BL_NODES = {
ct.NodeType.LibraryMedium: (
ct.NodeCategory.MAXWELLSIM_MEDIUMS
)
}
BL_NODES = {ct.NodeType.LibraryMedium: (ct.NodeCategory.MAXWELLSIM_MEDIUMS)}

View File

@ -3,4 +3,3 @@
####################
BL_REGISTER = []
BL_NODES = {}

View File

@ -3,4 +3,3 @@
####################
BL_REGISTER = []
BL_NODES = {}

View File

@ -3,4 +3,3 @@
####################
BL_REGISTER = []
BL_NODES = {}

View File

@ -6,67 +6,65 @@ from ... import contracts
from ... import sockets
from .. import base
class TripleSellmeierMediumNode(base.MaxwellSimTreeNode):
node_type = contracts.NodeType.TripleSellmeierMedium
bl_label = "Three-Parameter Sellmeier Medium"
bl_label = 'Three-Parameter Sellmeier Medium'
# bl_icon = ...
####################
# - Sockets
####################
input_sockets = {
f"B{i}": sockets.RealNumberSocketDef(
label=f"B{i}",
f'B{i}': sockets.RealNumberSocketDef(
label=f'B{i}',
)
for i in [1, 2, 3]
} | {
f"C{i}": sockets.PhysicalAreaSocketDef(
label=f"C{i}",
default_unit=spu.um**2
f'C{i}': sockets.PhysicalAreaSocketDef(
label=f'C{i}', default_unit=spu.um**2
)
for i in [1, 2, 3]
}
output_sockets = {
"medium": sockets.MaxwellMediumSocketDef(
label="Medium"
),
'medium': sockets.MaxwellMediumSocketDef(label='Medium'),
}
####################
# - Presets
####################
presets = {
"BK7": contracts.PresetDef(
label="BK7 Glass",
description="Borosilicate crown glass (known as BK7)",
'BK7': contracts.PresetDef(
label='BK7 Glass',
description='Borosilicate crown glass (known as BK7)',
values={
"B1": 1.03961212,
"B2": 0.231792344,
"B3": 1.01046945,
"C1": 6.00069867e-3 * spu.um**2,
"C2": 2.00179144e-2 * spu.um**2,
"C3": 103.560653 * spu.um**2,
}
'B1': 1.03961212,
'B2': 0.231792344,
'B3': 1.01046945,
'C1': 6.00069867e-3 * spu.um**2,
'C2': 2.00179144e-2 * spu.um**2,
'C3': 103.560653 * spu.um**2,
},
),
"FUSED_SILICA": contracts.PresetDef(
label="Fused Silica",
description="Fused silica aka. SiO2",
'FUSED_SILICA': contracts.PresetDef(
label='Fused Silica',
description='Fused silica aka. SiO2',
values={
"B1": 0.696166300,
"B2": 0.407942600,
"B3": 0.897479400,
"C1": 4.67914826e-3 * spu.um**2,
"C2": 1.35120631e-2 * spu.um**2,
"C3": 97.9340025 * spu.um**2,
}
'B1': 0.696166300,
'B2': 0.407942600,
'B3': 0.897479400,
'C1': 4.67914826e-3 * spu.um**2,
'C2': 1.35120631e-2 * spu.um**2,
'C3': 97.9340025 * spu.um**2,
},
),
}
####################
# - Output Socket Computation
####################
@base.computes_output_socket("medium")
@base.computes_output_socket('medium')
def compute_medium(self: contracts.NodeTypeProtocol) -> td.Sellmeier:
## Retrieval
# B1 = self.compute_input("B1")
@ -75,17 +73,19 @@ class TripleSellmeierMediumNode(base.MaxwellSimTreeNode):
## Processing
# C1 = spu.convert_to(C1_with_units, spu.um**2) / spu.um**2
return td.Sellmeier(coeffs = [
return td.Sellmeier(
coeffs=[
(
self.compute_input(f"B{i}"),
self.compute_input(f'B{i}'),
spu.convert_to(
self.compute_input(f"C{i}"),
self.compute_input(f'C{i}'),
spu.um**2,
) / spu.um**2
)
/ spu.um**2,
)
for i in [1, 2, 3]
])
]
)
####################

View File

@ -3,4 +3,3 @@
####################
BL_REGISTER = []
BL_NODES = {}

View File

@ -15,47 +15,48 @@ from ... import sockets
from ... import managed_objs
from .. import base
GEONODES_MONITOR_BOX = "monitor_box"
GEONODES_MONITOR_BOX = 'monitor_box'
class EHFieldMonitorNode(base.MaxwellSimNode):
node_type = ct.NodeType.EHFieldMonitor
bl_label = "E/H Field Monitor"
bl_label = 'E/H Field Monitor'
use_sim_node_name = True
####################
# - Sockets
####################
input_sockets = {
"Center": sockets.PhysicalPoint3DSocketDef(),
"Size": sockets.PhysicalSize3DSocketDef(),
"Samples/Space": sockets.Integer3DVectorSocketDef(
'Center': sockets.PhysicalPoint3DSocketDef(),
'Size': sockets.PhysicalSize3DSocketDef(),
'Samples/Space': sockets.Integer3DVectorSocketDef(
default_value=sp.Matrix([10, 10, 10])
),
}
input_socket_sets = {
"Freq Domain": {
"Freqs": sockets.PhysicalFreqSocketDef(
'Freq Domain': {
'Freqs': sockets.PhysicalFreqSocketDef(
is_list=True,
),
},
"Time Domain": {
"Rec Start": sockets.PhysicalTimeSocketDef(),
"Rec Stop": sockets.PhysicalTimeSocketDef(
'Time Domain': {
'Rec Start': sockets.PhysicalTimeSocketDef(),
'Rec Stop': sockets.PhysicalTimeSocketDef(
default_value=200 * spux.fs
),
"Samples/Time": sockets.IntegerNumberSocketDef(
'Samples/Time': sockets.IntegerNumberSocketDef(
default_value=100,
),
},
}
output_sockets = {
"Monitor": sockets.MaxwellMonitorSocketDef(),
'Monitor': sockets.MaxwellMonitorSocketDef(),
}
managed_obj_defs = {
"monitor_box": ct.schemas.ManagedObjDef(
'monitor_box': ct.schemas.ManagedObjDef(
mk=lambda name: managed_objs.ManagedBLObject(name),
name_prefix="",
name_prefix='',
)
}
@ -76,29 +77,36 @@ class EHFieldMonitorNode(base.MaxwellSimNode):
# - Output Sockets
####################
@base.computes_output_socket(
"Monitor",
'Monitor',
input_sockets={
"Rec Start", "Rec Stop", "Center", "Size", "Samples/Space",
"Samples/Time", "Freqs",
'Rec Start',
'Rec Stop',
'Center',
'Size',
'Samples/Space',
'Samples/Time',
'Freqs',
},
props={"active_socket_set", "sim_node_name"}
props={'active_socket_set', 'sim_node_name'},
)
def compute_monitor(self, input_sockets: dict, props: dict) -> td.FieldTimeMonitor:
_center = input_sockets["Center"]
_size = input_sockets["Size"]
_samples_space = input_sockets["Samples/Space"]
def compute_monitor(
self, input_sockets: dict, props: dict
) -> td.FieldTimeMonitor:
_center = input_sockets['Center']
_size = input_sockets['Size']
_samples_space = input_sockets['Samples/Space']
center = tuple(spu.convert_to(_center, spu.um) / spu.um)
size = tuple(spu.convert_to(_size, spu.um) / spu.um)
samples_space = tuple(_samples_space)
if props["active_socket_set"] == "Freq Domain":
freqs = input_sockets["Freqs"]
if props['active_socket_set'] == 'Freq Domain':
freqs = input_sockets['Freqs']
return td.FieldMonitor(
center=center,
size=size,
name=props["sim_node_name"],
name=props['sim_node_name'],
interval_space=samples_space,
freqs=[
float(spu.convert_to(freq, spu.hertz) / spu.hertz)
@ -106,9 +114,9 @@ class EHFieldMonitorNode(base.MaxwellSimNode):
],
)
else: ## Time Domain
_rec_start = input_sockets["Rec Start"]
_rec_stop = input_sockets["Rec Stop"]
samples_time = input_sockets["Samples/Time"]
_rec_start = input_sockets['Rec Start']
_rec_stop = input_sockets['Rec Stop']
samples_time = input_sockets['Samples/Time']
rec_start = spu.convert_to(_rec_start, spu.second) / spu.second
rec_stop = spu.convert_to(_rec_stop, spu.second) / spu.second
@ -116,7 +124,7 @@ class EHFieldMonitorNode(base.MaxwellSimNode):
return td.FieldTimeMonitor(
center=center,
size=size,
name=props["sim_node_name"],
name=props['sim_node_name'],
start=rec_start,
stop=rec_stop,
interval=samples_time,
@ -127,70 +135,65 @@ class EHFieldMonitorNode(base.MaxwellSimNode):
# - Preview - Changes to Input Sockets
####################
@base.on_value_changed(
socket_name={"Center", "Size"},
input_sockets={"Center", "Size"},
managed_objs={"monitor_box"},
socket_name={'Center', 'Size'},
input_sockets={'Center', 'Size'},
managed_objs={'monitor_box'},
)
def on_value_changed__center_size(
self,
input_sockets: dict,
managed_objs: dict[str, ct.schemas.ManagedObj],
):
_center = input_sockets["Center"]
center = tuple([
float(el)
for el in spu.convert_to(_center, spu.um) / spu.um
])
_center = input_sockets['Center']
center = tuple(
[float(el) for el in spu.convert_to(_center, spu.um) / spu.um]
)
_size = input_sockets["Size"]
size = tuple([
float(el)
for el in spu.convert_to(_size, spu.um) / spu.um
])
_size = input_sockets['Size']
size = tuple(
[float(el) for el in spu.convert_to(_size, spu.um) / spu.um]
)
## TODO: Preview unit system?? Presume um for now
# Retrieve Hard-Coded GeoNodes and Analyze Input
geo_nodes = bpy.data.node_groups[GEONODES_MONITOR_BOX]
geonodes_interface = analyze_geonodes.interface(
geo_nodes, direc="INPUT"
geo_nodes, direc='INPUT'
)
# Sync Modifier Inputs
managed_objs["monitor_box"].sync_geonodes_modifier(
managed_objs['monitor_box'].sync_geonodes_modifier(
geonodes_node_group=geo_nodes,
geonodes_identifier_to_value={
geonodes_interface["Size"].identifier: size,
geonodes_interface['Size'].identifier: size,
## TODO: Use 'bl_socket_map.value_to_bl`!
## - This accounts for auto-conversion, unit systems, etc. .
## - We could keep it in the node base class...
## - ...But it needs aligning with Blender, too. Hmm.
}
},
)
# Sync Object Position
managed_objs["monitor_box"].bl_object("MESH").location = center
managed_objs['monitor_box'].bl_object('MESH').location = center
####################
# - Preview - Show Preview
####################
@base.on_show_preview(
managed_objs={"monitor_box"},
managed_objs={'monitor_box'},
)
def on_show_preview(
self,
managed_objs: dict[str, ct.schemas.ManagedObj],
):
managed_objs["monitor_box"].show_preview("MESH")
managed_objs['monitor_box'].show_preview('MESH')
self.on_value_changed__center_size()
####################
# - Blender Registration
####################
BL_REGISTER = [
EHFieldMonitorNode,
]
BL_NODES = {
ct.NodeType.EHFieldMonitor: (
ct.NodeCategory.MAXWELLSIM_MONITORS
)
}
BL_NODES = {ct.NodeType.EHFieldMonitor: (ct.NodeCategory.MAXWELLSIM_MONITORS)}

View File

@ -3,4 +3,3 @@
####################
BL_REGISTER = []
BL_NODES = {}

View File

@ -15,48 +15,49 @@ from ... import sockets
from ... import managed_objs
from .. import base
GEONODES_MONITOR_BOX = "monitor_flux_box"
GEONODES_MONITOR_BOX = 'monitor_flux_box'
class FieldPowerFluxMonitorNode(base.MaxwellSimNode):
node_type = ct.NodeType.FieldPowerFluxMonitor
bl_label = "Field Power Flux Monitor"
bl_label = 'Field Power Flux Monitor'
use_sim_node_name = True
####################
# - Sockets
####################
input_sockets = {
"Center": sockets.PhysicalPoint3DSocketDef(),
"Size": sockets.PhysicalSize3DSocketDef(),
"Samples/Space": sockets.Integer3DVectorSocketDef(
'Center': sockets.PhysicalPoint3DSocketDef(),
'Size': sockets.PhysicalSize3DSocketDef(),
'Samples/Space': sockets.Integer3DVectorSocketDef(
default_value=sp.Matrix([10, 10, 10])
),
"Direction": sockets.BoolSocketDef(),
'Direction': sockets.BoolSocketDef(),
}
input_socket_sets = {
"Freq Domain": {
"Freqs": sockets.PhysicalFreqSocketDef(
'Freq Domain': {
'Freqs': sockets.PhysicalFreqSocketDef(
is_list=True,
),
},
"Time Domain": {
"Rec Start": sockets.PhysicalTimeSocketDef(),
"Rec Stop": sockets.PhysicalTimeSocketDef(
'Time Domain': {
'Rec Start': sockets.PhysicalTimeSocketDef(),
'Rec Stop': sockets.PhysicalTimeSocketDef(
default_value=200 * spux.fs
),
"Samples/Time": sockets.IntegerNumberSocketDef(
'Samples/Time': sockets.IntegerNumberSocketDef(
default_value=100,
),
},
}
output_sockets = {
"Monitor": sockets.MaxwellMonitorSocketDef(),
'Monitor': sockets.MaxwellMonitorSocketDef(),
}
managed_obj_defs = {
"monitor_box": ct.schemas.ManagedObjDef(
'monitor_box': ct.schemas.ManagedObjDef(
mk=lambda name: managed_objs.ManagedBLObject(name),
name_prefix="",
name_prefix='',
)
}
@ -77,31 +78,39 @@ class FieldPowerFluxMonitorNode(base.MaxwellSimNode):
# - Output Sockets
####################
@base.computes_output_socket(
"Monitor",
'Monitor',
input_sockets={
"Rec Start", "Rec Stop", "Center", "Size", "Samples/Space",
"Samples/Time", "Freqs", "Direction",
'Rec Start',
'Rec Stop',
'Center',
'Size',
'Samples/Space',
'Samples/Time',
'Freqs',
'Direction',
},
props={"active_socket_set", "sim_node_name"}
props={'active_socket_set', 'sim_node_name'},
)
def compute_monitor(self, input_sockets: dict, props: dict) -> td.FieldTimeMonitor:
_center = input_sockets["Center"]
_size = input_sockets["Size"]
_samples_space = input_sockets["Samples/Space"]
def compute_monitor(
self, input_sockets: dict, props: dict
) -> td.FieldTimeMonitor:
_center = input_sockets['Center']
_size = input_sockets['Size']
_samples_space = input_sockets['Samples/Space']
center = tuple(spu.convert_to(_center, spu.um) / spu.um)
size = tuple(spu.convert_to(_size, spu.um) / spu.um)
samples_space = tuple(_samples_space)
direction = "+" if input_sockets["Direction"] else "-"
direction = '+' if input_sockets['Direction'] else '-'
if props["active_socket_set"] == "Freq Domain":
freqs = input_sockets["Freqs"]
if props['active_socket_set'] == 'Freq Domain':
freqs = input_sockets['Freqs']
return td.FluxMonitor(
center=center,
size=size,
name=props["sim_node_name"],
name=props['sim_node_name'],
interval_space=samples_space,
freqs=[
float(spu.convert_to(freq, spu.hertz) / spu.hertz)
@ -110,9 +119,9 @@ class FieldPowerFluxMonitorNode(base.MaxwellSimNode):
normal_dir=direction,
)
else: ## Time Domain
_rec_start = input_sockets["Rec Start"]
_rec_stop = input_sockets["Rec Stop"]
samples_time = input_sockets["Samples/Time"]
_rec_start = input_sockets['Rec Start']
_rec_stop = input_sockets['Rec Stop']
samples_time = input_sockets['Samples/Time']
rec_start = spu.convert_to(_rec_start, spu.second) / spu.second
rec_stop = spu.convert_to(_rec_stop, spu.second) / spu.second
@ -120,7 +129,7 @@ class FieldPowerFluxMonitorNode(base.MaxwellSimNode):
return td.FieldTimeMonitor(
center=center,
size=size,
name=props["sim_node_name"],
name=props['sim_node_name'],
start=rec_start,
stop=rec_stop,
interval=samples_time,
@ -131,63 +140,64 @@ class FieldPowerFluxMonitorNode(base.MaxwellSimNode):
# - Preview - Changes to Input Sockets
####################
@base.on_value_changed(
socket_name={"Center", "Size"},
input_sockets={"Center", "Size", "Direction"},
managed_objs={"monitor_box"},
socket_name={'Center', 'Size'},
input_sockets={'Center', 'Size', 'Direction'},
managed_objs={'monitor_box'},
)
def on_value_changed__center_size(
self,
input_sockets: dict,
managed_objs: dict[str, ct.schemas.ManagedObj],
):
_center = input_sockets["Center"]
center = tuple([
float(el)
for el in spu.convert_to(_center, spu.um) / spu.um
])
_center = input_sockets['Center']
center = tuple(
[float(el) for el in spu.convert_to(_center, spu.um) / spu.um]
)
_size = input_sockets["Size"]
size = tuple([
float(el)
for el in spu.convert_to(_size, spu.um) / spu.um
])
_size = input_sockets['Size']
size = tuple(
[float(el) for el in spu.convert_to(_size, spu.um) / spu.um]
)
## TODO: Preview unit system?? Presume um for now
# Retrieve Hard-Coded GeoNodes and Analyze Input
geo_nodes = bpy.data.node_groups[GEONODES_MONITOR_BOX]
geonodes_interface = analyze_geonodes.interface(
geo_nodes, direc="INPUT"
geo_nodes, direc='INPUT'
)
# Sync Modifier Inputs
managed_objs["monitor_box"].sync_geonodes_modifier(
managed_objs['monitor_box'].sync_geonodes_modifier(
geonodes_node_group=geo_nodes,
geonodes_identifier_to_value={
geonodes_interface["Size"].identifier: size,
geonodes_interface["Direction"].identifier: input_sockets["Direction"],
geonodes_interface['Size'].identifier: size,
geonodes_interface['Direction'].identifier: input_sockets[
'Direction'
],
## TODO: Use 'bl_socket_map.value_to_bl`!
## - This accounts for auto-conversion, unit systems, etc. .
## - We could keep it in the node base class...
## - ...But it needs aligning with Blender, too. Hmm.
}
},
)
# Sync Object Position
managed_objs["monitor_box"].bl_object("MESH").location = center
managed_objs['monitor_box'].bl_object('MESH').location = center
####################
# - Preview - Show Preview
####################
@base.on_show_preview(
managed_objs={"monitor_box"},
managed_objs={'monitor_box'},
)
def on_show_preview(
self,
managed_objs: dict[str, ct.schemas.ManagedObj],
):
managed_objs["monitor_box"].show_preview("MESH")
managed_objs['monitor_box'].show_preview('MESH')
self.on_value_changed__center_size()
####################
# - Blender Registration
####################
@ -195,7 +205,5 @@ BL_REGISTER = [
FieldPowerFluxMonitorNode,
]
BL_NODES = {
ct.NodeType.FieldPowerFluxMonitor: (
ct.NodeCategory.MAXWELLSIM_MONITORS
)
ct.NodeType.FieldPowerFluxMonitor: (ct.NodeCategory.MAXWELLSIM_MONITORS)
}

View File

@ -11,11 +11,12 @@ from .... import contracts as ct
from .... import sockets
from ... import base
####################
# - Operators
####################
class JSONFileExporterSaveJSON(bpy.types.Operator):
bl_idname = "blender_maxwell.json_file_exporter_save_json"
bl_idname = 'blender_maxwell.json_file_exporter_save_json'
bl_label = "Save the JSON of what's linked into a JSONFileExporterNode."
@classmethod
@ -27,26 +28,27 @@ class JSONFileExporterSaveJSON(bpy.types.Operator):
node.export_data_as_json()
return {'FINISHED'}
####################
# - Node
####################
class JSONFileExporterNode(base.MaxwellSimNode):
node_type = ct.NodeType.JSONFileExporter
bl_label = "JSON File Exporter"
bl_label = 'JSON File Exporter'
# bl_icon = constants.ICON_SIM_INPUT
input_sockets = {
"Data": sockets.AnySocketDef(),
"JSON Path": sockets.FilePathSocketDef(
default_path=Path("simulation.json")
'Data': sockets.AnySocketDef(),
'JSON Path': sockets.FilePathSocketDef(
default_path=Path('simulation.json')
),
"JSON Indent": sockets.IntegerNumberSocketDef(
'JSON Indent': sockets.IntegerNumberSocketDef(
default_value=4,
),
}
output_sockets = {
"JSON String": sockets.StringSocketDef(),
'JSON String': sockets.StringSocketDef(),
}
####################
@ -57,31 +59,33 @@ class JSONFileExporterNode(base.MaxwellSimNode):
context: bpy.types.Context,
layout: bpy.types.UILayout,
) -> None:
layout.operator(JSONFileExporterSaveJSON.bl_idname, text="Save JSON")
layout.operator(JSONFileExporterSaveJSON.bl_idname, text='Save JSON')
####################
# - Methods
####################
def export_data_as_json(self) -> None:
if (json_str := self.compute_output("JSON String")):
if json_str := self.compute_output('JSON String'):
data_dict = json.loads(json_str)
with self._compute_input("JSON Path").open("w") as f:
indent = self._compute_input("JSON Indent")
with self._compute_input('JSON Path').open('w') as f:
indent = self._compute_input('JSON Indent')
json.dump(data_dict, f, ensure_ascii=False, indent=indent)
####################
# - Output Sockets
####################
@base.computes_output_socket(
"JSON String",
input_sockets={"Data"},
'JSON String',
input_sockets={'Data'},
)
def compute_json_string(self, input_sockets: dict[str, typ.Any]) -> str | None:
if not (data := input_sockets["Data"]):
def compute_json_string(
self, input_sockets: dict[str, typ.Any]
) -> str | None:
if not (data := input_sockets['Data']):
return None
# Tidy3D Objects: Call .json()
if hasattr(data, "json"):
if hasattr(data, 'json'):
return data.json()
# Pydantic Models: Call .model_dump_json()

View File

@ -16,25 +16,25 @@ from .... import contracts as ct
from .... import sockets
from ... import base
####################
# - Web Uploader / Loader / Runner / Releaser
####################
class UploadSimulation(bpy.types.Operator):
bl_idname = "blender_maxwell.nodes__upload_simulation"
bl_label = "Upload Tidy3D Simulation"
bl_description = "Upload the attached (locked) simulation, such that it is ready to run on the Tidy3D cloud"
bl_idname = 'blender_maxwell.nodes__upload_simulation'
bl_label = 'Upload Tidy3D Simulation'
bl_description = 'Upload the attached (locked) simulation, such that it is ready to run on the Tidy3D cloud'
@classmethod
def poll(cls, context):
return (
hasattr(context, "node")
and hasattr(context.node, "node_type")
hasattr(context, 'node')
and hasattr(context.node, 'node_type')
and context.node.node_type == ct.NodeType.Tidy3DWebExporter
and context.node.lock_tree
and tdcloud.IS_AUTHENTICATED
and not context.node.tracked_task_id
and context.node.inputs["FDTD Sim"].is_linked
and context.node.inputs['FDTD Sim'].is_linked
)
def execute(self, context):
@ -42,24 +42,27 @@ class UploadSimulation(bpy.types.Operator):
node.upload_sim()
return {'FINISHED'}
class RunSimulation(bpy.types.Operator):
bl_idname = "blender_maxwell.nodes__run_simulation"
bl_label = "Run Tracked Tidy3D Sim"
bl_description = "Run the currently tracked simulation task"
bl_idname = 'blender_maxwell.nodes__run_simulation'
bl_label = 'Run Tracked Tidy3D Sim'
bl_description = 'Run the currently tracked simulation task'
@classmethod
def poll(cls, context):
return (
hasattr(context, "node")
and hasattr(context.node, "node_type")
hasattr(context, 'node')
and hasattr(context.node, 'node_type')
and context.node.node_type == ct.NodeType.Tidy3DWebExporter
and tdcloud.IS_AUTHENTICATED
and context.node.tracked_task_id
and (task_info := tdcloud.TidyCloudTasks.task_info(
and (
task_info := tdcloud.TidyCloudTasks.task_info(
context.node.tracked_task_id
)) is not None
and task_info.status == "draft"
)
)
is not None
and task_info.status == 'draft'
)
def execute(self, context):
@ -67,18 +70,18 @@ class RunSimulation(bpy.types.Operator):
node.run_tracked_task()
return {'FINISHED'}
class ReloadTrackedTask(bpy.types.Operator):
bl_idname = "blender_maxwell.nodes__reload_tracked_task"
bl_label = "Reload Tracked Tidy3D Cloud Task"
bl_description = "Reload the currently tracked simulation task"
bl_idname = 'blender_maxwell.nodes__reload_tracked_task'
bl_label = 'Reload Tracked Tidy3D Cloud Task'
bl_description = 'Reload the currently tracked simulation task'
@classmethod
def poll(cls, context):
return (
hasattr(context, "node")
and hasattr(context.node, "node_type")
hasattr(context, 'node')
and hasattr(context.node, 'node_type')
and context.node.node_type == ct.NodeType.Tidy3DWebExporter
and tdcloud.IS_AUTHENTICATED
and context.node.tracked_task_id
)
@ -94,18 +97,18 @@ class ReloadTrackedTask(bpy.types.Operator):
cloud_task = tdcloud.TidyCloudTasks.update_task(cloud_task)
return {'FINISHED'}
class EstCostTrackedTask(bpy.types.Operator):
bl_idname = "blender_maxwell.nodes__est_cost_tracked_task"
bl_label = "Est Cost of Tracked Tidy3D Cloud Task"
bl_description = "Reload the currently tracked simulation task"
bl_idname = 'blender_maxwell.nodes__est_cost_tracked_task'
bl_label = 'Est Cost of Tracked Tidy3D Cloud Task'
bl_description = 'Reload the currently tracked simulation task'
@classmethod
def poll(cls, context):
return (
hasattr(context, "node")
and hasattr(context.node, "node_type")
hasattr(context, 'node')
and hasattr(context.node, 'node_type')
and context.node.node_type == ct.NodeType.Tidy3DWebExporter
and tdcloud.IS_AUTHENTICATED
and context.node.tracked_task_id
)
@ -113,47 +116,50 @@ class EstCostTrackedTask(bpy.types.Operator):
def execute(self, context):
node = context.node
if (
task_info := tdcloud.TidyCloudTasks.task_info(context.node.tracked_task_id)
task_info := tdcloud.TidyCloudTasks.task_info(
context.node.tracked_task_id
)
) is None:
msg = "Tried to estimate cost of tracked task, but it doesn't exist"
msg = (
"Tried to estimate cost of tracked task, but it doesn't exist"
)
raise RuntimeError(msg)
node.cache_est_cost = task_info.cost_est()
return {'FINISHED'}
class ReleaseTrackedTask(bpy.types.Operator):
bl_idname = "blender_maxwell.nodes__release_tracked_task"
bl_label = "Release Tracked Tidy3D Cloud Task"
bl_description = "Release the currently tracked simulation task"
bl_idname = 'blender_maxwell.nodes__release_tracked_task'
bl_label = 'Release Tracked Tidy3D Cloud Task'
bl_description = 'Release the currently tracked simulation task'
@classmethod
def poll(cls, context):
return (
hasattr(context, "node")
and hasattr(context.node, "node_type")
hasattr(context, 'node')
and hasattr(context.node, 'node_type')
and context.node.node_type == ct.NodeType.Tidy3DWebExporter
# and tdcloud.IS_AUTHENTICATED
and context.node.tracked_task_id
)
def execute(self, context):
node = context.node
node.tracked_task_id = ""
node.tracked_task_id = ''
return {'FINISHED'}
####################
# - Node
####################
class Tidy3DWebExporterNode(base.MaxwellSimNode):
node_type = ct.NodeType.Tidy3DWebExporter
bl_label = "Tidy3D Web Exporter"
bl_label = 'Tidy3D Web Exporter'
input_sockets = {
"FDTD Sim": sockets.MaxwellFDTDSimSocketDef(),
"Cloud Task": sockets.Tidy3DCloudTaskSocketDef(
'FDTD Sim': sockets.MaxwellFDTDSimSocketDef(),
'Cloud Task': sockets.Tidy3DCloudTaskSocketDef(
should_exist=False,
),
}
@ -162,27 +168,27 @@ class Tidy3DWebExporterNode(base.MaxwellSimNode):
# - Properties
####################
lock_tree: bpy.props.BoolProperty(
name="Whether to lock the attached tree",
description="Whether or not to lock the attached tree",
name='Whether to lock the attached tree',
description='Whether or not to lock the attached tree',
default=False,
update=lambda self, context: self.sync_lock_tree(context),
)
tracked_task_id: bpy.props.StringProperty(
name="Tracked Task ID",
description="The currently tracked task ID",
default="",
name='Tracked Task ID',
description='The currently tracked task ID',
default='',
update=lambda self, context: self.sync_tracked_task_id(context),
)
# Cache
cache_total_monitor_data: bpy.props.FloatProperty(
name="(Cached) Total Monitor Data",
description="Required storage space by all monitors",
name='(Cached) Total Monitor Data',
description='Required storage space by all monitors',
default=0.0,
)
cache_est_cost: bpy.props.FloatProperty(
name="(Cached) Estimated Total Cost",
description="Est. Cost in FlexCompute units",
name='(Cached) Estimated Total Cost',
description='Est. Cost in FlexCompute units',
default=-1.0,
)
@ -191,16 +197,17 @@ class Tidy3DWebExporterNode(base.MaxwellSimNode):
####################
def sync_lock_tree(self, context):
if self.lock_tree:
self.trigger_action("enable_lock")
self.trigger_action('enable_lock')
self.locked = False
for bl_socket in self.inputs:
if bl_socket.name == "FDTD Sim": continue
if bl_socket.name == 'FDTD Sim':
continue
bl_socket.locked = False
else:
self.trigger_action("disable_lock")
self.trigger_action('disable_lock')
self.sync_prop("lock_tree", context)
self.sync_prop('lock_tree', context)
def sync_tracked_task_id(self, context):
# Select Tracked Task
@ -209,44 +216,43 @@ class Tidy3DWebExporterNode(base.MaxwellSimNode):
task_info = tdcloud.TidyCloudTasks.task_info(self.tracked_task_id)
self.loose_output_sockets = {
"Cloud Task": sockets.Tidy3DCloudTaskSocketDef(
'Cloud Task': sockets.Tidy3DCloudTaskSocketDef(
should_exist=True,
),
}
self.inputs["Cloud Task"].locked = True
self.inputs['Cloud Task'].locked = True
# Release Tracked Task
else:
self.cache_est_cost = -1.0
self.loose_output_sockets = {}
self.inputs["Cloud Task"].sync_prepare_new_task()
self.inputs["Cloud Task"].locked = False
self.inputs['Cloud Task'].sync_prepare_new_task()
self.inputs['Cloud Task'].locked = False
self.sync_prop("tracked_task_id", context)
self.sync_prop('tracked_task_id', context)
####################
# - Output Socket Callbacks
####################
def validate_sim(self):
if (sim := self._compute_input("FDTD Sim")) is None:
msg = "Tried to validate simulation, but none is attached"
if (sim := self._compute_input('FDTD Sim')) is None:
msg = 'Tried to validate simulation, but none is attached'
raise ValueError(msg)
sim.validate_pre_upload(source_required=True)
def upload_sim(self):
if (sim := self._compute_input("FDTD Sim")) is None:
msg = "Tried to upload simulation, but none is attached"
if (sim := self._compute_input('FDTD Sim')) is None:
msg = 'Tried to upload simulation, but none is attached'
raise ValueError(msg)
if (
(new_task := self._compute_input("Cloud Task")) is None
or isinstance(
new_task := self._compute_input('Cloud Task')
) is None or isinstance(
new_task,
tdcloud.CloudTask,
)
):
msg = "Tried to upload simulation to new task, but existing task was selected"
msg = 'Tried to upload simulation to new task, but existing task was selected'
raise ValueError(msg)
# Create Cloud Task
@ -261,7 +267,7 @@ class Tidy3DWebExporterNode(base.MaxwellSimNode):
# Declare to Cloud Task that it Exists Now
## This will change the UI to not allow free-text input.
## If the socket is linked, this errors.
self.inputs["Cloud Task"].sync_created_new_task(cloud_task)
self.inputs['Cloud Task'].sync_created_new_task(cloud_task)
# Track the Newly Uploaded Task ID
self.tracked_task_id = cloud_task.task_id
@ -274,7 +280,9 @@ class Tidy3DWebExporterNode(base.MaxwellSimNode):
raise RuntimeError(msg)
cloud_task.submit()
tdcloud.TidyCloudTasks.update_task(cloud_task) ## TODO: Check that status is actually immediately updated.
tdcloud.TidyCloudTasks.update_task(
cloud_task
) ## TODO: Check that status is actually immediately updated.
####################
# - UI
@ -284,93 +292,94 @@ class Tidy3DWebExporterNode(base.MaxwellSimNode):
row = layout.row(align=True)
row.operator(
UploadSimulation.bl_idname,
text="Upload",
text='Upload',
)
tree_lock_icon = "LOCKED" if self.lock_tree else "UNLOCKED"
row.prop(self, "lock_tree", toggle=True, icon=tree_lock_icon, text="")
tree_lock_icon = 'LOCKED' if self.lock_tree else 'UNLOCKED'
row.prop(self, 'lock_tree', toggle=True, icon=tree_lock_icon, text='')
# Row: Run Sim Buttons
row = layout.row(align=True)
row.operator(
RunSimulation.bl_idname,
text="Run",
text='Run',
)
if self.tracked_task_id:
tree_lock_icon = "LOOP_BACK"
tree_lock_icon = 'LOOP_BACK'
row.operator(
ReleaseTrackedTask.bl_idname,
icon="LOOP_BACK",
text="",
icon='LOOP_BACK',
text='',
)
def draw_info(self, context, layout):
# Connection Info
auth_icon = "CHECKBOX_HLT" if tdcloud.IS_AUTHENTICATED else "CHECKBOX_DEHLT"
conn_icon = "CHECKBOX_HLT" if tdcloud.IS_ONLINE else "CHECKBOX_DEHLT"
auth_icon = (
'CHECKBOX_HLT' if tdcloud.IS_AUTHENTICATED else 'CHECKBOX_DEHLT'
)
conn_icon = 'CHECKBOX_HLT' if tdcloud.IS_ONLINE else 'CHECKBOX_DEHLT'
row = layout.row()
row.alignment = "CENTER"
row.label(text="Cloud Status")
row.alignment = 'CENTER'
row.label(text='Cloud Status')
box = layout.box()
split = box.split(factor=0.85)
## Split: Left Column
col = split.column(align=False)
col.label(text="Authed")
col.label(text="Connected")
col.label(text='Authed')
col.label(text='Connected')
## Split: Right Column
col = split.column(align=False)
col.label(icon=auth_icon)
col.label(icon=conn_icon)
# Simulation Info
if self.inputs["FDTD Sim"].is_linked:
if self.inputs['FDTD Sim'].is_linked:
row = layout.row()
row.alignment = "CENTER"
row.label(text="Sim Info")
row.alignment = 'CENTER'
row.label(text='Sim Info')
box = layout.box()
split = box.split(factor=0.4)
## Split: Left Column
col = split.column(align=False)
col.label(text="𝝨 Output")
col.label(text='𝝨 Output')
## Split: Right Column
col = split.column(align=False)
col.alignment = "RIGHT"
col.label(text=f"{self.cache_total_monitor_data / 1_000_000:.2f}MB")
col.alignment = 'RIGHT'
col.label(
text=f'{self.cache_total_monitor_data / 1_000_000:.2f}MB'
)
# Cloud Task Info
if self.tracked_task_id and tdcloud.IS_AUTHENTICATED:
task_info = tdcloud.TidyCloudTasks.task_info(
self.tracked_task_id
)
if task_info is None: return
task_info = tdcloud.TidyCloudTasks.task_info(self.tracked_task_id)
if task_info is None:
return
## Header
row = layout.row()
row.alignment = "CENTER"
row.label(text="Task Info")
row.alignment = 'CENTER'
row.label(text='Task Info')
## Progress Bar
row = layout.row(align=True)
row.progress(
factor=0.0,
type="BAR",
text=f"Status: {task_info.status.capitalize()}",
type='BAR',
text=f'Status: {task_info.status.capitalize()}',
)
row.operator(
ReloadTrackedTask.bl_idname,
text="",
icon="FILE_REFRESH",
text='',
icon='FILE_REFRESH',
)
row.operator(
EstCostTrackedTask.bl_idname,
text="",
icon="SORTTIME",
text='',
icon='SORTTIME',
)
## Information
@ -379,19 +388,27 @@ class Tidy3DWebExporterNode(base.MaxwellSimNode):
## Split: Left Column
col = split.column(align=False)
col.label(text="Status")
col.label(text="Est. Cost")
col.label(text="Real Cost")
col.label(text='Status')
col.label(text='Est. Cost')
col.label(text='Real Cost')
## Split: Right Column
cost_est = f"{self.cache_est_cost:.2f}" if self.cache_est_cost >= 0 else "TBD"
cost_real = f"{task_info.cost_real:.2f}" if task_info.cost_real is not None else "TBD"
cost_est = (
f'{self.cache_est_cost:.2f}'
if self.cache_est_cost >= 0
else 'TBD'
)
cost_real = (
f'{task_info.cost_real:.2f}'
if task_info.cost_real is not None
else 'TBD'
)
col = split.column(align=False)
col.alignment = "RIGHT"
col.alignment = 'RIGHT'
col.label(text=task_info.status.capitalize())
col.label(text=f"{cost_est} creds")
col.label(text=f"{cost_real} creds")
col.label(text=f'{cost_est} creds')
col.label(text=f'{cost_real} creds')
# Connection Information
@ -399,13 +416,14 @@ class Tidy3DWebExporterNode(base.MaxwellSimNode):
# - Output Methods
####################
@base.computes_output_socket(
"Cloud Task",
input_sockets={"Cloud Task"},
'Cloud Task',
input_sockets={'Cloud Task'},
)
def compute_cloud_task(self, input_sockets: dict) -> tdcloud.CloudTask | None:
def compute_cloud_task(
self, input_sockets: dict
) -> tdcloud.CloudTask | None:
if isinstance(
cloud_task := input_sockets["Cloud Task"],
tdcloud.CloudTask
cloud_task := input_sockets['Cloud Task'], tdcloud.CloudTask
):
return cloud_task
@ -415,11 +433,11 @@ class Tidy3DWebExporterNode(base.MaxwellSimNode):
# - Output Methods
####################
@base.on_value_changed(
socket_name="FDTD Sim",
input_sockets={"FDTD Sim"},
socket_name='FDTD Sim',
input_sockets={'FDTD Sim'},
)
def on_value_changed__fdtd_sim(self, input_sockets):
if (sim := self._compute_input("FDTD Sim")) is None:
if (sim := self._compute_input('FDTD Sim')) is None:
self.cache_total_monitor_data = 0
return

View File

@ -15,8 +15,8 @@ from ...managed_objs import managed_bl_object
class ConsoleViewOperator(bpy.types.Operator):
bl_idname = "blender_maxwell.console_view_operator"
bl_label = "View Plots"
bl_idname = 'blender_maxwell.console_view_operator'
bl_label = 'View Plots'
@classmethod
def poll(cls, context):
@ -27,9 +27,10 @@ class ConsoleViewOperator(bpy.types.Operator):
node.print_data_to_console()
return {'FINISHED'}
class RefreshPlotViewOperator(bpy.types.Operator):
bl_idname = "blender_maxwell.refresh_plot_view_operator"
bl_label = "Refresh Plots"
bl_idname = 'blender_maxwell.refresh_plot_view_operator'
bl_label = 'Refresh Plots'
@classmethod
def poll(cls, context):
@ -37,35 +38,38 @@ class RefreshPlotViewOperator(bpy.types.Operator):
def execute(self, context):
node = context.node
node.trigger_action("value_changed", "Data")
node.trigger_action('value_changed', 'Data')
return {'FINISHED'}
####################
# - Node
####################
class ViewerNode(base.MaxwellSimNode):
node_type = ct.NodeType.Viewer
bl_label = "Viewer"
bl_label = 'Viewer'
input_sockets = {
"Data": sockets.AnySocketDef(),
'Data': sockets.AnySocketDef(),
}
####################
# - Properties
####################
auto_plot: bpy.props.BoolProperty(
name="Auto-Plot",
description="Whether to auto-plot anything plugged into the viewer node",
name='Auto-Plot',
description='Whether to auto-plot anything plugged into the viewer node',
default=False,
update=lambda self, context: self.sync_prop("auto_plot", context),
update=lambda self, context: self.sync_prop('auto_plot', context),
)
auto_3d_preview: bpy.props.BoolProperty(
name="Auto 3D Preview",
name='Auto 3D Preview',
description="Whether to auto-preview anything 3D, that's plugged into the viewer node",
default=False,
update=lambda self, context: self.sync_prop("auto_3d_preview", context),
update=lambda self, context: self.sync_prop(
'auto_3d_preview', context
),
)
####################
@ -76,34 +80,34 @@ class ViewerNode(base.MaxwellSimNode):
# Split LHS
col = split.column(align=False)
col.label(text="Console")
col.label(text="Plot")
col.label(text="3D")
col.label(text='Console')
col.label(text='Plot')
col.label(text='3D')
# Split RHS
col = split.column(align=False)
## Console Options
col.operator(ConsoleViewOperator.bl_idname, text="Print")
col.operator(ConsoleViewOperator.bl_idname, text='Print')
## Plot Options
row = col.row(align=True)
row.prop(self, "auto_plot", text="Plot", toggle=True)
row.prop(self, 'auto_plot', text='Plot', toggle=True)
row.operator(
RefreshPlotViewOperator.bl_idname,
text="",
icon="FILE_REFRESH",
text='',
icon='FILE_REFRESH',
)
## 3D Preview Options
row = col.row(align=True)
row.prop(self, "auto_3d_preview", text="3D Preview", toggle=True)
row.prop(self, 'auto_3d_preview', text='3D Preview', toggle=True)
####################
# - Methods
####################
def print_data_to_console(self):
if not (data := self._compute_input("Data")):
if not (data := self._compute_input('Data')):
return
if isinstance(data, sp.Basic):
@ -115,14 +119,14 @@ class ViewerNode(base.MaxwellSimNode):
# - Updates
####################
@base.on_value_changed(
socket_name="Data",
props={"auto_3d_preview"},
socket_name='Data',
props={'auto_3d_preview'},
)
def on_value_changed__data(self, props):
# Show Plot
## Don't have to un-show other plots.
if self.auto_plot:
self.trigger_action("show_plot")
self.trigger_action('show_plot')
# Remove Anything Previewed
preview_collection = managed_bl_object.bl_collection(
@ -133,12 +137,12 @@ class ViewerNode(base.MaxwellSimNode):
preview_collection.objects.unlink(bl_object)
# Preview Anything that Should be Previewed (maybe)
if props["auto_3d_preview"]:
self.trigger_action("show_preview")
if props['auto_3d_preview']:
self.trigger_action('show_preview')
@base.on_value_changed(
prop_name="auto_3d_preview",
props={"auto_3d_preview"},
prop_name='auto_3d_preview',
props={'auto_3d_preview'},
)
def on_value_changed__auto_3d_preview(self, props):
# Remove Anything Previewed
@ -150,8 +154,8 @@ class ViewerNode(base.MaxwellSimNode):
preview_collection.objects.unlink(bl_object)
# Preview Anything that Should be Previewed (maybe)
if props["auto_3d_preview"]:
self.trigger_action("show_preview")
if props['auto_3d_preview']:
self.trigger_action('show_preview')
####################
@ -162,8 +166,4 @@ BL_REGISTER = [
RefreshPlotViewOperator,
ViewerNode,
]
BL_NODES = {
ct.NodeType.Viewer: (
ct.NodeCategory.MAXWELLSIM_OUTPUTS
)
}
BL_NODES = {ct.NodeType.Viewer: (ct.NodeCategory.MAXWELLSIM_OUTPUTS)}

View File

@ -6,46 +6,45 @@ from ... import contracts as ct
from ... import sockets
from .. import base
class FDTDSimNode(base.MaxwellSimNode):
node_type = ct.NodeType.FDTDSim
bl_label = "FDTD Simulation"
bl_label = 'FDTD Simulation'
####################
# - Sockets
####################
input_sockets = {
"Domain": sockets.MaxwellSimDomainSocketDef(),
"BCs": sockets.MaxwellBoundCondsSocketDef(),
"Sources": sockets.MaxwellSourceSocketDef(
'Domain': sockets.MaxwellSimDomainSocketDef(),
'BCs': sockets.MaxwellBoundCondsSocketDef(),
'Sources': sockets.MaxwellSourceSocketDef(
is_list=True,
),
"Structures": sockets.MaxwellStructureSocketDef(
'Structures': sockets.MaxwellStructureSocketDef(
is_list=True,
),
"Monitors": sockets.MaxwellMonitorSocketDef(
'Monitors': sockets.MaxwellMonitorSocketDef(
is_list=True,
),
}
output_sockets = {
"FDTD Sim": sockets.MaxwellFDTDSimSocketDef(),
'FDTD Sim': sockets.MaxwellFDTDSimSocketDef(),
}
####################
# - Output Socket Computation
####################
@base.computes_output_socket(
"FDTD Sim",
'FDTD Sim',
kind=ct.DataFlowKind.Value,
input_sockets={
"Sources", "Structures", "Domain", "BCs", "Monitors"
},
input_sockets={'Sources', 'Structures', 'Domain', 'BCs', 'Monitors'},
)
def compute_fdtd_sim(self, input_sockets: dict) -> sp.Expr:
sim_domain = input_sockets["Domain"]
sources = input_sockets["Sources"]
structures = input_sockets["Structures"]
bounds = input_sockets["BCs"]
monitors = input_sockets["Monitors"]
sim_domain = input_sockets['Domain']
sources = input_sockets['Sources']
structures = input_sockets['Structures']
bounds = input_sockets['BCs']
monitors = input_sockets['Monitors']
# if not isinstance(sources, list):
# sources = [sources]
@ -62,14 +61,11 @@ class FDTDSimNode(base.MaxwellSimNode):
boundary_spec=bounds,
)
####################
# - Blender Registration
####################
BL_REGISTER = [
FDTDSimNode,
]
BL_NODES = {
ct.NodeType.FDTDSim: (
ct.NodeCategory.MAXWELLSIM_SIMS
)
}
BL_NODES = {ct.NodeType.FDTDSim: (ct.NodeCategory.MAXWELLSIM_SIMS)}

View File

@ -9,30 +9,31 @@ from ... import sockets
from .. import base
from ... import managed_objs
GEONODES_DOMAIN_BOX = "simdomain_box"
GEONODES_DOMAIN_BOX = 'simdomain_box'
class SimDomainNode(base.MaxwellSimNode):
node_type = ct.NodeType.SimDomain
bl_label = "Sim Domain"
bl_label = 'Sim Domain'
input_sockets = {
"Duration": sockets.PhysicalTimeSocketDef(
'Duration': sockets.PhysicalTimeSocketDef(
default_value=5 * spu.ps,
default_unit=spu.ps,
),
"Center": sockets.PhysicalSize3DSocketDef(),
"Size": sockets.PhysicalSize3DSocketDef(),
"Grid": sockets.MaxwellSimGridSocketDef(),
"Ambient Medium": sockets.MaxwellMediumSocketDef(),
'Center': sockets.PhysicalSize3DSocketDef(),
'Size': sockets.PhysicalSize3DSocketDef(),
'Grid': sockets.MaxwellSimGridSocketDef(),
'Ambient Medium': sockets.MaxwellMediumSocketDef(),
}
output_sockets = {
"Domain": sockets.MaxwellSimDomainSocketDef(),
'Domain': sockets.MaxwellSimDomainSocketDef(),
}
managed_obj_defs = {
"domain_box": ct.schemas.ManagedObjDef(
'domain_box': ct.schemas.ManagedObjDef(
mk=lambda name: managed_objs.ManagedBLObject(name),
name_prefix="",
name_prefix='',
)
}
@ -40,17 +41,19 @@ class SimDomainNode(base.MaxwellSimNode):
# - Callbacks
####################
@base.computes_output_socket(
"Domain",
input_sockets={"Duration", "Center", "Size", "Grid", "Ambient Medium"},
'Domain',
input_sockets={'Duration', 'Center', 'Size', 'Grid', 'Ambient Medium'},
)
def compute_sim_domain(self, input_sockets: dict) -> sp.Expr:
if all([
(_duration := input_sockets["Duration"]),
(_center := input_sockets["Center"]),
(_size := input_sockets["Size"]),
(grid := input_sockets["Grid"]),
(medium := input_sockets["Ambient Medium"]),
]):
if all(
[
(_duration := input_sockets['Duration']),
(_center := input_sockets['Center']),
(_size := input_sockets['Size']),
(grid := input_sockets['Grid']),
(medium := input_sockets['Ambient Medium']),
]
):
duration = spu.convert_to(_duration, spu.second) / spu.second
center = tuple(spu.convert_to(_center, spu.um) / spu.um)
size = tuple(spu.convert_to(_size, spu.um) / spu.um)
@ -66,67 +69,62 @@ class SimDomainNode(base.MaxwellSimNode):
# - Preview
####################
@base.on_value_changed(
socket_name={"Center", "Size"},
input_sockets={"Center", "Size"},
managed_objs={"domain_box"},
socket_name={'Center', 'Size'},
input_sockets={'Center', 'Size'},
managed_objs={'domain_box'},
)
def on_value_changed__center_size(
self,
input_sockets: dict,
managed_objs: dict[str, ct.schemas.ManagedObj],
):
_center = input_sockets["Center"]
center = tuple([
float(el)
for el in spu.convert_to(_center, spu.um) / spu.um
])
_center = input_sockets['Center']
center = tuple(
[float(el) for el in spu.convert_to(_center, spu.um) / spu.um]
)
_size = input_sockets["Size"]
size = tuple([
float(el)
for el in spu.convert_to(_size, spu.um) / spu.um
])
_size = input_sockets['Size']
size = tuple(
[float(el) for el in spu.convert_to(_size, spu.um) / spu.um]
)
## TODO: Preview unit system?? Presume um for now
# Retrieve Hard-Coded GeoNodes and Analyze Input
geo_nodes = bpy.data.node_groups[GEONODES_DOMAIN_BOX]
geonodes_interface = analyze_geonodes.interface(
geo_nodes, direc="INPUT"
geo_nodes, direc='INPUT'
)
# Sync Modifier Inputs
managed_objs["domain_box"].sync_geonodes_modifier(
managed_objs['domain_box'].sync_geonodes_modifier(
geonodes_node_group=geo_nodes,
geonodes_identifier_to_value={
geonodes_interface["Size"].identifier: size,
geonodes_interface['Size'].identifier: size,
## TODO: Use 'bl_socket_map.value_to_bl`!
## - This accounts for auto-conversion, unit systems, etc. .
## - We could keep it in the node base class...
## - ...But it needs aligning with Blender, too. Hmm.
}
},
)
# Sync Object Position
managed_objs["domain_box"].bl_object("MESH").location = center
managed_objs['domain_box'].bl_object('MESH').location = center
@base.on_show_preview(
managed_objs={"domain_box"},
managed_objs={'domain_box'},
)
def on_show_preview(
self,
managed_objs: dict[str, ct.schemas.ManagedObj],
):
managed_objs["domain_box"].show_preview("MESH")
managed_objs['domain_box'].show_preview('MESH')
self.on_value_changed__center_size()
####################
# - Blender Registration
####################
BL_REGISTER = [
SimDomainNode,
]
BL_NODES = {
ct.NodeType.SimDomain: (
ct.NodeCategory.MAXWELLSIM_SIMS
)
}
BL_NODES = {ct.NodeType.SimDomain: (ct.NodeCategory.MAXWELLSIM_SIMS)}

View File

@ -3,4 +3,3 @@
####################
BL_REGISTER = []
BL_NODES = {}

View File

@ -3,4 +3,3 @@
####################
BL_REGISTER = []
BL_NODES = {}

View File

@ -3,4 +3,3 @@
####################
BL_REGISTER = []
BL_NODES = {}

View File

@ -3,4 +3,3 @@
####################
BL_REGISTER = []
BL_NODES = {}

View File

@ -1,6 +1,7 @@
from . import temporal_shapes
from . import point_dipole_source
# from . import uniform_current_source
from . import plane_wave_source
# from . import gaussian_beam_source

View File

@ -3,4 +3,3 @@
####################
BL_REGISTER = []
BL_NODES = {}

View File

@ -3,4 +3,3 @@
####################
BL_REGISTER = []
BL_NODES = {}

View File

@ -3,4 +3,3 @@
####################
BL_REGISTER = []
BL_NODES = {}

View File

@ -3,4 +3,3 @@
####################
BL_REGISTER = []
BL_NODES = {}

View File

@ -13,7 +13,8 @@ from ... import contracts as ct
from ... import sockets
from .. import base
GEONODES_PLANE_WAVE = "source_plane_wave"
GEONODES_PLANE_WAVE = 'source_plane_wave'
def convert_vector_to_spherical(
v: sp.MatrixBase,
@ -25,51 +26,49 @@ def convert_vector_to_spherical(
x, y, z = v
injection_axis = max(
('x', abs(x)),
('y', abs(y)),
('z', abs(z)),
key=lambda item: item[1]
('x', abs(x)), ('y', abs(y)), ('z', abs(z)), key=lambda item: item[1]
)[0]
## Select injection axis that minimizes 'theta'
if injection_axis == "x":
direction = "+" if x >= 0 else "-"
if injection_axis == 'x':
direction = '+' if x >= 0 else '-'
theta = sp.acos(x / sp.sqrt(x**2 + y**2 + z**2))
phi = sp.atan2(z, y)
elif injection_axis == "y":
direction = "+" if y >= 0 else "-"
elif injection_axis == 'y':
direction = '+' if y >= 0 else '-'
theta = sp.acos(y / sp.sqrt(x**2 + y**2 + z**2))
phi = sp.atan2(x, z)
else:
direction = "+" if z >= 0 else "-"
direction = '+' if z >= 0 else '-'
theta = sp.acos(z / sp.sqrt(x**2 + y**2 + z**2))
phi = sp.atan2(y, x)
return injection_axis, direction, theta, phi
class PlaneWaveSourceNode(base.MaxwellSimNode):
node_type = ct.NodeType.PlaneWaveSource
bl_label = "Plane Wave Source"
bl_label = 'Plane Wave Source'
####################
# - Sockets
####################
input_sockets = {
"Temporal Shape": sockets.MaxwellTemporalShapeSocketDef(),
"Center": sockets.PhysicalPoint3DSocketDef(),
"Direction": sockets.Real3DVectorSocketDef(
'Temporal Shape': sockets.MaxwellTemporalShapeSocketDef(),
'Center': sockets.PhysicalPoint3DSocketDef(),
'Direction': sockets.Real3DVectorSocketDef(
default_value=sp.Matrix([0, 0, -1])
),
"Pol Angle": sockets.PhysicalAngleSocketDef(),
'Pol Angle': sockets.PhysicalAngleSocketDef(),
}
output_sockets = {
"Source": sockets.MaxwellSourceSocketDef(),
'Source': sockets.MaxwellSourceSocketDef(),
}
managed_obj_defs = {
"plane_wave_source": ct.schemas.ManagedObjDef(
'plane_wave_source': ct.schemas.ManagedObjDef(
mk=lambda name: managed_objs.ManagedBLObject(name),
name_prefix="",
name_prefix='',
)
}
@ -77,21 +76,23 @@ class PlaneWaveSourceNode(base.MaxwellSimNode):
# - Output Socket Computation
####################
@base.computes_output_socket(
"Source",
input_sockets={"Temporal Shape", "Center", "Direction", "Pol Angle"},
'Source',
input_sockets={'Temporal Shape', 'Center', 'Direction', 'Pol Angle'},
)
def compute_source(self, input_sockets: dict):
temporal_shape = input_sockets["Temporal Shape"]
_center = input_sockets["Center"]
direction = input_sockets["Direction"]
pol_angle = input_sockets["Pol Angle"]
temporal_shape = input_sockets['Temporal Shape']
_center = input_sockets['Center']
direction = input_sockets['Direction']
pol_angle = input_sockets['Pol Angle']
injection_axis, dir_sgn, theta, phi = convert_vector_to_spherical(direction)
injection_axis, dir_sgn, theta, phi = convert_vector_to_spherical(
direction
)
size = {
"x": (0, math.inf, math.inf),
"y": (math.inf, 0, math.inf),
"z": (math.inf, math.inf, 0),
'x': (0, math.inf, math.inf),
'y': (math.inf, 0, math.inf),
'z': (math.inf, math.inf, 0),
}[injection_axis]
center = tuple(spu.convert_to(_center, spu.um) / spu.um)
@ -110,69 +111,60 @@ class PlaneWaveSourceNode(base.MaxwellSimNode):
# - Preview
####################
@base.on_value_changed(
socket_name={"Center", "Direction"},
input_sockets={"Center", "Direction"},
managed_objs={"plane_wave_source"},
socket_name={'Center', 'Direction'},
input_sockets={'Center', 'Direction'},
managed_objs={'plane_wave_source'},
)
def on_value_changed__center_direction(
self,
input_sockets: dict,
managed_objs: dict[str, ct.schemas.ManagedObj],
):
_center = input_sockets["Center"]
center = tuple([
float(el)
for el in spu.convert_to(_center, spu.um) / spu.um
])
_center = input_sockets['Center']
center = tuple(
[float(el) for el in spu.convert_to(_center, spu.um) / spu.um]
)
_direction = input_sockets["Direction"]
direction = tuple([
float(el)
for el in _direction
])
_direction = input_sockets['Direction']
direction = tuple([float(el) for el in _direction])
## TODO: Preview unit system?? Presume um for now
# Retrieve Hard-Coded GeoNodes and Analyze Input
geo_nodes = bpy.data.node_groups[GEONODES_PLANE_WAVE]
geonodes_interface = analyze_geonodes.interface(
geo_nodes, direc="INPUT"
geo_nodes, direc='INPUT'
)
# Sync Modifier Inputs
managed_objs["plane_wave_source"].sync_geonodes_modifier(
managed_objs['plane_wave_source'].sync_geonodes_modifier(
geonodes_node_group=geo_nodes,
geonodes_identifier_to_value={
geonodes_interface["Direction"].identifier: direction,
geonodes_interface['Direction'].identifier: direction,
## TODO: Use 'bl_socket_map.value_to_bl`!
## - This accounts for auto-conversion, unit systems, etc. .
## - We could keep it in the node base class...
## - ...But it needs aligning with Blender, too. Hmm.
}
},
)
# Sync Object Position
managed_objs["plane_wave_source"].bl_object("MESH").location = center
managed_objs['plane_wave_source'].bl_object('MESH').location = center
@base.on_show_preview(
managed_objs={"plane_wave_source"},
managed_objs={'plane_wave_source'},
)
def on_show_preview(
self,
managed_objs: dict[str, ct.schemas.ManagedObj],
):
managed_objs["plane_wave_source"].show_preview("MESH")
managed_objs['plane_wave_source'].show_preview('MESH')
self.on_value_changed__center_direction()
####################
# - Blender Registration
####################
BL_REGISTER = [
PlaneWaveSourceNode,
]
BL_NODES = {
ct.NodeType.PlaneWaveSource: (
ct.NodeCategory.MAXWELLSIM_SOURCES
)
}
BL_NODES = {ct.NodeType.PlaneWaveSource: (ct.NodeCategory.MAXWELLSIM_SOURCES)}

View File

@ -10,28 +10,29 @@ from ... import sockets
from .. import base
from ... import managed_objs
class PointDipoleSourceNode(base.MaxwellSimNode):
node_type = ct.NodeType.PointDipoleSource
bl_label = "Point Dipole Source"
bl_label = 'Point Dipole Source'
####################
# - Sockets
####################
input_sockets = {
"Temporal Shape": sockets.MaxwellTemporalShapeSocketDef(),
"Center": sockets.PhysicalPoint3DSocketDef(),
"Interpolate": sockets.BoolSocketDef(
'Temporal Shape': sockets.MaxwellTemporalShapeSocketDef(),
'Center': sockets.PhysicalPoint3DSocketDef(),
'Interpolate': sockets.BoolSocketDef(
default_value=True,
),
}
output_sockets = {
"Source": sockets.MaxwellSourceSocketDef(),
'Source': sockets.MaxwellSourceSocketDef(),
}
managed_obj_defs = {
"sphere_empty": ct.schemas.ManagedObjDef(
'sphere_empty': ct.schemas.ManagedObjDef(
mk=lambda name: managed_objs.ManagedBLObject(name),
name_prefix="",
name_prefix='',
)
}
@ -39,15 +40,15 @@ class PointDipoleSourceNode(base.MaxwellSimNode):
# - Properties
####################
pol_axis: bpy.props.EnumProperty(
name="Polarization Axis",
description="Polarization Axis",
name='Polarization Axis',
description='Polarization Axis',
items=[
("EX", "Ex", "Electric field in x-dir"),
("EY", "Ey", "Electric field in y-dir"),
("EZ", "Ez", "Electric field in z-dir"),
('EX', 'Ex', 'Electric field in x-dir'),
('EY', 'Ey', 'Electric field in y-dir'),
('EZ', 'Ez', 'Electric field in z-dir'),
],
default="EX",
update=(lambda self, context: self.sync_prop("pol_axis", context)),
default='EX',
update=(lambda self, context: self.sync_prop('pol_axis', context)),
)
####################
@ -57,29 +58,31 @@ class PointDipoleSourceNode(base.MaxwellSimNode):
split = layout.split(factor=0.6)
col = split.column()
col.label(text="Pol Axis")
col.label(text='Pol Axis')
col = split.column()
col.prop(self, "pol_axis", text="")
col.prop(self, 'pol_axis', text='')
####################
# - Output Socket Computation
####################
@base.computes_output_socket(
"Source",
input_sockets={"Temporal Shape", "Center", "Interpolate"},
props={"pol_axis"},
'Source',
input_sockets={'Temporal Shape', 'Center', 'Interpolate'},
props={'pol_axis'},
)
def compute_source(self, input_sockets: dict[str, typ.Any], props: dict[str, typ.Any]) -> td.PointDipole:
def compute_source(
self, input_sockets: dict[str, typ.Any], props: dict[str, typ.Any]
) -> td.PointDipole:
pol_axis = {
"EX": "Ex",
"EY": "Ey",
"EZ": "Ez",
}[props["pol_axis"]]
'EX': 'Ex',
'EY': 'Ey',
'EZ': 'Ez',
}[props['pol_axis']]
temporal_shape = input_sockets["Temporal Shape"]
_center = input_sockets["Center"]
interpolate = input_sockets["Interpolate"]
temporal_shape = input_sockets['Temporal Shape']
_center = input_sockets['Center']
interpolate = input_sockets['Interpolate']
center = tuple(spu.convert_to(_center, spu.um) / spu.um)
@ -95,36 +98,37 @@ class PointDipoleSourceNode(base.MaxwellSimNode):
# - Preview
####################
@base.on_value_changed(
socket_name="Center",
input_sockets={"Center"},
managed_objs={"sphere_empty"},
socket_name='Center',
input_sockets={'Center'},
managed_objs={'sphere_empty'},
)
def on_value_changed__center(
self,
input_sockets: dict,
managed_objs: dict[str, ct.schemas.ManagedObj],
):
_center = input_sockets["Center"]
_center = input_sockets['Center']
center = tuple(spu.convert_to(_center, spu.um) / spu.um)
## TODO: Preview unit system?? Presume um for now
mobj = managed_objs["sphere_empty"]
bl_object = mobj.bl_object("EMPTY")
mobj = managed_objs['sphere_empty']
bl_object = mobj.bl_object('EMPTY')
bl_object.location = center # tuple([float(el) for el in center])
@base.on_show_preview(
managed_objs={"sphere_empty"},
managed_objs={'sphere_empty'},
)
def on_show_preview(
self,
managed_objs: dict[str, ct.schemas.ManagedObj],
):
managed_objs["sphere_empty"].show_preview(
"EMPTY",
empty_display_type="SPHERE",
managed_objs['sphere_empty'].show_preview(
'EMPTY',
empty_display_type='SPHERE',
)
managed_objs["sphere_empty"].bl_object("EMPTY").empty_display_size = 0.2
managed_objs['sphere_empty'].bl_object(
'EMPTY'
).empty_display_size = 0.2
####################
@ -134,7 +138,5 @@ BL_REGISTER = [
PointDipoleSourceNode,
]
BL_NODES = {
ct.NodeType.PointDipoleSource: (
ct.NodeCategory.MAXWELLSIM_SOURCES
)
ct.NodeType.PointDipoleSource: (ct.NodeCategory.MAXWELLSIM_SOURCES)
}

View File

@ -3,4 +3,3 @@
####################
BL_REGISTER = []
BL_NODES = {}

View File

@ -6,10 +6,11 @@ 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_label = 'Continuous Wave Temporal Shape'
# bl_icon = ...
####################
@ -19,35 +20,35 @@ class ContinuousWaveTemporalShapeNode(base.MaxwellSimTreeNode):
# "amplitude": sockets.RealNumberSocketDef(
# label="Temporal Shape",
# ), ## Should have a unit of some kind...
"phase": sockets.PhysicalAngleSocketDef(
label="Phase",
'phase': sockets.PhysicalAngleSocketDef(
label='Phase',
),
"freq_center": sockets.PhysicalFreqSocketDef(
label="Freq Center",
'freq_center': sockets.PhysicalFreqSocketDef(
label='Freq Center',
),
"freq_std": sockets.PhysicalFreqSocketDef(
label="Freq STD",
'freq_std': sockets.PhysicalFreqSocketDef(
label='Freq STD',
),
"time_delay_rel_ang_freq": sockets.RealNumberSocketDef(
label="Time Delay rel. Ang. Freq",
'time_delay_rel_ang_freq': sockets.RealNumberSocketDef(
label='Time Delay rel. Ang. Freq',
default_value=5.0,
),
}
output_sockets = {
"temporal_shape": sockets.MaxwellTemporalShapeSocketDef(
label="Temporal Shape",
'temporal_shape': sockets.MaxwellTemporalShapeSocketDef(
label='Temporal Shape',
),
}
####################
# - Output Socket Computation
####################
@base.computes_output_socket("temporal_shape")
@base.computes_output_socket('temporal_shape')
def compute_source(self: contracts.NodeTypeProtocol) -> td.PointDipole:
_phase = self.compute_input("phase")
_freq_center = self.compute_input("freq_center")
_freq_std = self.compute_input("freq_std")
time_delay_rel_ang_freq = self.compute_input("time_delay_rel_ang_freq")
_phase = self.compute_input('phase')
_freq_center = self.compute_input('freq_center')
_freq_std = self.compute_input('freq_std')
time_delay_rel_ang_freq = self.compute_input('time_delay_rel_ang_freq')
cheating_amplitude = 1.0
phase = spu.convert_to(_phase, spu.radian) / spu.radian
@ -63,7 +64,6 @@ class ContinuousWaveTemporalShapeNode(base.MaxwellSimTreeNode):
)
####################
# - Blender Registration
####################

View File

@ -13,9 +13,10 @@ from .... import sockets
from .... import managed_objs
from ... import base
class GaussianPulseTemporalShapeNode(base.MaxwellSimNode):
node_type = ct.NodeType.GaussianPulseTemporalShape
bl_label = "Gaussian Pulse Temporal Shape"
bl_label = 'Gaussian Pulse Temporal Shape'
# bl_icon = ...
####################
@ -25,28 +26,28 @@ class GaussianPulseTemporalShapeNode(base.MaxwellSimNode):
# "amplitude": sockets.RealNumberSocketDef(
# label="Temporal Shape",
# ), ## Should have a unit of some kind...
"Freq Center": sockets.PhysicalFreqSocketDef(
'Freq Center': sockets.PhysicalFreqSocketDef(
default_value=500 * spuex.terahertz,
),
"Freq Std.": sockets.PhysicalFreqSocketDef(
'Freq Std.': sockets.PhysicalFreqSocketDef(
default_value=200 * spuex.terahertz,
),
"Phase": sockets.PhysicalAngleSocketDef(),
"Delay rel. AngFreq": sockets.RealNumberSocketDef(
'Phase': sockets.PhysicalAngleSocketDef(),
'Delay rel. AngFreq': sockets.RealNumberSocketDef(
default_value=5.0,
),
"Remove DC": sockets.BoolSocketDef(
'Remove DC': sockets.BoolSocketDef(
default_value=True,
),
}
output_sockets = {
"Temporal Shape": sockets.MaxwellTemporalShapeSocketDef(),
'Temporal Shape': sockets.MaxwellTemporalShapeSocketDef(),
}
managed_obj_defs = {
"amp_time": ct.schemas.ManagedObjDef(
'amp_time': ct.schemas.ManagedObjDef(
mk=lambda name: managed_objs.ManagedBLImage(name),
name_prefix="amp_time_",
name_prefix='amp_time_',
)
}
@ -54,51 +55,59 @@ class GaussianPulseTemporalShapeNode(base.MaxwellSimNode):
# - Properties
####################
plot_time_start: bpy.props.FloatProperty(
name="Plot Time Start (ps)",
description="The instance ID of a particular MaxwellSimNode instance, used to index caches",
name='Plot Time Start (ps)',
description='The instance ID of a particular MaxwellSimNode instance, used to index caches',
default=0.0,
update=(lambda self, context: self.sync_prop("plot_time_start", context)),
update=(
lambda self, context: self.sync_prop('plot_time_start', context)
),
)
plot_time_end: bpy.props.FloatProperty(
name="Plot Time End (ps)",
description="The instance ID of a particular MaxwellSimNode instance, used to index caches",
name='Plot Time End (ps)',
description='The instance ID of a particular MaxwellSimNode instance, used to index caches',
default=5,
update=(lambda self, context: self.sync_prop("plot_time_start", context)),
update=(
lambda self, context: self.sync_prop('plot_time_start', context)
),
)
####################
# - UI
####################
def draw_props(self, context, layout):
layout.label(text="Plot Settings")
layout.label(text='Plot Settings')
split = layout.split(factor=0.6)
col = split.column()
col.label(text="t-Range (ps)")
col.label(text='t-Range (ps)')
col = split.column()
col.prop(self, "plot_time_start", text="")
col.prop(self, "plot_time_end", text="")
col.prop(self, 'plot_time_start', text='')
col.prop(self, 'plot_time_end', text='')
####################
# - Output Socket Computation
####################
@base.computes_output_socket(
"Temporal Shape",
'Temporal Shape',
input_sockets={
"Freq Center", "Freq Std.", "Phase", "Delay rel. AngFreq",
"Remove DC",
}
'Freq Center',
'Freq Std.',
'Phase',
'Delay rel. AngFreq',
'Remove DC',
},
)
def compute_source(self, input_sockets: dict) -> td.GaussianPulse:
if (
(_freq_center := input_sockets["Freq Center"]) is None
or (_freq_std := input_sockets["Freq Std."]) is None
or (_phase := input_sockets["Phase"]) is None
or (time_delay_rel_ang_freq := input_sockets["Delay rel. AngFreq"]) is None
or (remove_dc_component := input_sockets["Remove DC"]) is None
(_freq_center := input_sockets['Freq Center']) is None
or (_freq_std := input_sockets['Freq Std.']) is None
or (_phase := input_sockets['Phase']) is None
or (time_delay_rel_ang_freq := input_sockets['Delay rel. AngFreq'])
is None
or (remove_dc_component := input_sockets['Remove DC']) is None
):
raise ValueError("Inputs not defined")
raise ValueError('Inputs not defined')
cheating_amplitude = 1.0
freq_center = spu.convert_to(_freq_center, spu.hertz) / spu.hertz
@ -115,9 +124,9 @@ class GaussianPulseTemporalShapeNode(base.MaxwellSimNode):
)
@base.on_show_plot(
managed_objs={"amp_time"},
props={"plot_time_start", "plot_time_end"},
output_sockets={"Temporal Shape"},
managed_objs={'amp_time'},
props={'plot_time_start', 'plot_time_end'},
output_sockets={'Temporal Shape'},
stop_propagation=True,
)
def on_show_plot(
@ -126,19 +135,18 @@ class GaussianPulseTemporalShapeNode(base.MaxwellSimNode):
output_sockets: dict[str, typ.Any],
props: dict[str, typ.Any],
):
temporal_shape = output_sockets["Temporal Shape"]
plot_time_start = props["plot_time_start"] * 1e-15
plot_time_end = props["plot_time_end"] * 1e-15
temporal_shape = output_sockets['Temporal Shape']
plot_time_start = props['plot_time_start'] * 1e-15
plot_time_end = props['plot_time_end'] * 1e-15
times = np.linspace(plot_time_start, plot_time_end)
managed_objs["amp_time"].mpl_plot_to_image(
managed_objs['amp_time'].mpl_plot_to_image(
lambda ax: temporal_shape.plot_spectrum(times, ax=ax),
bl_select=True,
)
####################
# - Blender Registration
####################

View File

@ -3,4 +3,3 @@
####################
BL_REGISTER = []
BL_NODES = {}

View File

@ -6,12 +6,10 @@ from . import primitives
BL_REGISTER = [
# *object_structure.BL_REGISTER,
*geonodes_structure.BL_REGISTER,
*primitives.BL_REGISTER,
]
BL_NODES = {
# **object_structure.BL_NODES,
**geonodes_structure.BL_NODES,
**primitives.BL_NODES,
}

View File

@ -16,26 +16,27 @@ from ... import sockets
from .. import base
from ... import managed_objs
class GeoNodesStructureNode(base.MaxwellSimNode):
node_type = ct.NodeType.GeoNodesStructure
bl_label = "GeoNodes Structure"
bl_label = 'GeoNodes Structure'
####################
# - Sockets
####################
input_sockets = {
"Unit System": sockets.PhysicalUnitSystemSocketDef(),
"Medium": sockets.MaxwellMediumSocketDef(),
"GeoNodes": sockets.BlenderGeoNodesSocketDef(),
'Unit System': sockets.PhysicalUnitSystemSocketDef(),
'Medium': sockets.MaxwellMediumSocketDef(),
'GeoNodes': sockets.BlenderGeoNodesSocketDef(),
}
output_sockets = {
"Structure": sockets.MaxwellStructureSocketDef(),
'Structure': sockets.MaxwellStructureSocketDef(),
}
managed_obj_defs = {
"geometry": ct.schemas.ManagedObjDef(
'geometry': ct.schemas.ManagedObjDef(
mk=lambda name: managed_objs.ManagedBLObject(name),
name_prefix="",
name_prefix='',
)
}
@ -43,9 +44,9 @@ class GeoNodesStructureNode(base.MaxwellSimNode):
# - Output Socket Computation
####################
@base.computes_output_socket(
"Structure",
input_sockets={"Medium"},
managed_objs={"geometry"},
'Structure',
input_sockets={'Medium'},
managed_objs={'geometry'},
)
def compute_structure(
self,
@ -53,7 +54,7 @@ class GeoNodesStructureNode(base.MaxwellSimNode):
managed_objs: dict[str, ct.schemas.ManagedObj],
) -> td.Structure:
# Extract the Managed Blender Object
mobj = managed_objs["geometry"]
mobj = managed_objs['geometry']
# Extract Geometry as Arrays
geometry_as_arrays = mobj.mesh_as_arrays
@ -61,20 +62,19 @@ class GeoNodesStructureNode(base.MaxwellSimNode):
# Return TriMesh Structure
return td.Structure(
geometry=td.TriangleMesh.from_vertices_faces(
geometry_as_arrays["verts"],
geometry_as_arrays["faces"],
geometry_as_arrays['verts'],
geometry_as_arrays['faces'],
),
medium=input_sockets["Medium"],
medium=input_sockets['Medium'],
)
####################
# - Event Methods
####################
@base.on_value_changed(
socket_name="GeoNodes",
managed_objs={"geometry"},
input_sockets={"GeoNodes"},
socket_name='GeoNodes',
managed_objs={'geometry'},
input_sockets={'GeoNodes'},
)
def on_value_changed__geonodes(
self,
@ -85,15 +85,15 @@ class GeoNodesStructureNode(base.MaxwellSimNode):
Refreshes the Loose Input Sockets, which map directly to the GeoNodes tree input sockets.
"""
if not (geo_nodes := input_sockets["GeoNodes"]):
managed_objs["geometry"].free()
if not (geo_nodes := input_sockets['GeoNodes']):
managed_objs['geometry'].free()
self.loose_input_sockets = {}
return
# Analyze GeoNodes
## Extract Valid Inputs (via GeoNodes Tree "Interface")
geonodes_interface = analyze_geonodes.interface(
geo_nodes, direc="INPUT"
geo_nodes, direc='INPUT'
)
# Set Loose Input Sockets
@ -116,9 +116,8 @@ class GeoNodesStructureNode(base.MaxwellSimNode):
@base.on_value_changed(
any_loose_input_socket=True,
managed_objs={"geometry"},
input_sockets={"Unit System", "GeoNodes"},
managed_objs={'geometry'},
input_sockets={'Unit System', 'GeoNodes'},
)
def on_value_changed__loose_inputs(
self,
@ -131,15 +130,16 @@ class GeoNodesStructureNode(base.MaxwellSimNode):
Synchronizes the change to the actual GeoNodes modifier, so that the change is immediately visible.
"""
# Retrieve Data
unit_system = input_sockets["Unit System"]
mobj = managed_objs["geometry"]
unit_system = input_sockets['Unit System']
mobj = managed_objs['geometry']
if not (geo_nodes := input_sockets["GeoNodes"]): return
if not (geo_nodes := input_sockets['GeoNodes']):
return
# Analyze GeoNodes Interface (input direction)
## This retrieves NodeTreeSocketInterface elements
geonodes_interface = analyze_geonodes.interface(
geo_nodes, direc="INPUT"
geo_nodes, direc='INPUT'
)
## TODO: Check that Loose Sockets matches the Interface
@ -159,14 +159,14 @@ class GeoNodesStructureNode(base.MaxwellSimNode):
for socket_name, bl_interface_socket in (
geonodes_interface.items()
)
}
},
)
####################
# - Event Methods
####################
@base.on_show_preview(
managed_objs={"geometry"},
managed_objs={'geometry'},
)
def on_show_preview(
self,
@ -176,7 +176,7 @@ class GeoNodesStructureNode(base.MaxwellSimNode):
Synchronizes the change to the actual GeoNodes modifier, so that the change is immediately visible.
"""
managed_objs["geometry"].show_preview("MESH")
managed_objs['geometry'].show_preview('MESH')
####################
@ -186,7 +186,5 @@ BL_REGISTER = [
GeoNodesStructureNode,
]
BL_NODES = {
ct.NodeType.GeoNodesStructure: (
ct.NodeCategory.MAXWELLSIM_STRUCTURES
)
ct.NodeType.GeoNodesStructure: (ct.NodeCategory.MAXWELLSIM_STRUCTURES)
}

View File

@ -10,35 +10,36 @@ from ... import contracts
from ... import sockets
from .. import base
class ObjectStructureNode(base.MaxwellSimTreeNode):
node_type = contracts.NodeType.ObjectStructure
bl_label = "Object Structure"
bl_label = 'Object Structure'
# bl_icon = ...
####################
# - Sockets
####################
input_sockets = {
"medium": sockets.MaxwellMediumSocketDef(
label="Medium",
'medium': sockets.MaxwellMediumSocketDef(
label='Medium',
),
"object": sockets.BlenderObjectSocketDef(
label="Object",
'object': sockets.BlenderObjectSocketDef(
label='Object',
),
}
output_sockets = {
"structure": sockets.MaxwellStructureSocketDef(
label="Structure",
'structure': sockets.MaxwellStructureSocketDef(
label='Structure',
),
}
####################
# - Output Socket Computation
####################
@base.computes_output_socket("structure")
@base.computes_output_socket('structure')
def compute_structure(self: contracts.NodeTypeProtocol) -> td.Structure:
# Extract the Blender Object
bl_object = self.compute_input("object")
bl_object = self.compute_input('object')
# Ensure Updated Geometry
bpy.context.view_layer.update()
@ -51,27 +52,25 @@ class ObjectStructureNode(base.MaxwellSimTreeNode):
)
bmesh.ops.triangulate(bmesh_mesh, faces=bmesh_mesh.faces)
mesh = bpy.data.meshes.new(name="TriangulatedMesh")
mesh = bpy.data.meshes.new(name='TriangulatedMesh')
bmesh_mesh.to_mesh(mesh)
bmesh_mesh.free()
# Extract Vertices and Faces
vertices = np.array([vert.co for vert in mesh.vertices])
faces = np.array([
[vert for vert in poly.vertices]
for poly in mesh.polygons
])
faces = np.array(
[[vert for vert in poly.vertices] for poly in mesh.polygons]
)
# Remove Temporary Mesh
bpy.data.meshes.remove(mesh)
return td.Structure(
geometry=td.TriangleMesh.from_vertices_faces(vertices, faces),
medium=self.compute_input("medium")
medium=self.compute_input('medium'),
)
####################
# - Blender Registration
####################

View File

@ -1,4 +1,5 @@
from . import box_structure
# from . import cylinder_structure
from . import sphere_structure

View File

@ -10,30 +10,31 @@ from .... import sockets
from .... import managed_objs
from ... import base
GEONODES_STRUCTURE_BOX = "structure_box"
GEONODES_STRUCTURE_BOX = 'structure_box'
class BoxStructureNode(base.MaxwellSimNode):
node_type = ct.NodeType.BoxStructure
bl_label = "Box Structure"
bl_label = 'Box Structure'
####################
# - Sockets
####################
input_sockets = {
"Medium": sockets.MaxwellMediumSocketDef(),
"Center": sockets.PhysicalPoint3DSocketDef(),
"Size": sockets.PhysicalSize3DSocketDef(
'Medium': sockets.MaxwellMediumSocketDef(),
'Center': sockets.PhysicalPoint3DSocketDef(),
'Size': sockets.PhysicalSize3DSocketDef(
default_value=sp.Matrix([500, 500, 500]) * spu.nm
),
}
output_sockets = {
"Structure": sockets.MaxwellStructureSocketDef(),
'Structure': sockets.MaxwellStructureSocketDef(),
}
managed_obj_defs = {
"structure_box": ct.schemas.ManagedObjDef(
'structure_box': ct.schemas.ManagedObjDef(
mk=lambda name: managed_objs.ManagedBLObject(name),
name_prefix="",
name_prefix='',
)
}
@ -41,13 +42,13 @@ class BoxStructureNode(base.MaxwellSimNode):
# - Output Socket Computation
####################
@base.computes_output_socket(
"Structure",
input_sockets={"Medium", "Center", "Size"},
'Structure',
input_sockets={'Medium', 'Center', 'Size'},
)
def compute_simulation(self, input_sockets: dict) -> td.Box:
medium = input_sockets["Medium"]
_center = input_sockets["Center"]
_size = input_sockets["Size"]
medium = input_sockets['Medium']
_center = input_sockets['Center']
_size = input_sockets['Size']
center = tuple(spu.convert_to(_center, spu.um) / spu.um)
size = tuple(spu.convert_to(_size, spu.um) / spu.um)
@ -64,64 +65,61 @@ class BoxStructureNode(base.MaxwellSimNode):
# - Preview - Changes to Input Sockets
####################
@base.on_value_changed(
socket_name={"Center", "Size"},
input_sockets={"Center", "Size"},
managed_objs={"structure_box"},
socket_name={'Center', 'Size'},
input_sockets={'Center', 'Size'},
managed_objs={'structure_box'},
)
def on_value_changed__center_size(
self,
input_sockets: dict,
managed_objs: dict[str, ct.schemas.ManagedObj],
):
_center = input_sockets["Center"]
center = tuple([
float(el)
for el in spu.convert_to(_center, spu.um) / spu.um
])
_center = input_sockets['Center']
center = tuple(
[float(el) for el in spu.convert_to(_center, spu.um) / spu.um]
)
_size = input_sockets["Size"]
size = tuple([
float(el)
for el in spu.convert_to(_size, spu.um) / spu.um
])
_size = input_sockets['Size']
size = tuple(
[float(el) for el in spu.convert_to(_size, spu.um) / spu.um]
)
## TODO: Preview unit system?? Presume um for now
# Retrieve Hard-Coded GeoNodes and Analyze Input
geo_nodes = bpy.data.node_groups[GEONODES_STRUCTURE_BOX]
geonodes_interface = analyze_geonodes.interface(
geo_nodes, direc="INPUT"
geo_nodes, direc='INPUT'
)
# Sync Modifier Inputs
managed_objs["structure_box"].sync_geonodes_modifier(
managed_objs['structure_box'].sync_geonodes_modifier(
geonodes_node_group=geo_nodes,
geonodes_identifier_to_value={
geonodes_interface["Size"].identifier: size,
geonodes_interface['Size'].identifier: size,
## TODO: Use 'bl_socket_map.value_to_bl`!
## - This accounts for auto-conversion, unit systems, etc. .
## - We could keep it in the node base class...
## - ...But it needs aligning with Blender, too. Hmm.
}
},
)
# Sync Object Position
managed_objs["structure_box"].bl_object("MESH").location = center
managed_objs['structure_box'].bl_object('MESH').location = center
####################
# - Preview - Show Preview
####################
@base.on_show_preview(
managed_objs={"structure_box"},
managed_objs={'structure_box'},
)
def on_show_preview(
self,
managed_objs: dict[str, ct.schemas.ManagedObj],
):
managed_objs["structure_box"].show_preview("MESH")
managed_objs['structure_box'].show_preview('MESH')
self.on_value_changed__center_size()
####################
# - Blender Registration
####################

View File

@ -6,43 +6,44 @@ from .... import contracts
from .... import sockets
from ... import base
class CylinderStructureNode(base.MaxwellSimTreeNode):
node_type = contracts.NodeType.CylinderStructure
bl_label = "Cylinder Structure"
bl_label = 'Cylinder Structure'
# bl_icon = ...
####################
# - Sockets
####################
input_sockets = {
"medium": sockets.MaxwellMediumSocketDef(
label="Medium",
'medium': sockets.MaxwellMediumSocketDef(
label='Medium',
),
"center": sockets.PhysicalPoint3DSocketDef(
label="Center",
'center': sockets.PhysicalPoint3DSocketDef(
label='Center',
),
"radius": sockets.PhysicalLengthSocketDef(
label="Radius",
'radius': sockets.PhysicalLengthSocketDef(
label='Radius',
),
"height": sockets.PhysicalLengthSocketDef(
label="Height",
'height': sockets.PhysicalLengthSocketDef(
label='Height',
),
}
output_sockets = {
"structure": sockets.MaxwellStructureSocketDef(
label="Structure",
'structure': sockets.MaxwellStructureSocketDef(
label='Structure',
),
}
####################
# - Output Socket Computation
####################
@base.computes_output_socket("structure")
@base.computes_output_socket('structure')
def compute_simulation(self: contracts.NodeTypeProtocol) -> td.Box:
medium = self.compute_input("medium")
_center = self.compute_input("center")
_radius = self.compute_input("radius")
_height = self.compute_input("height")
medium = self.compute_input('medium')
_center = self.compute_input('center')
_radius = self.compute_input('radius')
_height = self.compute_input('height')
center = tuple(spu.convert_to(_center, spu.um) / spu.um)
radius = spu.convert_to(_radius, spu.um) / spu.um
@ -58,7 +59,6 @@ class CylinderStructureNode(base.MaxwellSimTreeNode):
)
####################
# - Blender Registration
####################

View File

@ -10,30 +10,31 @@ from .... import sockets
from .... import managed_objs
from ... import base
GEONODES_STRUCTURE_SPHERE = "structure_sphere"
GEONODES_STRUCTURE_SPHERE = 'structure_sphere'
class SphereStructureNode(base.MaxwellSimNode):
node_type = ct.NodeType.SphereStructure
bl_label = "Sphere Structure"
bl_label = 'Sphere Structure'
####################
# - Sockets
####################
input_sockets = {
"Center": sockets.PhysicalPoint3DSocketDef(),
"Radius": sockets.PhysicalLengthSocketDef(
'Center': sockets.PhysicalPoint3DSocketDef(),
'Radius': sockets.PhysicalLengthSocketDef(
default_value=150 * spu.nm,
),
"Medium": sockets.MaxwellMediumSocketDef(),
'Medium': sockets.MaxwellMediumSocketDef(),
}
output_sockets = {
"Structure": sockets.MaxwellStructureSocketDef(),
'Structure': sockets.MaxwellStructureSocketDef(),
}
managed_obj_defs = {
"structure_sphere": ct.schemas.ManagedObjDef(
'structure_sphere': ct.schemas.ManagedObjDef(
mk=lambda name: managed_objs.ManagedBLObject(name),
name_prefix="",
name_prefix='',
)
}
@ -41,13 +42,13 @@ class SphereStructureNode(base.MaxwellSimNode):
# - Output Socket Computation
####################
@base.computes_output_socket(
"Structure",
input_sockets={"Center", "Radius", "Medium"},
'Structure',
input_sockets={'Center', 'Radius', 'Medium'},
)
def compute_structure(self, input_sockets: dict) -> td.Box:
medium = input_sockets["Medium"]
_center = input_sockets["Center"]
_radius = input_sockets["Radius"]
medium = input_sockets['Medium']
_center = input_sockets['Center']
_radius = input_sockets['Radius']
center = tuple(spu.convert_to(_center, spu.um) / spu.um)
radius = spu.convert_to(_radius, spu.um) / spu.um
@ -64,61 +65,59 @@ class SphereStructureNode(base.MaxwellSimNode):
# - Preview - Changes to Input Sockets
####################
@base.on_value_changed(
socket_name={"Center", "Radius"},
input_sockets={"Center", "Radius"},
managed_objs={"structure_sphere"},
socket_name={'Center', 'Radius'},
input_sockets={'Center', 'Radius'},
managed_objs={'structure_sphere'},
)
def on_value_changed__center_radius(
self,
input_sockets: dict,
managed_objs: dict[str, ct.schemas.ManagedObj],
):
_center = input_sockets["Center"]
center = tuple([
float(el)
for el in spu.convert_to(_center, spu.um) / spu.um
])
_center = input_sockets['Center']
center = tuple(
[float(el) for el in spu.convert_to(_center, spu.um) / spu.um]
)
_radius = input_sockets["Radius"]
_radius = input_sockets['Radius']
radius = float(spu.convert_to(_radius, spu.um) / spu.um)
## TODO: Preview unit system?? Presume um for now
# Retrieve Hard-Coded GeoNodes and Analyze Input
geo_nodes = bpy.data.node_groups[GEONODES_STRUCTURE_SPHERE]
geonodes_interface = analyze_geonodes.interface(
geo_nodes, direc="INPUT"
geo_nodes, direc='INPUT'
)
# Sync Modifier Inputs
managed_objs["structure_sphere"].sync_geonodes_modifier(
managed_objs['structure_sphere'].sync_geonodes_modifier(
geonodes_node_group=geo_nodes,
geonodes_identifier_to_value={
geonodes_interface["Radius"].identifier: radius,
geonodes_interface['Radius'].identifier: radius,
## TODO: Use 'bl_socket_map.value_to_bl`!
## - This accounts for auto-conversion, unit systems, etc. .
## - We could keep it in the node base class...
## - ...But it needs aligning with Blender, too. Hmm.
}
},
)
# Sync Object Position
managed_objs["structure_sphere"].bl_object("MESH").location = center
managed_objs['structure_sphere'].bl_object('MESH').location = center
####################
# - Preview - Show Preview
####################
@base.on_show_preview(
managed_objs={"structure_sphere"},
managed_objs={'structure_sphere'},
)
def on_show_preview(
self,
managed_objs: dict[str, ct.schemas.ManagedObj],
):
managed_objs["structure_sphere"].show_preview("MESH")
managed_objs['structure_sphere'].show_preview('MESH')
self.on_value_changed__center_radius()
####################
# - Blender Registration
####################

View File

@ -3,4 +3,3 @@
####################
BL_REGISTER = []
BL_NODES = {}

View File

@ -4,13 +4,11 @@ from . import combine
BL_REGISTER = [
# *math.BL_REGISTER,
*combine.BL_REGISTER,
# *separate.BL_REGISTER,
]
BL_NODES = {
# **math.BL_NODES,
**combine.BL_NODES,
# **separate.BL_NODES,
}

View File

@ -10,21 +10,21 @@ from .. import base
MAX_AMOUNT = 20
class CombineNode(base.MaxwellSimNode):
node_type = ct.NodeType.Combine
bl_label = "Combine"
bl_label = 'Combine'
# bl_icon = ...
####################
# - Sockets
####################
input_socket_sets = {
"Maxwell Sources": {},
"Maxwell Structures": {},
"Maxwell Monitors": {},
"Real 3D Vector": {
f"x_{i}": sockets.RealNumberSocketDef()
for i in range(3)
'Maxwell Sources': {},
'Maxwell Structures': {},
'Maxwell Monitors': {},
'Real 3D Vector': {
f'x_{i}': sockets.RealNumberSocketDef() for i in range(3)
},
# "Point 3D": {
# axis: sockets.PhysicalLengthSocketDef()
@ -43,23 +43,23 @@ class CombineNode(base.MaxwellSimNode):
# },
}
output_socket_sets = {
"Maxwell Sources": {
"Sources": sockets.MaxwellSourceSocketDef(
'Maxwell Sources': {
'Sources': sockets.MaxwellSourceSocketDef(
is_list=True,
),
},
"Maxwell Structures": {
"Structures": sockets.MaxwellStructureSocketDef(
'Maxwell Structures': {
'Structures': sockets.MaxwellStructureSocketDef(
is_list=True,
),
},
"Maxwell Monitors": {
"Monitors": sockets.MaxwellMonitorSocketDef(
'Maxwell Monitors': {
'Monitors': sockets.MaxwellMonitorSocketDef(
is_list=True,
),
},
"Real 3D Vector": {
"Real 3D Vector": sockets.Real3DVectorSocketDef(),
'Real 3D Vector': {
'Real 3D Vector': sockets.Real3DVectorSocketDef(),
},
# "Point 3D": {
# "3D Point": sockets.PhysicalPoint3DSocketDef(),
@ -70,92 +70,83 @@ class CombineNode(base.MaxwellSimNode):
}
amount: bpy.props.IntProperty(
name="# Objects to Combine",
description="Amount of Objects to Combine",
name='# Objects to Combine',
description='Amount of Objects to Combine',
default=1,
min=1,
max=MAX_AMOUNT,
update=lambda self, context: self.sync_prop("amount", context)
update=lambda self, context: self.sync_prop('amount', context),
)
####################
# - Draw
####################
def draw_props(self, context, layout):
layout.prop(self, "amount", text="#")
layout.prop(self, 'amount', text='#')
####################
# - Output Socket Computation
####################
@base.computes_output_socket(
"Real 3D Vector",
input_sockets={"x_0", "x_1", "x_2"}
'Real 3D Vector', input_sockets={'x_0', 'x_1', 'x_2'}
)
def compute_real_3d_vector(self, input_sockets) -> sp.Expr:
return sp.Matrix([input_sockets[f"x_{i}"] for i in range(3)])
return sp.Matrix([input_sockets[f'x_{i}'] for i in range(3)])
@base.computes_output_socket(
"Sources",
input_sockets={f"Source #{i}" for i in range(MAX_AMOUNT)},
props={"amount"},
'Sources',
input_sockets={f'Source #{i}' for i in range(MAX_AMOUNT)},
props={'amount'},
)
def compute_sources(self, input_sockets, props) -> sp.Expr:
return [
input_sockets[f"Source #{i}"]
for i in range(props["amount"])
]
return [input_sockets[f'Source #{i}'] for i in range(props['amount'])]
@base.computes_output_socket(
"Structures",
input_sockets={f"Structure #{i}" for i in range(MAX_AMOUNT)},
props={"amount"},
'Structures',
input_sockets={f'Structure #{i}' for i in range(MAX_AMOUNT)},
props={'amount'},
)
def compute_structures(self, input_sockets, props) -> sp.Expr:
return [
input_sockets[f"Structure #{i}"]
for i in range(props["amount"])
input_sockets[f'Structure #{i}'] for i in range(props['amount'])
]
@base.computes_output_socket(
"Monitors",
input_sockets={f"Monitor #{i}" for i in range(MAX_AMOUNT)},
props={"amount"},
'Monitors',
input_sockets={f'Monitor #{i}' for i in range(MAX_AMOUNT)},
props={'amount'},
)
def compute_monitors(self, input_sockets, props) -> sp.Expr:
return [
input_sockets[f"Monitor #{i}"]
for i in range(props["amount"])
]
return [input_sockets[f'Monitor #{i}'] for i in range(props['amount'])]
####################
# - Input Socket Compilation
####################
@base.on_value_changed(
prop_name="active_socket_set",
props={"active_socket_set", "amount"},
prop_name='active_socket_set',
props={'active_socket_set', 'amount'},
)
def on_value_changed__active_socket_set(self, props):
if props["active_socket_set"] == "Maxwell Sources":
if props['active_socket_set'] == 'Maxwell Sources':
self.loose_input_sockets = {
f"Source #{i}": sockets.MaxwellSourceSocketDef()
for i in range(props["amount"])
f'Source #{i}': sockets.MaxwellSourceSocketDef()
for i in range(props['amount'])
}
elif props["active_socket_set"] == "Maxwell Structures":
elif props['active_socket_set'] == 'Maxwell Structures':
self.loose_input_sockets = {
f"Structure #{i}": sockets.MaxwellStructureSocketDef()
for i in range(props["amount"])
f'Structure #{i}': sockets.MaxwellStructureSocketDef()
for i in range(props['amount'])
}
elif props["active_socket_set"] == "Maxwell Monitors":
elif props['active_socket_set'] == 'Maxwell Monitors':
self.loose_input_sockets = {
f"Monitor #{i}": sockets.MaxwellMonitorSocketDef()
for i in range(props["amount"])
f'Monitor #{i}': sockets.MaxwellMonitorSocketDef()
for i in range(props['amount'])
}
else:
self.loose_input_sockets = {}
@base.on_value_changed(
prop_name="amount",
prop_name='amount',
)
def on_value_changed__amount(self):
self.on_value_changed__active_socket_set()
@ -171,8 +162,4 @@ class CombineNode(base.MaxwellSimNode):
BL_REGISTER = [
CombineNode,
]
BL_NODES = {
ct.NodeType.Combine: (
ct.NodeCategory.MAXWELLSIM_UTILITIES
)
}
BL_NODES = {ct.NodeType.Combine: (ct.NodeCategory.MAXWELLSIM_UTILITIES)}

View File

@ -7,9 +7,10 @@ from .... import contracts
from .... import sockets
from ... import base
class WaveConverterNode(base.MaxwellSimTreeNode):
node_type = contracts.NodeType.WaveConverter
bl_label = "Wave Converter"
bl_label = 'Wave Converter'
# bl_icon = ...
####################
@ -17,27 +18,27 @@ class WaveConverterNode(base.MaxwellSimTreeNode):
####################
input_sockets = {}
input_socket_sets = {
"freq_to_vacwl": {
"freq": sockets.PhysicalFreqSocketDef(
label="Freq",
'freq_to_vacwl': {
'freq': sockets.PhysicalFreqSocketDef(
label='Freq',
),
},
"vacwl_to_freq": {
"vacwl": sockets.PhysicalVacWLSocketDef(
label="Vac WL",
'vacwl_to_freq': {
'vacwl': sockets.PhysicalVacWLSocketDef(
label='Vac WL',
),
},
}
output_sockets = {}
output_socket_sets = {
"freq_to_vacwl": {
"vacwl": sockets.PhysicalVacWLSocketDef(
label="Vac WL",
'freq_to_vacwl': {
'vacwl': sockets.PhysicalVacWLSocketDef(
label='Vac WL',
),
},
"vacwl_to_freq": {
"freq": sockets.PhysicalFreqSocketDef(
label="Freq",
'vacwl_to_freq': {
'freq': sockets.PhysicalFreqSocketDef(
label='Freq',
),
},
}
@ -45,22 +46,26 @@ class WaveConverterNode(base.MaxwellSimTreeNode):
####################
# - Output Socket Computation
####################
@base.computes_output_socket("freq")
@base.computes_output_socket('freq')
def compute_freq(self: contracts.NodeTypeProtocol) -> sp.Expr:
vac_speed_of_light = sc.constants.speed_of_light * spu.meter/spu.second
vac_speed_of_light = (
sc.constants.speed_of_light * spu.meter / spu.second
)
vacwl = self.compute_input("vacwl")
vacwl = self.compute_input('vacwl')
return spu.convert_to(
vac_speed_of_light / vacwl,
spu.hertz,
)
@base.computes_output_socket("vacwl")
@base.computes_output_socket('vacwl')
def compute_vacwl(self: contracts.NodeTypeProtocol) -> sp.Expr:
vac_speed_of_light = sc.constants.speed_of_light * spu.meter/spu.second
vac_speed_of_light = (
sc.constants.speed_of_light * spu.meter / spu.second
)
freq = self.compute_input("freq")
freq = self.compute_input('freq')
return spu.convert_to(
vac_speed_of_light / freq,
@ -68,7 +73,6 @@ class WaveConverterNode(base.MaxwellSimTreeNode):
)
####################
# - Blender Registration
####################

View File

@ -16,28 +16,27 @@ from ... import managed_objs
CACHE = {}
class FDTDSimDataVizNode(base.MaxwellSimNode):
node_type = ct.NodeType.FDTDSimDataViz
bl_label = "FDTD Sim Data Viz"
bl_label = 'FDTD Sim Data Viz'
####################
# - Sockets
####################
input_sockets = {
"FDTD Sim Data": sockets.MaxwellFDTDSimDataSocketDef(),
}
output_sockets= {
"Preview": sockets.AnySocketDef()
'FDTD Sim Data': sockets.MaxwellFDTDSimDataSocketDef(),
}
output_sockets = {'Preview': sockets.AnySocketDef()}
managed_obj_defs = {
"viz_plot": ct.schemas.ManagedObjDef(
'viz_plot': ct.schemas.ManagedObjDef(
mk=lambda name: managed_objs.ManagedBLImage(name),
name_prefix="",
name_prefix='',
),
"viz_object": ct.schemas.ManagedObjDef(
'viz_object': ct.schemas.ManagedObjDef(
mk=lambda name: managed_objs.ManagedBLObject(name),
name_prefix="",
name_prefix='',
),
}
@ -45,147 +44,168 @@ class FDTDSimDataVizNode(base.MaxwellSimNode):
# - Properties
####################
viz_monitor_name: bpy.props.EnumProperty(
name="Viz Monitor Name",
description="Monitor to visualize within the attached SimData",
name='Viz Monitor Name',
description='Monitor to visualize within the attached SimData',
items=lambda self, context: self.retrieve_monitors(context),
update=(lambda self, context: self.sync_viz_monitor_name(context)),
)
cache_viz_monitor_type: bpy.props.StringProperty(
name="Viz Monitor Type",
description="Type of the viz monitor",
default=""
name='Viz Monitor Type',
description='Type of the viz monitor',
default='',
)
# Field Monitor Type
field_viz_component: bpy.props.EnumProperty(
name="Field Component",
description="Field component to visualize",
name='Field Component',
description='Field component to visualize',
items=[
("E", "E", "Electric"),
('E', 'E', 'Electric'),
# ("H", "H", "Magnetic"),
# ("S", "S", "Poynting"),
("Ex", "Ex", "Ex"),
("Ey", "Ey", "Ey"),
("Ez", "Ez", "Ez"),
('Ex', 'Ex', 'Ex'),
('Ey', 'Ey', 'Ey'),
('Ez', 'Ez', 'Ez'),
# ("Hx", "Hx", "Hx"),
# ("Hy", "Hy", "Hy"),
# ("Hz", "Hz", "Hz"),
],
default="E",
update=lambda self, context: self.sync_prop("field_viz_component", context),
default='E',
update=lambda self, context: self.sync_prop(
'field_viz_component', context
),
)
field_viz_part: bpy.props.EnumProperty(
name="Field Part",
description="Field part to visualize",
name='Field Part',
description='Field part to visualize',
items=[
("real", "Real", "Electric"),
("imag", "Imaginary", "Imaginary"),
("abs", "Abs", "Abs"),
("abs^2", "Squared Abs", "Square Abs"),
("phase", "Phase", "Phase"),
('real', 'Real', 'Electric'),
('imag', 'Imaginary', 'Imaginary'),
('abs', 'Abs', 'Abs'),
('abs^2', 'Squared Abs', 'Square Abs'),
('phase', 'Phase', 'Phase'),
],
default="real",
update=lambda self, context: self.sync_prop("field_viz_part", context),
default='real',
update=lambda self, context: self.sync_prop('field_viz_part', context),
)
field_viz_scale: bpy.props.EnumProperty(
name="Field Scale",
description="Field scale to visualize in, Linear or Log",
name='Field Scale',
description='Field scale to visualize in, Linear or Log',
items=[
("lin", "Linear", "Linear Scale"),
("dB", "Log (dB)", "Logarithmic (dB) Scale"),
('lin', 'Linear', 'Linear Scale'),
('dB', 'Log (dB)', 'Logarithmic (dB) Scale'),
],
default="lin",
update=lambda self, context: self.sync_prop("field_viz_scale", context),
default='lin',
update=lambda self, context: self.sync_prop(
'field_viz_scale', context
),
)
field_viz_structure_visibility: bpy.props.FloatProperty(
name="Field Viz Plot: Structure Visibility",
description="Visibility of structes",
name='Field Viz Plot: Structure Visibility',
description='Visibility of structes',
default=0.2,
min=0.0,
max=1.0,
update=lambda self, context: self.sync_prop("field_viz_plot_fixed_f", context),
update=lambda self, context: self.sync_prop(
'field_viz_plot_fixed_f', context
),
)
field_viz_plot_fix_x: bpy.props.BoolProperty(
name="Field Viz Plot: Fix X",
description="Fix the x-coordinate on the plot",
name='Field Viz Plot: Fix X',
description='Fix the x-coordinate on the plot',
default=False,
update=lambda self, context: self.sync_prop("field_viz_plot_fix_x", context),
update=lambda self, context: self.sync_prop(
'field_viz_plot_fix_x', context
),
)
field_viz_plot_fix_y: bpy.props.BoolProperty(
name="Field Viz Plot: Fix Y",
description="Fix the y coordinate on the plot",
name='Field Viz Plot: Fix Y',
description='Fix the y coordinate on the plot',
default=False,
update=lambda self, context: self.sync_prop("field_viz_plot_fix_y", context),
update=lambda self, context: self.sync_prop(
'field_viz_plot_fix_y', context
),
)
field_viz_plot_fix_z: bpy.props.BoolProperty(
name="Field Viz Plot: Fix Z",
description="Fix the z coordinate on the plot",
name='Field Viz Plot: Fix Z',
description='Fix the z coordinate on the plot',
default=False,
update=lambda self, context: self.sync_prop("field_viz_plot_fix_z", context),
update=lambda self, context: self.sync_prop(
'field_viz_plot_fix_z', context
),
)
field_viz_plot_fix_f: bpy.props.BoolProperty(
name="Field Viz Plot: Fix Freq",
description="Fix the frequency coordinate on the plot",
name='Field Viz Plot: Fix Freq',
description='Fix the frequency coordinate on the plot',
default=False,
update=lambda self, context: self.sync_prop("field_viz_plot_fix_f", context),
update=lambda self, context: self.sync_prop(
'field_viz_plot_fix_f', context
),
)
field_viz_plot_fixed_x: bpy.props.FloatProperty(
name="Field Viz Plot: Fix X",
description="Fix the x-coordinate on the plot",
name='Field Viz Plot: Fix X',
description='Fix the x-coordinate on the plot',
default=0.0,
update=lambda self, context: self.sync_prop("field_viz_plot_fixed_x", context),
update=lambda self, context: self.sync_prop(
'field_viz_plot_fixed_x', context
),
)
field_viz_plot_fixed_y: bpy.props.FloatProperty(
name="Field Viz Plot: Fixed Y",
description="Fix the y coordinate on the plot",
name='Field Viz Plot: Fixed Y',
description='Fix the y coordinate on the plot',
default=0.0,
update=lambda self, context: self.sync_prop("field_viz_plot_fixed_y", context),
update=lambda self, context: self.sync_prop(
'field_viz_plot_fixed_y', context
),
)
field_viz_plot_fixed_z: bpy.props.FloatProperty(
name="Field Viz Plot: Fixed Z",
description="Fix the z coordinate on the plot",
name='Field Viz Plot: Fixed Z',
description='Fix the z coordinate on the plot',
default=0.0,
update=lambda self, context: self.sync_prop("field_viz_plot_fixed_z", context),
update=lambda self, context: self.sync_prop(
'field_viz_plot_fixed_z', context
),
)
field_viz_plot_fixed_f: bpy.props.FloatProperty(
name="Field Viz Plot: Fixed Freq (Thz)",
description="Fix the frequency coordinate on the plot",
name='Field Viz Plot: Fixed Freq (Thz)',
description='Fix the frequency coordinate on the plot',
default=0.0,
update=lambda self, context: self.sync_prop("field_viz_plot_fixed_f", context),
update=lambda self, context: self.sync_prop(
'field_viz_plot_fixed_f', context
),
)
####################
# - Derived Properties
####################
def sync_viz_monitor_name(self, context):
if (sim_data := self._compute_input("FDTD Sim Data")) is None:
if (sim_data := self._compute_input('FDTD Sim Data')) is None:
return
self.cache_viz_monitor_type = sim_data.monitor_data[
self.viz_monitor_name
].type
self.sync_prop("viz_monitor_name", context)
self.sync_prop('viz_monitor_name', context)
def retrieve_monitors(self, context) -> list[tuple]:
global CACHE
if not CACHE.get(self.instance_id):
sim_data = self._compute_input("FDTD Sim Data")
sim_data = self._compute_input('FDTD Sim Data')
if sim_data is not None:
CACHE[self.instance_id] = {"monitors": list(
sim_data.monitor_data.keys()
)}
CACHE[self.instance_id] = {
'monitors': list(sim_data.monitor_data.keys())
}
else:
return [("NONE", "None", "No monitors")]
return [('NONE', 'None', 'No monitors')]
monitor_names = CACHE[self.instance_id]["monitors"]
monitor_names = CACHE[self.instance_id]['monitors']
# Check for No Monitors
if not monitor_names:
return [("NONE", "None", "No monitors")]
return [('NONE', 'None', 'No monitors')]
return [
(
@ -201,42 +221,41 @@ class FDTDSimDataVizNode(base.MaxwellSimNode):
####################
def draw_props(self, context, layout):
row = layout.row()
row.prop(self, "viz_monitor_name", text="")
if self.cache_viz_monitor_type == "FieldData":
row.prop(self, 'viz_monitor_name', text='')
if self.cache_viz_monitor_type == 'FieldData':
# Array Selection
split = layout.split(factor=0.45)
col = split.column(align=False)
col.label(text="Component")
col.label(text="Part")
col.label(text="Scale")
col.label(text='Component')
col.label(text='Part')
col.label(text='Scale')
col = split.column(align=False)
col.prop(self, "field_viz_component", text="")
col.prop(self, "field_viz_part", text="")
col.prop(self, "field_viz_scale", text="")
col.prop(self, 'field_viz_component', text='')
col.prop(self, 'field_viz_part', text='')
col.prop(self, 'field_viz_scale', text='')
# Coordinate Fixing
split = layout.split(factor=0.45)
col = split.column(align=False)
col.prop(self, "field_viz_plot_fix_x", text="Fix x (um)")
col.prop(self, "field_viz_plot_fix_y", text="Fix y (um)")
col.prop(self, "field_viz_plot_fix_z", text="Fix z (um)")
col.prop(self, "field_viz_plot_fix_f", text="Fix f (THz)")
col.prop(self, 'field_viz_plot_fix_x', text='Fix x (um)')
col.prop(self, 'field_viz_plot_fix_y', text='Fix y (um)')
col.prop(self, 'field_viz_plot_fix_z', text='Fix z (um)')
col.prop(self, 'field_viz_plot_fix_f', text='Fix f (THz)')
col = split.column(align=False)
col.prop(self, "field_viz_plot_fixed_x", text="")
col.prop(self, "field_viz_plot_fixed_y", text="")
col.prop(self, "field_viz_plot_fixed_z", text="")
col.prop(self, "field_viz_plot_fixed_f", text="")
col.prop(self, 'field_viz_plot_fixed_x', text='')
col.prop(self, 'field_viz_plot_fixed_y', text='')
col.prop(self, 'field_viz_plot_fixed_z', text='')
col.prop(self, 'field_viz_plot_fixed_f', text='')
####################
# - On Value Changed Methods
####################
@base.on_value_changed(
socket_name="FDTD Sim Data",
managed_objs={"viz_object"},
input_sockets={"FDTD Sim Data"},
socket_name='FDTD Sim Data',
managed_objs={'viz_object'},
input_sockets={'FDTD Sim Data'},
)
def on_value_changed__fdtd_sim_data(
self,
@ -245,29 +264,35 @@ class FDTDSimDataVizNode(base.MaxwellSimNode):
) -> None:
global CACHE
if (sim_data := input_sockets["FDTD Sim Data"]) is None:
if (sim_data := input_sockets['FDTD Sim Data']) is None:
CACHE.pop(self.instance_id, None)
return
CACHE[self.instance_id] = {"monitors": list(
sim_data.monitor_data.keys()
)}
CACHE[self.instance_id] = {
'monitors': list(sim_data.monitor_data.keys())
}
####################
# - Plotting
####################
@base.on_show_plot(
managed_objs={"viz_plot"},
managed_objs={'viz_plot'},
props={
"viz_monitor_name", "field_viz_component",
"field_viz_part", "field_viz_scale",
"field_viz_structure_visibility",
"field_viz_plot_fix_x", "field_viz_plot_fix_y",
"field_viz_plot_fix_z", "field_viz_plot_fix_f",
"field_viz_plot_fixed_x", "field_viz_plot_fixed_y",
"field_viz_plot_fixed_z", "field_viz_plot_fixed_f",
'viz_monitor_name',
'field_viz_component',
'field_viz_part',
'field_viz_scale',
'field_viz_structure_visibility',
'field_viz_plot_fix_x',
'field_viz_plot_fix_y',
'field_viz_plot_fix_z',
'field_viz_plot_fix_f',
'field_viz_plot_fixed_x',
'field_viz_plot_fixed_y',
'field_viz_plot_fixed_z',
'field_viz_plot_fixed_f',
},
input_sockets={"FDTD Sim Data"},
input_sockets={'FDTD Sim Data'},
stop_propagation=True,
)
def on_show_plot(
@ -276,35 +301,35 @@ class FDTDSimDataVizNode(base.MaxwellSimNode):
input_sockets: dict[str, typ.Any],
props: dict[str, typ.Any],
):
if (
(sim_data := input_sockets["FDTD Sim Data"]) is None
or (monitor_name := props["viz_monitor_name"]) == "NONE"
):
if (sim_data := input_sockets['FDTD Sim Data']) is None or (
monitor_name := props['viz_monitor_name']
) == 'NONE':
return
coord_fix = {}
for coord in ["x", "y", "z", "f"]:
if props[f"field_viz_plot_fix_{coord}"]:
for coord in ['x', 'y', 'z', 'f']:
if props[f'field_viz_plot_fix_{coord}']:
coord_fix |= {
coord: props[f"field_viz_plot_fixed_{coord}"],
coord: props[f'field_viz_plot_fixed_{coord}'],
}
if "f" in coord_fix:
coord_fix["f"] *= 1e12
if 'f' in coord_fix:
coord_fix['f'] *= 1e12
managed_objs["viz_plot"].mpl_plot_to_image(
managed_objs['viz_plot'].mpl_plot_to_image(
lambda ax: sim_data.plot_field(
monitor_name,
props["field_viz_component"],
val=props["field_viz_part"],
scale=props["field_viz_scale"],
eps_alpha=props["field_viz_structure_visibility"],
props['field_viz_component'],
val=props['field_viz_part'],
scale=props['field_viz_scale'],
eps_alpha=props['field_viz_structure_visibility'],
phase=0,
**coord_fix,
ax=ax,
),
bl_select=True,
)
# @base.on_show_preview(
# managed_objs={"viz_object"},
# )
@ -325,8 +350,4 @@ class FDTDSimDataVizNode(base.MaxwellSimNode):
BL_REGISTER = [
FDTDSimDataVizNode,
]
BL_NODES = {
ct.NodeType.FDTDSimDataViz: (
ct.NodeCategory.MAXWELLSIM_VIZ
)
}
BL_NODES = {ct.NodeType.FDTDSimDataViz: (ct.NodeCategory.MAXWELLSIM_VIZ)}

View File

@ -1,18 +1,21 @@
from . import base
from . import basic
AnySocketDef = basic.AnySocketDef
BoolSocketDef = basic.BoolSocketDef
StringSocketDef = basic.StringSocketDef
FilePathSocketDef = basic.FilePathSocketDef
from . import number
IntegerNumberSocketDef = number.IntegerNumberSocketDef
RationalNumberSocketDef = number.RationalNumberSocketDef
RealNumberSocketDef = number.RealNumberSocketDef
ComplexNumberSocketDef = number.ComplexNumberSocketDef
from . import vector
Real2DVectorSocketDef = vector.Real2DVectorSocketDef
Complex2DVectorSocketDef = vector.Complex2DVectorSocketDef
Integer3DVectorSocketDef = vector.Integer3DVectorSocketDef
@ -20,6 +23,7 @@ Real3DVectorSocketDef = vector.Real3DVectorSocketDef
Complex3DVectorSocketDef = vector.Complex3DVectorSocketDef
from . import physical
PhysicalUnitSystemSocketDef = physical.PhysicalUnitSystemSocketDef
PhysicalTimeSocketDef = physical.PhysicalTimeSocketDef
PhysicalAngleSocketDef = physical.PhysicalAngleSocketDef
@ -36,6 +40,7 @@ PhysicalPolSocketDef = physical.PhysicalPolSocketDef
PhysicalFreqSocketDef = physical.PhysicalFreqSocketDef
from . import blender
BlenderObjectSocketDef = blender.BlenderObjectSocketDef
BlenderCollectionSocketDef = blender.BlenderCollectionSocketDef
BlenderImageSocketDef = blender.BlenderImageSocketDef
@ -43,6 +48,7 @@ BlenderGeoNodesSocketDef = blender.BlenderGeoNodesSocketDef
BlenderTextSocketDef = blender.BlenderTextSocketDef
from . import maxwell
MaxwellBoundCondSocketDef = maxwell.MaxwellBoundCondSocketDef
MaxwellBoundCondsSocketDef = maxwell.MaxwellBoundCondsSocketDef
MaxwellMediumSocketDef = maxwell.MaxwellMediumSocketDef
@ -58,6 +64,7 @@ MaxwellSimGridAxisSocketDef = maxwell.MaxwellSimGridAxisSocketDef
MaxwellSimDomainSocketDef = maxwell.MaxwellSimDomainSocketDef
from . import tidy3d
Tidy3DCloudTaskSocketDef = tidy3d.Tidy3DCloudTaskSocketDef
BL_REGISTER = [

View File

@ -9,6 +9,7 @@ import sympy as sp
import sympy.physics.units as spu
from .. import contracts as ct
class MaxwellSimSocket(bpy.types.NodeSocket):
# Fundamentals
socket_type: ct.SocketType
@ -16,8 +17,12 @@ class MaxwellSimSocket(bpy.types.NodeSocket):
# Style
display_shape: typx.Literal[
"CIRCLE", "SQUARE", "DIAMOND", "CIRCLE_DOT", "SQUARE_DOT",
"DIAMOND_DOT",
'CIRCLE',
'SQUARE',
'DIAMOND',
'CIRCLE_DOT',
'SQUARE_DOT',
'DIAMOND_DOT',
]
## We use the following conventions for shapes:
## - CIRCLE: Single Value.
@ -41,14 +46,14 @@ class MaxwellSimSocket(bpy.types.NodeSocket):
super().__init_subclass__(**kwargs) ## Yucky superclass setup.
# Setup Blender ID for Node
if not hasattr(cls, "socket_type"):
if not hasattr(cls, 'socket_type'):
msg = f"Socket class {cls} does not define 'socket_type'"
raise ValueError(msg)
cls.bl_idname = str(cls.socket_type.value)
# Setup Locked Property for Node
cls.__annotations__["locked"] = bpy.props.BoolProperty(
name="Locked State",
cls.__annotations__['locked'] = bpy.props.BoolProperty(
name='Locked State',
description="The lock-state of a particular socket, which determines the socket's user editability",
default=False,
)
@ -58,37 +63,37 @@ class MaxwellSimSocket(bpy.types.NodeSocket):
cls.socket_shape = ct.SOCKET_SHAPES[cls.socket_type]
# Setup List
cls.__annotations__["is_list"] = bpy.props.BoolProperty(
name="Is List",
description="Whether or not a particular socket is a list type socket",
cls.__annotations__['is_list'] = bpy.props.BoolProperty(
name='Is List',
description='Whether or not a particular socket is a list type socket',
default=False,
update=lambda self, context: self.sync_is_list(context)
update=lambda self, context: self.sync_is_list(context),
)
# Configure Use of Units
if cls.use_units:
# Set Shape :)
cls.socket_shape += "_DOT"
cls.socket_shape += '_DOT'
if not (socket_units := ct.SOCKET_UNITS.get(cls.socket_type)):
msg = "Tried to `use_units` on {cls.bl_idname} socket, but `SocketType` has no units defined in `contracts.SOCKET_UNITS`"
msg = 'Tried to `use_units` on {cls.bl_idname} socket, but `SocketType` has no units defined in `contracts.SOCKET_UNITS`'
raise RuntimeError(msg)
# Current Unit
cls.__annotations__["active_unit"] = bpy.props.EnumProperty(
name="Unit",
description="Choose a unit",
cls.__annotations__['active_unit'] = bpy.props.EnumProperty(
name='Unit',
description='Choose a unit',
items=[
(unit_name, str(unit_value), str(unit_value))
for unit_name, unit_value in socket_units["values"].items()
for unit_name, unit_value in socket_units['values'].items()
],
default=socket_units["default"],
default=socket_units['default'],
update=lambda self, context: self.sync_unit_change(),
)
# Previous Unit (for conversion)
cls.__annotations__["prev_active_unit"] = bpy.props.StringProperty(
default=socket_units["default"],
cls.__annotations__['prev_active_unit'] = bpy.props.StringProperty(
default=socket_units['default'],
)
####################
@ -96,7 +101,13 @@ class MaxwellSimSocket(bpy.types.NodeSocket):
####################
def trigger_action(
self,
action: typx.Literal["enable_lock", "disable_lock", "value_changed", "show_preview", "show_plot"],
action: typx.Literal[
'enable_lock',
'disable_lock',
'value_changed',
'show_preview',
'show_plot',
],
) -> None:
"""Called whenever the socket's output value has changed.
@ -109,7 +120,7 @@ class MaxwellSimSocket(bpy.types.NodeSocket):
`trigger_action` method will be called.
"""
# Forwards Chains
if action in {"value_changed"}:
if action in {'value_changed'}:
## Input Socket
if not self.is_output:
self.node.trigger_action(action, socket_name=self.name)
@ -120,11 +131,16 @@ class MaxwellSimSocket(bpy.types.NodeSocket):
link.to_socket.trigger_action(action)
# Backwards Chains
elif action in {"enable_lock", "disable_lock", "show_preview", "show_plot"}:
if action == "enable_lock":
elif action in {
'enable_lock',
'disable_lock',
'show_preview',
'show_plot',
}:
if action == 'enable_lock':
self.locked = True
if action == "disable_lock":
if action == 'disable_lock':
self.locked = False
## Output Socket
@ -140,36 +156,35 @@ class MaxwellSimSocket(bpy.types.NodeSocket):
# - Action Chain: Event Handlers
####################
def sync_is_list(self, context: bpy.types.Context):
"""Called when the "is_list_ property has been updated.
"""
"""Called when the "is_list_ property has been updated."""
if self.is_list:
if self.use_units:
self.display_shape = "SQUARE_DOT"
self.display_shape = 'SQUARE_DOT'
else:
self.display_shape = "SQUARE"
self.display_shape = 'SQUARE'
self.trigger_action("value_changed")
self.trigger_action('value_changed')
def sync_prop(self, prop_name: str, context: bpy.types.Context):
"""Called when a property has been updated.
"""
"""Called when a property has been updated."""
if not hasattr(self, prop_name):
msg = f"Property {prop_name} not defined on socket {self}"
msg = f'Property {prop_name} not defined on socket {self}'
raise RuntimeError(msg)
self.trigger_action("value_changed")
self.trigger_action('value_changed')
def sync_link_added(self, link) -> bool:
"""Called when a link has been added to this (input) socket.
Returns a bool, whether or not the socket consents to the link change.
"""
if self.locked: return False
if self.locked:
return False
if self.is_output:
msg = f"Tried to sync 'link add' on output socket"
raise RuntimeError(msg)
self.trigger_action("value_changed")
self.trigger_action('value_changed')
return True
@ -178,12 +193,13 @@ class MaxwellSimSocket(bpy.types.NodeSocket):
Returns a bool, whether or not the socket consents to the link change.
"""
if self.locked: return False
if self.locked:
return False
if self.is_output:
msg = f"Tried to sync 'link add' on output socket"
raise RuntimeError(msg)
self.trigger_action("value_changed")
self.trigger_action('value_changed')
return True
@ -193,6 +209,7 @@ class MaxwellSimSocket(bpy.types.NodeSocket):
@property
def value(self) -> typ.Any:
raise NotImplementedError
@value.setter
def value(self, value: typ.Any) -> None:
raise NotImplementedError
@ -200,26 +217,29 @@ class MaxwellSimSocket(bpy.types.NodeSocket):
@property
def value_list(self) -> typ.Any:
return [self.value]
@value_list.setter
def value_list(self, value: typ.Any) -> None:
raise NotImplementedError
def value_as_unit_system(
self,
unit_system: dict,
dimensionless: bool = True
self, unit_system: dict, dimensionless: bool = True
) -> typ.Any:
## TODO: Caching could speed this boi up quite a bit
unit_system_unit = unit_system[self.socket_type]
return spu.convert_to(
return (
spu.convert_to(
self.value,
unit_system_unit,
) / unit_system_unit
)
/ unit_system_unit
)
@property
def lazy_value(self) -> None:
raise NotImplementedError
@lazy_value.setter
def lazy_value(self, lazy_value: typ.Any) -> None:
raise NotImplementedError
@ -227,6 +247,7 @@ class MaxwellSimSocket(bpy.types.NodeSocket):
@property
def lazy_value_list(self) -> typ.Any:
return [self.lazy_value]
@lazy_value_list.setter
def lazy_value_list(self, value: typ.Any) -> None:
raise NotImplementedError
@ -244,11 +265,15 @@ class MaxwellSimSocket(bpy.types.NodeSocket):
**NOTE**: Low-level method. Use `compute_data` instead.
"""
if kind == ct.DataFlowKind.Value:
if self.is_list: return self.value_list
else: return self.value
if self.is_list:
return self.value_list
else:
return self.value
elif kind == ct.DataFlowKind.LazyValue:
if self.is_list: return self.lazy_value_list
else: return self.lazy_value
if self.is_list:
return self.lazy_value_list
else:
return self.lazy_value
return self.lazy_value
elif kind == ct.DataFlowKind.Capabilities:
return self.capabilities
@ -268,21 +293,23 @@ class MaxwellSimSocket(bpy.types.NodeSocket):
## List-like sockets guarantee that a list of a thing is passed.
if self.is_output:
res = self.node.compute_output(self.name, kind=kind)
if self.is_list and not isinstance(res, list): return [res]
if self.is_list and not isinstance(res, list):
return [res]
return res
# Compute Input Socket
## Unlinked: Retrieve Socket Value
if not self.is_linked: return self._compute_data(kind)
if not self.is_linked:
return self._compute_data(kind)
## Linked: Compute Output of Linked Sockets
linked_values = [
link.from_socket.compute_data(kind)
for link in self.links
link.from_socket.compute_data(kind) for link in self.links
]
## Return Single Value / List of Values
if len(linked_values) == 1: return linked_values[0]
if len(linked_values) == 1:
return linked_values[0]
return linked_values
####################
@ -294,9 +321,7 @@ class MaxwellSimSocket(bpy.types.NodeSocket):
msg = "Tried to get possible units for socket {self}, but socket doesn't `use_units`"
raise ValueError(msg)
return ct.SOCKET_UNITS[
self.socket_type
]["values"]
return ct.SOCKET_UNITS[self.socket_type]['values']
@property
def unit(self) -> sp.Expr:
@ -344,8 +369,7 @@ class MaxwellSimSocket(bpy.types.NodeSocket):
## - Using only self.value avoids implementation-specific details.
self.value = spu.convert_to(
prev_value,
self.unit
prev_value, self.unit
) ## Now, the unit conversion can be done correctly.
self.prev_active_unit = self.active_unit
@ -358,14 +382,12 @@ class MaxwellSimSocket(bpy.types.NodeSocket):
context: bpy.types.Context,
node: bpy.types.Node,
) -> ct.BLColorRGBA:
"""Color of the socket icon, when embedded in a node.
"""
"""Color of the socket icon, when embedded in a node."""
return self.socket_color
@classmethod
def draw_color_simple(cls) -> ct.BLColorRGBA:
"""Fallback color of the socket icon (ex.when not embedded in a node).
"""
"""Fallback color of the socket icon (ex.when not embedded in a node)."""
return cls.socket_color
####################
@ -378,8 +400,7 @@ class MaxwellSimSocket(bpy.types.NodeSocket):
node: bpy.types.Node,
text: str,
) -> None:
"""Called by Blender to draw the socket UI.
"""
"""Called by Blender to draw the socket UI."""
if self.is_output:
self.draw_output(context, layout, node, text)
@ -402,13 +423,13 @@ class MaxwellSimSocket(bpy.types.NodeSocket):
node: bpy.types.Node,
text: str,
) -> None:
"""Draws the socket UI, when the socket is an input socket.
"""
"""Draws the socket UI, when the socket is an input socket."""
col = layout.column(align=False)
# Label Row
row = col.row(align=False)
if self.locked: row.enabled = False
if self.locked:
row.enabled = False
## Linked Label
if self.is_linked:
@ -423,7 +444,7 @@ class MaxwellSimSocket(bpy.types.NodeSocket):
self.draw_label_row(_row, text)
_col = split.column(align=True)
_col.prop(self, "active_unit", text="")
_col.prop(self, 'active_unit', text='')
else:
self.draw_label_row(row, text)
@ -438,7 +459,8 @@ class MaxwellSimSocket(bpy.types.NodeSocket):
row = col.row(align=False)
row.enabled = False
else:
if self.locked: row.enabled = False
if self.locked:
row.enabled = False
# Value Column(s)
col = row.column(align=True)
@ -454,8 +476,7 @@ class MaxwellSimSocket(bpy.types.NodeSocket):
node: bpy.types.Node,
text: str,
) -> None:
"""Draws the socket UI, when the socket is an output socket.
"""
"""Draws the socket UI, when the socket is an output socket."""
layout.label(text=text)
####################
@ -485,4 +506,3 @@ class MaxwellSimSocket(bpy.types.NodeSocket):
Can be overridden.
"""
pass

View File

@ -1,13 +1,17 @@
from . import any as any_socket
AnySocketDef = any_socket.AnySocketDef
from . import bool as bool_socket
BoolSocketDef = bool_socket.BoolSocketDef
from . import string
StringSocketDef = string.StringSocketDef
from . import file_path
FilePathSocketDef = file_path.FilePathSocketDef

View File

@ -7,12 +7,14 @@ import pydantic as pyd
from .. import base
from ... import contracts as ct
####################
# - Blender Socket
####################
class AnyBLSocket(base.MaxwellSimSocket):
socket_type = ct.SocketType.Any
bl_label = "Any"
bl_label = 'Any'
####################
# - Socket Configuration
@ -23,6 +25,7 @@ class AnySocketDef(pyd.BaseModel):
def init(self, bl_socket: AnyBLSocket) -> None:
pass
####################
# - Blender Registration
####################

View File

@ -7,29 +7,32 @@ import pydantic as pyd
from .. import base
from ... import contracts as ct
####################
# - Blender Socket
####################
class BoolBLSocket(base.MaxwellSimSocket):
socket_type = ct.SocketType.Bool
bl_label = "Bool"
bl_label = 'Bool'
####################
# - Properties
####################
raw_value: bpy.props.BoolProperty(
name="Boolean",
description="Represents a boolean value",
name='Boolean',
description='Represents a boolean value',
default=False,
update=(lambda self, context: self.sync_prop("raw_value", context)),
update=(lambda self, context: self.sync_prop('raw_value', context)),
)
####################
# - Socket UI
####################
def draw_label_row(self, label_col_row: bpy.types.UILayout, text: str) -> None:
def draw_label_row(
self, label_col_row: bpy.types.UILayout, text: str
) -> None:
label_col_row.label(text=text)
label_col_row.prop(self, "raw_value", text="")
label_col_row.prop(self, 'raw_value', text='')
####################
# - Computation of Default Value
@ -42,6 +45,7 @@ class BoolBLSocket(base.MaxwellSimSocket):
def value(self, value: bool) -> None:
self.raw_value = value
####################
# - Socket Configuration
####################
@ -53,6 +57,7 @@ class BoolSocketDef(pyd.BaseModel):
def init(self, bl_socket: BoolBLSocket) -> None:
bl_socket.value = self.default_value
####################
# - Blender Registration
####################

View File

@ -8,21 +8,22 @@ import pydantic as pyd
from .. import base
from ... import contracts as ct
####################
# - Blender Socket
####################
class FilePathBLSocket(base.MaxwellSimSocket):
socket_type = ct.SocketType.FilePath
bl_label = "File Path"
bl_label = 'File Path'
####################
# - Properties
####################
raw_value: bpy.props.StringProperty(
name="File Path",
description="Represents the path to a file",
subtype="FILE_PATH",
update=(lambda self, context: self.sync_prop("raw_value", context)),
name='File Path',
description='Represents the path to a file',
subtype='FILE_PATH',
update=(lambda self, context: self.sync_prop('raw_value', context)),
)
####################
@ -30,7 +31,7 @@ class FilePathBLSocket(base.MaxwellSimSocket):
####################
def draw_value(self, col: bpy.types.UILayout) -> None:
col_row = col.row(align=True)
col_row.prop(self, "raw_value", text="")
col_row.prop(self, 'raw_value', text='')
####################
# - Computation of Default Value
@ -43,17 +44,19 @@ class FilePathBLSocket(base.MaxwellSimSocket):
def value(self, value: Path) -> None:
self.raw_value = bpy.path.relpath(str(value))
####################
# - Socket Configuration
####################
class FilePathSocketDef(pyd.BaseModel):
socket_type: ct.SocketType = ct.SocketType.FilePath
default_path: Path = Path("")
default_path: Path = Path('')
def init(self, bl_socket: FilePathBLSocket) -> None:
bl_socket.value = self.default_path
####################
# - Blender Registration
####################

View File

@ -7,28 +7,31 @@ import pydantic as pyd
from .. import base
from ... import contracts as ct
####################
# - Blender Socket
####################
class StringBLSocket(base.MaxwellSimSocket):
socket_type = ct.SocketType.String
bl_label = "String"
bl_label = 'String'
####################
# - Properties
####################
raw_value: bpy.props.StringProperty(
name="String",
description="Represents a string",
default="",
update=(lambda self, context: self.sync_prop("raw_value", context)),
name='String',
description='Represents a string',
default='',
update=(lambda self, context: self.sync_prop('raw_value', context)),
)
####################
# - Socket UI
####################
def draw_label_row(self, label_col_row: bpy.types.UILayout, text: str) -> None:
label_col_row.prop(self, "raw_value", text=text)
def draw_label_row(
self, label_col_row: bpy.types.UILayout, text: str
) -> None:
label_col_row.prop(self, 'raw_value', text=text)
####################
# - Computation of Default Value
@ -41,17 +44,19 @@ class StringBLSocket(base.MaxwellSimSocket):
def value(self, value: str) -> None:
self.raw_value = value
####################
# - Socket Configuration
####################
class StringSocketDef(pyd.BaseModel):
socket_type: ct.SocketType = ct.SocketType.String
default_text: str = ""
default_text: str = ''
def init(self, bl_socket: StringBLSocket) -> None:
bl_socket.value = self.default_text
####################
# - Blender Registration
####################

View File

@ -1,20 +1,22 @@
from . import object as object_socket
from . import collection
BlenderObjectSocketDef = object_socket.BlenderObjectSocketDef
BlenderCollectionSocketDef = collection.BlenderCollectionSocketDef
from . import image
BlenderImageSocketDef = image.BlenderImageSocketDef
from . import geonodes
from . import text
BlenderGeoNodesSocketDef = geonodes.BlenderGeoNodesSocketDef
BlenderTextSocketDef = text.BlenderTextSocketDef
BL_REGISTER = [
*object_socket.BL_REGISTER,
*collection.BL_REGISTER,
*text.BL_REGISTER,
*image.BL_REGISTER,
*geonodes.BL_REGISTER,

View File

@ -6,28 +6,29 @@ import pydantic as pyd
from .. import base
from ... import contracts as ct
####################
# - Blender Socket
####################
class BlenderCollectionBLSocket(base.MaxwellSimSocket):
socket_type = ct.SocketType.BlenderCollection
bl_label = "Blender Collection"
bl_label = 'Blender Collection'
####################
# - Properties
####################
raw_value: bpy.props.PointerProperty(
name="Blender Collection",
description="A Blender collection",
name='Blender Collection',
description='A Blender collection',
type=bpy.types.Collection,
update=(lambda self, context: self.sync_prop("raw_value", context)),
update=(lambda self, context: self.sync_prop('raw_value', context)),
)
####################
# - UI
####################
def draw_value(self, col: bpy.types.UILayout) -> None:
col.prop(self, "raw_value", text="")
col.prop(self, 'raw_value', text='')
####################
# - Default Value
@ -40,6 +41,7 @@ class BlenderCollectionBLSocket(base.MaxwellSimSocket):
def value(self, value: bpy.types.Collection) -> None:
self.raw_value = value
####################
# - Socket Configuration
####################
@ -49,9 +51,8 @@ class BlenderCollectionSocketDef(pyd.BaseModel):
def init(self, bl_socket: BlenderCollectionBLSocket) -> None:
pass
####################
# - Blender Registration
####################
BL_REGISTER = [
BlenderCollectionBLSocket
]
BL_REGISTER = [BlenderCollectionBLSocket]

View File

@ -6,16 +6,17 @@ import pydantic as pyd
from .. import base
from ... import contracts as ct
####################
# - Operators
####################
class BlenderMaxwellResetGeoNodesSocket(bpy.types.Operator):
bl_idname = "blender_maxwell.reset_geo_nodes_socket"
bl_label = "Reset GeoNodes Socket"
bl_idname = 'blender_maxwell.reset_geo_nodes_socket'
bl_label = 'Reset GeoNodes Socket'
node_tree_name: bpy.props.StringProperty(name="Node Tree Name")
node_name: bpy.props.StringProperty(name="Node Name")
socket_name: bpy.props.StringProperty(name="Socket Name")
node_tree_name: bpy.props.StringProperty(name='Node Tree Name')
node_name: bpy.props.StringProperty(name='Node Name')
socket_name: bpy.props.StringProperty(name='Socket Name')
def execute(self, context):
node_tree = bpy.data.node_groups[self.node_tree_name]
@ -23,7 +24,7 @@ class BlenderMaxwellResetGeoNodesSocket(bpy.types.Operator):
socket = node.inputs[self.socket_name]
# Report as though the GeoNodes Tree Changed
socket.sync_prop("raw_value", context)
socket.sync_prop('raw_value', context)
return {'FINISHED'}
@ -33,17 +34,17 @@ class BlenderMaxwellResetGeoNodesSocket(bpy.types.Operator):
####################
class BlenderGeoNodesBLSocket(base.MaxwellSimSocket):
socket_type = ct.SocketType.BlenderGeoNodes
bl_label = "Geometry Node Tree"
bl_label = 'Geometry Node Tree'
####################
# - Properties
####################
raw_value: bpy.props.PointerProperty(
name="Blender GeoNodes Tree",
description="Represents a Blender GeoNodes Tree",
name='Blender GeoNodes Tree',
description='Represents a Blender GeoNodes Tree',
type=bpy.types.NodeTree,
poll=(lambda self, obj: obj.bl_idname == "GeometryNodeTree"),
update=(lambda self, context: self.sync_prop("raw_value", context)),
poll=(lambda self, obj: obj.bl_idname == 'GeometryNodeTree'),
update=(lambda self, context: self.sync_prop('raw_value', context)),
)
####################
@ -66,7 +67,7 @@ class BlenderGeoNodesBLSocket(base.MaxwellSimSocket):
# - UI
####################
def draw_value(self, col: bpy.types.UILayout) -> None:
col.prop(self, "raw_value", text="")
col.prop(self, 'raw_value', text='')
####################
# - Default Value
@ -79,6 +80,7 @@ class BlenderGeoNodesBLSocket(base.MaxwellSimSocket):
def value(self, value: bpy.types.NodeTree) -> None:
self.raw_value = value
####################
# - Socket Configuration
####################
@ -88,6 +90,7 @@ class BlenderGeoNodesSocketDef(pyd.BaseModel):
def init(self, bl_socket: BlenderGeoNodesBLSocket) -> None:
pass
####################
# - Blender Registration
####################

View File

@ -6,28 +6,29 @@ import pydantic as pyd
from .. import base
from ... import contracts as ct
####################
# - Blender Socket
####################
class BlenderImageBLSocket(base.MaxwellSimSocket):
socket_type = ct.SocketType.BlenderImage
bl_label = "Blender Image"
bl_label = 'Blender Image'
####################
# - Properties
####################
raw_value: bpy.props.PointerProperty(
name="Blender Image",
description="Represents a Blender Image",
name='Blender Image',
description='Represents a Blender Image',
type=bpy.types.Image,
update=(lambda self, context: self.sync_prop("raw_value", context)),
update=(lambda self, context: self.sync_prop('raw_value', context)),
)
####################
# - UI
####################
def draw_value(self, col: bpy.types.UILayout) -> None:
col.prop(self, "raw_value", text="")
col.prop(self, 'raw_value', text='')
####################
# - Default Value
@ -40,6 +41,7 @@ class BlenderImageBLSocket(base.MaxwellSimSocket):
def value(self, value: bpy.types.Image) -> None:
self.raw_value = value
####################
# - Socket Configuration
####################
@ -49,9 +51,8 @@ class BlenderImageSocketDef(pyd.BaseModel):
def init(self, bl_socket: BlenderImageBLSocket) -> None:
pass
####################
# - Blender Registration
####################
BL_REGISTER = [
BlenderImageBLSocket
]
BL_REGISTER = [BlenderImageBLSocket]

View File

@ -6,16 +6,17 @@ import pydantic as pyd
from .. import base
from ... import contracts as ct
####################
# - Create and Assign BL Object
####################
class BlenderMaxwellCreateAndAssignBLObject(bpy.types.Operator):
bl_idname = "blender_maxwell.create_and_assign_bl_object"
bl_label = "Create and Assign BL Object"
bl_idname = 'blender_maxwell.create_and_assign_bl_object'
bl_label = 'Create and Assign BL Object'
node_tree_name = bpy.props.StringProperty(name="Node Tree Name")
node_name = bpy.props.StringProperty(name="Node Name")
socket_name = bpy.props.StringProperty(name="Socket Name")
node_tree_name = bpy.props.StringProperty(name='Node Tree Name')
node_name = bpy.props.StringProperty(name='Node Name')
socket_name = bpy.props.StringProperty(name='Socket Name')
def execute(self, context):
node_tree = bpy.data.node_groups[self.node_tree_name]
@ -26,21 +27,22 @@ class BlenderMaxwellCreateAndAssignBLObject(bpy.types.Operator):
return {'FINISHED'}
####################
# - Blender Socket
####################
class BlenderObjectBLSocket(base.MaxwellSimSocket):
socket_type = ct.SocketType.BlenderObject
bl_label = "Blender Object"
bl_label = 'Blender Object'
####################
# - Properties
####################
raw_value: bpy.props.PointerProperty(
name="Blender Object",
description="Represents a Blender object",
name='Blender Object',
description='Represents a Blender object',
type=bpy.types.Object,
update=(lambda self, context: self.sync_prop("raw_value", context)),
update=(lambda self, context: self.sync_prop('raw_value', context)),
)
####################
@ -50,24 +52,24 @@ class BlenderObjectBLSocket(base.MaxwellSimSocket):
label_col_row.label(text=text)
op = label_col_row.operator(
"blender_maxwell.create_and_assign_bl_object",
text="",
icon="ADD",
'blender_maxwell.create_and_assign_bl_object',
text='',
icon='ADD',
)
op.socket_name = self.name
op.node_name = self.node.name
op.node_tree_name = self.node.id_data.name
def draw_value(self, col: bpy.types.UILayout) -> None:
col.prop(self, "raw_value", text="")
col.prop(self, 'raw_value', text='')
####################
# - Methods
####################
def create_and_assign_bl_object(self):
node_tree = self.node.id_data
mesh = bpy.data.meshes.new("MaxwellMesh")
new_bl_object = bpy.data.objects.new("MaxwellObject", mesh)
mesh = bpy.data.meshes.new('MaxwellMesh')
new_bl_object = bpy.data.objects.new('MaxwellObject', mesh)
bpy.context.collection.objects.link(new_bl_object)
@ -84,6 +86,7 @@ class BlenderObjectBLSocket(base.MaxwellSimSocket):
def value(self, value: bpy.types.Object) -> None:
self.raw_value = value
####################
# - Socket Configuration
####################
@ -93,6 +96,7 @@ class BlenderObjectSocketDef(pyd.BaseModel):
def init(self, bl_socket: BlenderObjectBLSocket) -> None:
pass
####################
# - Blender Registration
####################

View File

@ -6,28 +6,29 @@ import pydantic as pyd
from .. import base
from ... import contracts as ct
####################
# - Blender Socket
####################
class BlenderTextBLSocket(base.MaxwellSimSocket):
socket_type = ct.SocketType.BlenderText
bl_label = "Blender Text"
bl_label = 'Blender Text'
####################
# - Properties
####################
raw_value: bpy.props.PointerProperty(
name="Blender Text",
description="Represents a Blender text datablock",
name='Blender Text',
description='Represents a Blender text datablock',
type=bpy.types.Text,
update=(lambda self, context: self.sync_prop("raw_value", context)),
update=(lambda self, context: self.sync_prop('raw_value', context)),
)
####################
# - UI
####################
def draw_value(self, col: bpy.types.UILayout) -> None:
col.prop(self, "raw_value", text="")
col.prop(self, 'raw_value', text='')
####################
# - Default Value
@ -40,6 +41,7 @@ class BlenderTextBLSocket(base.MaxwellSimSocket):
def value(self, value: bpy.types.Text) -> None:
self.raw_value = value
####################
# - Socket Configuration
####################
@ -49,9 +51,8 @@ class BlenderTextSocketDef(pyd.BaseModel):
def init(self, bl_socket: BlenderTextBLSocket) -> None:
pass
####################
# - Blender Registration
####################
BL_REGISTER = [
BlenderTextBLSocket
]
BL_REGISTER = [BlenderTextBLSocket]

View File

@ -1,22 +1,29 @@
from . import bound_cond
from . import bound_conds
MaxwellBoundCondSocketDef = bound_cond.MaxwellBoundCondSocketDef
MaxwellBoundCondsSocketDef = bound_conds.MaxwellBoundCondsSocketDef
from . import medium
from . import medium_non_linearity
MaxwellMediumSocketDef = medium.MaxwellMediumSocketDef
MaxwellMediumNonLinearitySocketDef = medium_non_linearity.MaxwellMediumNonLinearitySocketDef
MaxwellMediumNonLinearitySocketDef = (
medium_non_linearity.MaxwellMediumNonLinearitySocketDef
)
from . import source
from . import temporal_shape
MaxwellSourceSocketDef = source.MaxwellSourceSocketDef
MaxwellTemporalShapeSocketDef = temporal_shape.MaxwellTemporalShapeSocketDef
from . import structure
MaxwellStructureSocketDef = structure.MaxwellStructureSocketDef
from . import monitor
MaxwellMonitorSocketDef = monitor.MaxwellMonitorSocketDef
from . import fdtd_sim
@ -24,6 +31,7 @@ from . import fdtd_sim_data
from . import sim_grid
from . import sim_grid_axis
from . import sim_domain
MaxwellFDTDSimSocketDef = fdtd_sim.MaxwellFDTDSimSocketDef
MaxwellFDTDSimDataSocketDef = fdtd_sim_data.MaxwellFDTDSimDataSocketDef
MaxwellSimGridSocketDef = sim_grid.MaxwellSimGridSocketDef

View File

@ -8,31 +8,34 @@ import tidy3d as td
from .. import base
from ... import contracts as ct
class MaxwellBoundCondBLSocket(base.MaxwellSimSocket):
socket_type = ct.SocketType.MaxwellBoundCond
bl_label = "Maxwell Bound Face"
bl_label = 'Maxwell Bound Face'
####################
# - Properties
####################
default_choice: bpy.props.EnumProperty(
name="Bound Face",
description="A choice of default boundary face",
name='Bound Face',
description='A choice of default boundary face',
items=[
("PML", "PML", "Perfectly matched layer"),
("PEC", "PEC", "Perfect electrical conductor"),
("PMC", "PMC", "Perfect magnetic conductor"),
("PERIODIC", "Periodic", "Infinitely periodic layer"),
('PML', 'PML', 'Perfectly matched layer'),
('PEC', 'PEC', 'Perfect electrical conductor'),
('PMC', 'PMC', 'Perfect magnetic conductor'),
('PERIODIC', 'Periodic', 'Infinitely periodic layer'),
],
default="PML",
update=(lambda self, context: self.sync_prop("default_choice", context)),
default='PML',
update=(
lambda self, context: self.sync_prop('default_choice', context)
),
)
####################
# - UI
####################
def draw_value(self, col: bpy.types.UILayout) -> None:
col.prop(self, "default_choice", text="")
col.prop(self, 'default_choice', text='')
####################
# - Computation of Default Value
@ -40,27 +43,31 @@ class MaxwellBoundCondBLSocket(base.MaxwellSimSocket):
@property
def value(self) -> td.BoundarySpec:
return {
"PML": td.PML(num_layers=12),
"PEC": td.PECBoundary(),
"PMC": td.PMCBoundary(),
"PERIODIC": td.Periodic(),
'PML': td.PML(num_layers=12),
'PEC': td.PECBoundary(),
'PMC': td.PMCBoundary(),
'PERIODIC': td.Periodic(),
}[self.default_choice]
@value.setter
def value(self, value: typx.Literal["PML", "PEC", "PMC", "PERIODIC"]) -> None:
def value(
self, value: typx.Literal['PML', 'PEC', 'PMC', 'PERIODIC']
) -> None:
self.default_choice = value
####################
# - Socket Configuration
####################
class MaxwellBoundCondSocketDef(pyd.BaseModel):
socket_type: ct.SocketType = ct.SocketType.MaxwellBoundCond
default_choice: typx.Literal["PML", "PEC", "PMC", "PERIODIC"] = "PML"
default_choice: typx.Literal['PML', 'PEC', 'PMC', 'PERIODIC'] = 'PML'
def init(self, bl_socket: MaxwellBoundCondBLSocket) -> None:
bl_socket.value = self.default_choice
####################
# - Blender Registration
####################

View File

@ -8,73 +8,76 @@ from .. import base
from ... import contracts as ct
BOUND_FACE_ITEMS = [
("PML", "PML", "Perfectly matched layer"),
("PEC", "PEC", "Perfect electrical conductor"),
("PMC", "PMC", "Perfect magnetic conductor"),
("PERIODIC", "Periodic", "Infinitely periodic layer"),
('PML', 'PML', 'Perfectly matched layer'),
('PEC', 'PEC', 'Perfect electrical conductor'),
('PMC', 'PMC', 'Perfect magnetic conductor'),
('PERIODIC', 'Periodic', 'Infinitely periodic layer'),
]
BOUND_MAP = {
"PML": td.PML(),
"PEC": td.PECBoundary(),
"PMC": td.PMCBoundary(),
"PERIODIC": td.Periodic(),
'PML': td.PML(),
'PEC': td.PECBoundary(),
'PMC': td.PMCBoundary(),
'PERIODIC': td.Periodic(),
}
class MaxwellBoundCondsBLSocket(base.MaxwellSimSocket):
socket_type = ct.SocketType.MaxwellBoundConds
bl_label = "Maxwell Bound Box"
bl_label = 'Maxwell Bound Box'
####################
# - Properties
####################
show_definition: bpy.props.BoolProperty(
name="Show Bounds Definition",
description="Toggle to show bound faces",
name='Show Bounds Definition',
description='Toggle to show bound faces',
default=False,
update=(lambda self, context: self.sync_prop("show_definition", context)),
update=(
lambda self, context: self.sync_prop('show_definition', context)
),
)
x_pos: bpy.props.EnumProperty(
name="+x Bound Face",
description="+x choice of default boundary face",
name='+x Bound Face',
description='+x choice of default boundary face',
items=BOUND_FACE_ITEMS,
default="PML",
update=(lambda self, context: self.sync_prop("x_pos", context)),
default='PML',
update=(lambda self, context: self.sync_prop('x_pos', context)),
)
x_neg: bpy.props.EnumProperty(
name="-x Bound Face",
description="-x choice of default boundary face",
name='-x Bound Face',
description='-x choice of default boundary face',
items=BOUND_FACE_ITEMS,
default="PML",
update=(lambda self, context: self.sync_prop("x_neg", context)),
default='PML',
update=(lambda self, context: self.sync_prop('x_neg', context)),
)
y_pos: bpy.props.EnumProperty(
name="+y Bound Face",
description="+y choice of default boundary face",
name='+y Bound Face',
description='+y choice of default boundary face',
items=BOUND_FACE_ITEMS,
default="PML",
update=(lambda self, context: self.sync_prop("y_pos", context)),
default='PML',
update=(lambda self, context: self.sync_prop('y_pos', context)),
)
y_neg: bpy.props.EnumProperty(
name="-y Bound Face",
description="-y choice of default boundary face",
name='-y Bound Face',
description='-y choice of default boundary face',
items=BOUND_FACE_ITEMS,
default="PML",
update=(lambda self, context: self.sync_prop("y_neg", context)),
default='PML',
update=(lambda self, context: self.sync_prop('y_neg', context)),
)
z_pos: bpy.props.EnumProperty(
name="+z Bound Face",
description="+z choice of default boundary face",
name='+z Bound Face',
description='+z choice of default boundary face',
items=BOUND_FACE_ITEMS,
default="PML",
update=(lambda self, context: self.sync_prop("z_pos", context)),
default='PML',
update=(lambda self, context: self.sync_prop('z_pos', context)),
)
z_neg: bpy.props.EnumProperty(
name="-z Bound Face",
description="-z choice of default boundary face",
name='-z Bound Face',
description='-z choice of default boundary face',
items=BOUND_FACE_ITEMS,
default="PML",
update=(lambda self, context: self.sync_prop("z_neg", context)),
default='PML',
update=(lambda self, context: self.sync_prop('z_neg', context)),
)
####################
@ -83,25 +86,25 @@ class MaxwellBoundCondsBLSocket(base.MaxwellSimSocket):
def draw_label_row(self, row: bpy.types.UILayout, text) -> None:
row.label(text=text)
row.prop(
self, "show_definition", toggle=True, text="", icon="MOD_LENGTH"
self, 'show_definition', toggle=True, text='', icon='MOD_LENGTH'
)
def draw_value(self, col: bpy.types.UILayout) -> None:
if not self.show_definition: return
if not self.show_definition:
return
for axis in ["x", "y", "z"]:
for axis in ['x', 'y', 'z']:
row = col.row(align=False)
split = row.split(factor=0.2, align=False)
_col = split.column(align=True)
_col.alignment = "RIGHT"
_col.label(text=axis + " -")
_col.label(text=" +")
_col.alignment = 'RIGHT'
_col.label(text=axis + ' -')
_col.label(text=' +')
_col = split.column(align=True)
_col.prop(self, axis + "_neg", text="")
_col.prop(self, axis + "_pos", text="")
_col.prop(self, axis + '_neg', text='')
_col.prop(self, axis + '_pos', text='')
####################
# - Computation of Default Value
@ -123,6 +126,7 @@ class MaxwellBoundCondsBLSocket(base.MaxwellSimSocket):
),
)
####################
# - Socket Configuration
####################
@ -132,6 +136,7 @@ class MaxwellBoundCondsSocketDef(pyd.BaseModel):
def init(self, bl_socket: MaxwellBoundCondsBLSocket) -> None:
pass
####################
# - Blender Registration
####################

View File

@ -7,14 +7,16 @@ import tidy3d as td
from .. import base
from ... import contracts as ct
class MaxwellFDTDSimBLSocket(base.MaxwellSimSocket):
socket_type = ct.SocketType.MaxwellFDTDSim
bl_label = "Maxwell FDTD Simulation"
bl_label = 'Maxwell FDTD Simulation'
@property
def value(self) -> None:
return None
####################
# - Socket Configuration
####################
@ -24,6 +26,7 @@ class MaxwellFDTDSimSocketDef(pyd.BaseModel):
def init(self, bl_socket: MaxwellFDTDSimBLSocket) -> None:
pass
####################
# - Blender Registration
####################

View File

@ -7,14 +7,16 @@ import tidy3d as td
from .. import base
from ... import contracts as ct
class MaxwellFDTDSimDataBLSocket(base.MaxwellSimSocket):
socket_type = ct.SocketType.MaxwellFDTDSimData
bl_label = "Maxwell FDTD Simulation"
bl_label = 'Maxwell FDTD Simulation'
@property
def value(self):
return None
####################
# - Socket Configuration
####################
@ -24,6 +26,7 @@ class MaxwellFDTDSimDataSocketDef(pyd.BaseModel):
def init(self, bl_socket: MaxwellFDTDSimDataBLSocket) -> None:
pass
####################
# - Blender Registration
####################

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