diff --git a/FUTURE.md b/FUTURE.md new file mode 100644 index 0000000..855b826 --- /dev/null +++ b/FUTURE.md @@ -0,0 +1,52 @@ +# Projects / Plugins +## Larger Architectural Changes +[ ] Dedicated way of generating properties for sockets and nodes, incl. helping make better (less boilerplatey) use of callbacks + - Perhaps, we should also go 100% custom `PropertyGroup`, to the point that we have `nodes`, `sockets` and `props`. + - This would allow far simplified sockets (the more complex kinds), and help standardize use of ex. units in node properties. + - Having a dedicated base class for custom props would help avoid issues like forgetting to run `self.sync_prop` on every goddamn update method in every goddamn socket. +[ ] Dedicated way of handling node-specific operators without all the boilerplate. + +## Field Data +[ ] Directly dealing with field data, instead of having field manipulations be baked into viz node(s). +[ ] Yee Cell Data as Attributes on By-Cell Point Cloud w/GeoNodes Integrations +- In effect, when we have xarray data defined based on Yee Cells ex. Poynting vector coordinates, let's import this to Blender as a simple point cloud centered at each cell and grant each an attribute corresponding to the data. +- What we can then do is use vanilla GeoNodes to ex. read the vector attribute, and draw small arrow meshes (maybe resampled which auto-interpolates the field values) from each point, thus effectively visualizing . vector fields and many other fun things. +- Of course, this is no good for volume cell data - but we can just overlay the raw volume cell data as we please. We can also, if we're sneaky, deal with our volume data as points as far as we can, and then finally do a "points to volume" type deal to make it sufficiently "fluffy/cloudy". +- I wonder if we could use the Attribute node in the shader editor to project interpolated values from points, onto a ex. plane mesh, in a way that would also be visualizable in the viewport. + +## Tidy3D Features +[ ] Symmetry for Performance +- [ ] Implement +[ ] Dispersive Model Fitting +[ ] Scattering Matrix Calculator +[ ] Resonance Finder +[ ] Adjoint Optimization +[ ] Design Space Exploration / Parameterization + +## Preview Semantics +[ ] Node tree header toggle that toggles a modal operator on and off, which constantly checks the context of the selected nodes, and tries to `bl_select` them (which in turn, should cause the node base class to `bl_select` shit inside). +- Shouldn't survive a file save; always startup with this thing off. +[ ] Custom gizmos attached to preview toggles! +- There is a WIP for GN-driven gizmos: +- Probably best to wait for that, then just add gizmos to existing driven GN trees, as opposed to unholy OGL spaghetti. +[ ] Node-ManagedObj Selection binding +- BL to Node: + - Trigger: The post-depsgraph handler seems appropriate. + - Input: Read the object location (origin), using a unit system. + - Output: Write the input socket value. + - Condition: Input socket is unlinked. (If it's linked, then lock the object's position. Use sync_link_added() for that) +- Node to BL: + - Trigger: "Report" action on an input socket that the managed object declares reliance on. + - Input: The input socket value (linked or unlinked) + - Output: The object location (origin), using a unit system. + +## Parametric Geometry UX +[ ] Consider allowing a mesh attribute (set in ex. geometry node) to specify the name of a medium. +- This allows assembling complex multi-medium structures in one geonodes tree. +- This should result in the spawning of several Medium input sockets in the GeoNodes structure node, named as the attributes are. +- The GeoNodes structure node should then output as array-like TriMeshes, for which mediums are correctly defined. + +## Alternative Engines +[ ] Heat Solver +[ ] MEEP integration () +- The main boost would be if we could setup a MEEP simulation entirely from a td.Simulation object. diff --git a/README.md b/README.md index 73117da..e4d9abd 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,24 @@ # Nodes +**LEGEND**: +- [-] Exists but doesn't quite work good enough. +- [x] Done to working degree (the standard is "good enough for the demo"). + - See check marks underneath +- [?] Unsure whether we should do this. + ## Inputs [x] Wave Constant -- [ ] Implement export of frequency / wavelength ranges. -[ ] Unit System +- [ ] Implement export of frequency / wavelength array/range. +[-] Unit System - [ ] Implement presets, including "Tidy3D" and "Blender", shown in the label row. -[ ] Constants / Blender Constant -[ ] Constants / Number Constant +[ ] Constants / Scientific Constant +[x] Constants / Number Constant [ ] Constants / Physical Constant - [ ] Pol: Elliptical plot viz - [ ] Pol: Poincare sphere viz -[ ] Constants / Scientific Constant +[x] Constants / Blender Constant -[ ] Web / Tidy3D Web Importer +[x] Web / Tidy3D Web Importer [ ] File Import / JSON File Import - [ ] Dropdown to choose various supported JSON-sourced objects incl. @@ -25,12 +31,14 @@ - [ ] Implement a LazyValue to provide a data path that avoids having to load massive arrays every time always. ## Outputs -[ ] Viewer +[x] Viewer +- [ ] **BIG ONE**: Remove image preview when disabling plots. +- [ ] Either enforce singleton, or find a way to have several viewers at the same time. - [ ] A setting that live-previews just a value. - [ ] Pop-up multiline string print as alternative to console print. -- [ ] Toggleable auto-plot, auto-3D-preview, auto-value-view, (?)auto-text-view. +- [x] Toggleable auto-plot, auto-3D-preview, auto-value-view, (?)auto-text-view. -[ ] File Export / JSON File Export +[x] File Export / JSON File Export [ ] File Import / Tidy3D File Export - [ ] Implement HDF-based export of Tidy3D-exported object (which includes ex. mesh data and such) [ ] File Export / Array File Export @@ -41,16 +49,18 @@ ## Viz [ ] Monitor Data Viz - [ ] Implement dropdown to choose which monitor in the SimulationData should be visualized (based on which are available in the SimulationData), and implement visualization based on every kind of monitor-adjascent output data type () +- [ ] Project field values onto a plane object (managed) ## Sources -[ ] Temporal Shapes / Gaussian Pulse Temporal Shape -[ ] Temporal Shapes / Continuous Wave Temporal Shape +[x] Temporal Shapes / Gaussian Pulse Temporal Shape +[x] Temporal Shapes / Continuous Wave Temporal Shape [ ] Temporal Shapes / Symbolic Temporal Shape - [ ] Specify a Sympy function to generate appropriate array based on [ ] Temporal Shapes / Array Temporal Shape -[ ] Point Dipole Source -[ ] Plane Wave Source +[x] Point Dipole Source +- [ ] Consider a "real" mesh - the empty kind of gets stuck inside of the sim domain. +[-] Plane Wave Source - [ ] Implement an oriented vector input with 3D preview. [ ] Uniform Current Source [ ] TFSF Source @@ -84,8 +94,9 @@ ## Structures [ ] BLObject Structure -[ ] GeoNodes Structure -- [ ] Use the modifier itself as memory, via the ManagedObj +[x] GeoNodes Structure +- [x] Rewrite the `bl_socket_map.py` +- [x] Use the modifier itself as memory, via the ManagedObj - [?] When GeoNodes themselves declare panels, implement a grid-like tab system to select which sockets should be exposed in the node at a given point in time. [ ] Primitive Structures / Plane @@ -120,11 +131,11 @@ ## Simulations [-] FDTDSim -[-] Sim Domain +[x] Sim Domain - [ ] By-Medium batching of Structures when building the td.Simulation object, which can have significant performance implications. -[-] Boundary Conds -- [ ] Rename from Bounds / BoundBox +[x] Boundary Conds +- [x] Rename from Bounds / BoundBox [ ] Boundary Cond / PML Bound Face - [ ] Implement dropdown for "Normal" and "Stable" [ ] Boundary Cond / PEC Bound Face @@ -174,36 +185,59 @@ # Benchmark / Example Sims -- [ ] Tunable Chiral Metasurface +[ ] Research-Grade Experiment +- Membrane 15nm thickness suspended in air +- Square lattice of holes period 900nm (900nm between each hole, air inside holes) +- Holes square radius 100nm +- Square lattice +- Analysis of transmission +- Guided mode resonance +[ ] Tunable Chiral Metasurface # Sockets ## Basic -[ ] Any -[ ] Bool -[ ] String +[x] Any +[x] Bool +[x] String - [ ] Rename from "Text" -[ ] File Path +[x] File Path +[x] Color + +## Number +[x] Integer +[x] Rational +- [ ] Implement constrained SympyExpr check for Rational. +[x] Real +- [ ] Implement min/max for ex. 0..1 factor support. +- [ ] Implement constrained SympyExpr check for Rational. +[x] Complex ## Blender -[ ] Object -[ ] Collection +[x] Object +- [ ] Implement default SocketDef object name +[x] Collection +- [ ] Implement default SocketDef collection name -[ ] Image +[x] Image +- [ ] Implement default SocketDef image name -[ ] GeoNodes -[ ] Text +[x] GeoNodes +- [ ] Implement default SocketDef geonodes name +[x] Text +- [ ] Implement default SocketDef object name ## Maxwell -[ ] Bound Conds +[x] Bound Conds [ ] Bound Cond -[ ] Medium +[x] Medium [ ] Medium Non-Linearity -[ ] Source +[x] Source [ ] Temporal Shape +- [ ] Sane-default pulses for easy access. [ ] Structure [ ] Monitor @@ -217,47 +251,46 @@ [ ] Simulation Data ## Tidy3D -[ ] Cloud Task - -## Number -[ ] Integer -[ ] Rational -[ ] Real -[ ] Complex +[x] Cloud Task +- [ ] Implement switcher for API-key-having config filconfig file vs. direct entry of API key. It should be auto-filled with the config file when such a thing exists. ## Physical -[ ] Unit System +[x] Unit System - [ ] Implement more comprehensible UI; honestly, probably with the new panels () -[ ] Time +[x] Time -[ ] Angle +[x] Angle [ ] Solid Angle (steradian) -[ ] Frequency (hertz) +[x] Frequency (hertz) [ ] Angular Frequency (`rad*hertz`) ### Cartesian -[ ] Length -[ ] Area -[ ] Volume +[x] Length +[x] Area +[x] Volume [ ] Point 1D [ ] Point 2D -[ ] Point 3D +[x] Point 3D [ ] Size 2D -[ ] Size 3D +[x] Size 3D + +[ ] Rotation 3D +- [ ] Implement Euler methods +- [ ] Implement Quaternion methods ### Mechanical [ ] Mass -[ ] Speed +[x] Speed [ ] Velocity 3D -[ ] Acceleration Scalar +[x] Acceleration Scalar [ ] Acceleration 3D -[ ] Force Scalar +[x] Force Scalar [ ] Force 3D [ ] Pressure -### Statistical +### Energy [ ] Energy (joule) [ ] Power (watt) [ ] Temperature @@ -283,7 +316,7 @@ [ ] Illuminance (lux) ### Optical [ ] Jones Polarization -[ ] Polarization +[ ] Polarization (Stokes) @@ -295,18 +328,21 @@ # Architecture ## Registration and Contracts -[ ] Finish the contract code converting from Blender sockets to our sockets based on dimensionality and the property description. -[ ] Refactor the node category code; it's ugly as all fuck. +[x] Finish the contract code converting from Blender sockets to our sockets based on dimensionality and the property description. +[ ] Refactor the node category code; it's ugly. [?] Would be nice with some kind of indicator somewhere to help set good socket descriptions when using geonodes and wanting units. ## Managed Objects -[ ] Implement modifier support on the managed BL object, with special attention paid to the needs of the GeoNodes socket. -- [ ] Implement preview toggling too, ex. using the relevant node tree collections +[x] Implement modifier support on the managed BL object, with special attention paid to the needs of the GeoNodes socket. +- [x] Implement preview toggling too, ex. using the relevant node tree collections - Remember, the managed object is "dumb". It's the node's responsibility to react to any relevant `on_value_change`, and forward all state needed by the modifier to the managed obj. It's only the managed obj's responsibility to not update any modifier value that wouldn't change anything. [ ] Implement loading the xarray-defined voxels into OpenVDB, saving it, and loading it as a managed BL object with the volume setting. [ ] Implement basic jax-driven volume voxel processing, especially cube based slicing. [ ] Implement jax-driven linear interpolation of volume voxels to an image texture, whose pixels are sized according to the dimensions of another managed plane object (perhaps a uniquely described Managed BL object itself). +## Utils or Services +[ ] Dedicated module for managing the interaction with the tidy3d cloud, to help nuke all the random caches out of existance. + ## Node Base Class [ ] Dedicated `draw_preview`-type draw functions for plot customizations. - [ ] For now, previewing isn't something I think should be part of the node @@ -314,8 +350,9 @@ [ ] When presets are used, if a preset is selected and the user alters a preset setting, then dynamically switch the preset indicator back to "Custom" to indicate that there is no active preset [ ] It seems that `node.inputs` and `node.outputs` allows the use of a `move` method, which may allow reordering sockets dynamically, which we should expose to the user as user-configurable ordering rules (maybe resolved with a constraint solver). [?] Mechanism for dynamic names (ex. "Library Medium" becoming "Au Medium") -[ ] Mechanism for selecting a blender object managed by a particular node. +[-] Mechanism for selecting a blender object managed by a particular node. [ ] Mechanism for ex. specially coloring a node that is currently participating in the preview. +[ ] Custom callbacks when deleting a node (in `free()`), to ex. delete all previews with the viewer node. ## Socket Base Class [ ] A feature `use_array` which allows a socket to declare that it can be both a single value and array-like (possibly constrained to a given shape). This should also allow the SocketDef to request that the input socket be initialised as a multi-input socket, once Blender updates to support those. @@ -352,46 +389,3 @@ [ ] Test on Windows ## Node Tree Cache Semantics - -## Projects / Plugins -### Field Data -[ ] Directly dealing with field data, instead of having field manipulations be baked into viz node(s). -[ ] Yee Cell Data as Attributes on By-Cell Point Cloud w/GeoNodes Integrations -- In effect, when we have xarray data defined based on Yee Cells ex. Poynting vector coordinates, let's import this to Blender as a simple point cloud centered at each cell and grant each an attribute corresponding to the data. -- What we can then do is use vanilla GeoNodes to ex. read the vector attribute, and draw small arrow meshes (maybe resampled which auto-interpolates the field values) from each point, thus effectively visualizing . vector fields and many other fun things. -- Of course, this is no good for volume cell data - but we can just overlay the raw volume cell data as we please. We can also, if we're sneaky, deal with our volume data as points as far as we can, and then finally do a "points to volume" type deal to make it sufficiently "fluffy/cloudy". -- I wonder if we could use the Attribute node in the shader editor to project interpolated values from points, onto a ex. plane mesh, in a way that would also be visualizable in the viewport. - -### Tidy3D Features -[ ] Symmetry for Performance -- [ ] Implement -[ ] Dispersive Model Fitting -[ ] Scattering Matrix Calculator -[ ] Resonance Finder -[ ] Adjoint Optimization -[ ] Design Space Exploration / Parameterization - -### Preview Semantics -[ ] Custom gizmos attached to preview toggles! -- There is a WIP for GN-driven gizmos: -- Probably best to wait for that, then just add gizmos to existing driven GN trees, as opposed to unholy OGL spaghetti. -[ ] Node-ManagedObj Selection binding -- BL to Node: - - Trigger: The post-depsgraph handler seems appropriate. - - Input: Read the object location (origin), using a unit system. - - Output: Write the input socket value. - - Condition: Input socket is unlinked. (If it's linked, then lock the object's position. Use sync_link_added() for that) -- Node to BL: - - Trigger: "Report" action on an input socket that the managed object declares reliance on. - - Input: The input socket value (linked or unlinked) - - Output: The object location (origin), using a unit system. - -### Parametric Geometry UX -[ ] Consider allowing a mesh attribute (set in ex. geometry node) to specify the name of a medium. -- This allows assembling complex multi-medium structures in one geonodes tree. -- This should result in the spawning of several Medium input sockets in the GeoNodes structure node, named as the attributes are. -- The GeoNodes structure node should then output as array-like TriMeshes, for which mediums are correctly defined. - -### Alternative Engines -[ ] MEEP integration () -- The main boost would be if we could setup a MEEP simulation entirely from a td.Simulation object. diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/bl_socket_map.py b/blender_maxwell/node_trees/maxwell_sim_nodes/bl_socket_map.py new file mode 100644 index 0000000..cd2d665 --- /dev/null +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/bl_socket_map.py @@ -0,0 +1,251 @@ +import typing as typ +import typing_extensions as typx + +import pydantic as pyd +import sympy as sp +import sympy.physics.units as spu + +import bpy + +from ...utils import extra_sympy_units as spuex +from . import contracts as ct +from .contracts import SocketType as ST +from . import sockets as sck + +# TODO: Caching? +# TODO: Move the manual labor stuff to contracts + +BLSocketType = str ## A Blender-Defined Socket Type +BLSocketSize = int +DescType = str +Unit = typ.Any ## Type of a valid unit + +#################### +# - Socket to SocketDef +#################### +SOCKET_DEFS = { + socket_type: getattr( + sck, + socket_type.value.removesuffix("SocketType") + "SocketDef", + ) + for socket_type in ST + 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", + ): + print("Missing SocketDef for", socket_type.value) + + +#################### +# - BL Socket Size Parser +#################### +BL_SOCKET_3D_TYPE_PREFIXES = { + "NodeSocketVector", + "NodeSocketRotation", +} +BL_SOCKET_4D_TYPE_PREFIXES = { + "NodeSocketColor", +} +def size_from_bl_interface_socket( + bl_interface_socket: bpy.types.NodeTreeInterfaceSocket +) -> typx.Literal[1, 2, 3, 4]: + """Parses the `size`, aka. number of elements, contained within the `default_value` of a Blender interface socket. + + Since there are no 2D sockets in Blender, the user can specify "2D" in the Blender socket's description to "promise" that only the first two values will be used. + When this is done, the third value is left entirely untouched by this entire system. + + A hard-coded set of NodeSocket prefixes are used to determine which interface sockets are, in fact, 3D. + - For 3D sockets, a hard-coded list of Blender node socket types is used. + - Else, it is a 1D socket type. + """ + if bl_interface_socket.description.startswith("2D"): return 2 + if any( + bl_interface_socket.socket_type.startswith(bl_socket_3d_type_prefix) + for bl_socket_3d_type_prefix in BL_SOCKET_3D_TYPE_PREFIXES + ): + return 3 + if any( + bl_interface_socket.socket_type.startswith(bl_socket_4d_type_prefix) + for bl_socket_4d_type_prefix in BL_SOCKET_4D_TYPE_PREFIXES + ): + return 4 + + return 1 + + +#################### +# - BL Socket Type / Unit Parser +#################### +def parse_bl_interface_socket( + bl_interface_socket: bpy.types.NodeTreeInterfaceSocket, +) -> tuple[ST, sp.Expr | None]: + """Parse a Blender interface socket by parsing its description, falling back to any direct type links. + + Arguments: + bl_interface_socket: An interface socket associated with the global input to a node tree. + + Returns: + The type of a corresponding MaxwellSimSocket, as well as a unit (if a particular unit was requested by the Blender interface socket). + """ + size = size_from_bl_interface_socket(bl_interface_socket) + + # Determine Direct Socket Type + if ( + direct_socket_type := ct.BL_SOCKET_DIRECT_TYPE_MAP.get( + (bl_interface_socket.socket_type, size) + ) + ) is None: + msg = "Blender interface socket has no mapping among 'MaxwellSimSocket's." + raise ValueError(msg) + + # (Maybe) Return Direct Socket Type + ## When there's no description, that's it; return. + if not ct.BL_SOCKET_DESCR_ANNOT_STRING in bl_interface_socket.description: + return (direct_socket_type, None) + + # Parse Description for Socket Type + tokens = ( + _tokens + if (_tokens := bl_interface_socket.description.split(" "))[0] != "2D" + else _tokens[1:] + ) ## Don't include the "2D" token, if defined. + if ( + socket_type := ct.BL_SOCKET_DESCR_TYPE_MAP.get( + (tokens[0], bl_interface_socket.socket_type, size) + ) + ) is None: + return (direct_socket_type, None) ## Description doesn't map to anything + + # 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]: + # Compute () as Unit Token + unit_token = tokens[1].removeprefix("(").removesuffix(")") + + # Compare Unit Token to Valid Sympy-Printed Units + socket_unit = _socket_unit if (_socket_unit := [ + unit + for unit in ct.SOCKET_UNITS[socket_type]["values"].values() + if str(unit) == unit_token + ]) else ct.SOCKET_UNITS[socket_type]["values"][ + ct.SOCKET_UNITS[socket_type]["default"] + ] + ## TODO: Enforce abbreviated sympy printing here, not globally + + return (socket_type, socket_unit) + + +#################### +# - BL Socket Interface Definition +#################### +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] + ] + + +#################### +# - Extract Default Interface Socket Value +#################### +def value_from_bl( + bl_interface_socket: bpy.types.NodeTreeInterfaceSocket, + unit_system: dict | None = None, +) -> typ.Any: + """Reads the value of any Blender socket, and writes its `default_value` to the `value` of any `MaxwellSimSocket`. + - If the size of the Blender socket is >1, then `value` is written to as a `sympy.Matrix`. + - If a unit system is given, then the Blender socket is matched to a `MaxwellSimSocket`, which is used to lookup an appropriate unit in the given `unit_system`. + + """ + ## TODO: Consider sympy.S()'ing the default_value + parsed_bl_socket_value = { + 1: lambda: bl_interface_socket.default_value, + 2: lambda: sp.Matrix(tuple(bl_interface_socket.default_value)[:2]), + 3: lambda: sp.Matrix(tuple(bl_interface_socket.default_value)), + 4: lambda: sp.Matrix(tuple(bl_interface_socket.default_value)), + }[size_from_bl_interface_socket(bl_interface_socket)]() + ## The 'lambda' delays construction until size is determined + + socket_type, unit = parse_bl_interface_socket(bl_interface_socket) + + # Add Unit to Parsed (if relevant) + if unit is not None: + parsed_bl_socket_value *= unit + elif unit_system is not None: + parsed_bl_socket_value *= unit_system[socket_type] + + return parsed_bl_socket_value + +#################### +# - Convert to Blender-Compatible Value +#################### +def make_scalar_bl_compat(scalar: typ.Any) -> typ.Any: + """Blender doesn't accept ex. Sympy numbers as values. + Therefore, we need to do some conforming. + + Currently hard-coded; this is probably best. + """ + if isinstance(scalar, sp.Integer): + return int(scalar) + elif isinstance(scalar, sp.Float): + return float(scalar) + elif isinstance(scalar, sp.Rational): + return float(scalar) + elif isinstance(scalar, sp.Expr): + return float(scalar.n()) + ## TODO: More? + + return scalar + +def value_to_bl( + bl_interface_socket: bpy.types.NodeSocket, + value: typ.Any, + unit_system: dict | None = None, +) -> typ.Any: + socket_type, unit = parse_bl_interface_socket(bl_interface_socket) + + # Set Socket + if unit is not None: + bl_socket_value = spu.convert_to(value, unit) / unit + elif ( + unit_system is not None + and socket_type in unit_system + ): + bl_socket_value = spu.convert_to( + value, unit_system[socket_type] + ) / unit_system[socket_type] + else: + bl_socket_value = value + + return { + 1: lambda: make_scalar_bl_compat(bl_socket_value), + 2: lambda: tuple([ + make_scalar_bl_compat(bl_socket_value[0]), + make_scalar_bl_compat(bl_socket_value[1]), + bl_interface_socket.default_value[2] + ## Don't touch (unused) 3rd bl_socket coordinate + ]), + 3: lambda: tuple([ + make_scalar_bl_compat(el) + for el in bl_socket_value + ]), + 4: lambda: tuple([ + make_scalar_bl_compat(el) + for el in bl_socket_value + ]), + }[size_from_bl_interface_socket(bl_interface_socket)]() + ## The 'lambda' delays construction until size is determined diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/__init__.py b/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/__init__.py index 71d2824..ef15d25 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/__init__.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/__init__.py @@ -26,7 +26,11 @@ from .socket_types import SocketType from .socket_units import SOCKET_UNITS from .socket_colors import SOCKET_COLORS from .socket_shapes import SOCKET_SHAPES -from .socket_bl_maps import BLSocketToSocket + +from .socket_from_bl_desc import BL_SOCKET_DESCR_TYPE_MAP +from .socket_from_bl_direct import BL_SOCKET_DIRECT_TYPE_MAP + +from .socket_from_bl_desc import BL_SOCKET_DESCR_ANNOT_STRING #################### # - Node Types diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/node_cat_labels.py b/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/node_cat_labels.py index 6f34484..ba95f67 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/node_cat_labels.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/node_cat_labels.py @@ -29,7 +29,7 @@ NODE_CAT_LABELS = { # Bounds/ NC.MAXWELLSIM_BOUNDS: "Bounds", - NC.MAXWELLSIM_BOUNDS_BOUNDFACES: "Bound Faces", + NC.MAXWELLSIM_BOUNDS_BOUNDCONDS: "Bound Conds", # Monitors/ NC.MAXWELLSIM_MONITORS: "Monitors", diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/node_cats.py b/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/node_cats.py index 4bff4d1..0c8d522 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/node_cats.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/node_cats.py @@ -36,7 +36,7 @@ class NodeCategory(BlenderTypeEnum): # Bounds/ MAXWELLSIM_BOUNDS = enum.auto() - MAXWELLSIM_BOUNDS_BOUNDFACES = enum.auto() + MAXWELLSIM_BOUNDS_BOUNDCONDS = enum.auto() # Monitors/ MAXWELLSIM_MONITORS = enum.auto() diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/node_types.py b/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/node_types.py index 5549843..e6f4325 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/node_types.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/node_types.py @@ -97,16 +97,16 @@ class NodeType(BlenderTypeEnum): # Bounds - BoundBox = enum.auto() + BoundConds = enum.auto() ## Bounds / Bound Faces - PMLBoundFace = enum.auto() - PECBoundFace = enum.auto() - PMCBoundFace = enum.auto() + PMLBoundCond = enum.auto() + PECBoundCond = enum.auto() + PMCBoundCond = enum.auto() - BlochBoundFace = enum.auto() - PeriodicBoundFace = enum.auto() - AbsorbingBoundFace = enum.auto() + BlochBoundCond = enum.auto() + PeriodicBoundCond = enum.auto() + AbsorbingBoundCond = enum.auto() # Monitors diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/schemas/managed_obj.py b/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/schemas/managed_obj.py index dd036a0..925b6ad 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/schemas/managed_obj.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/schemas/managed_obj.py @@ -3,6 +3,8 @@ import typing as typx import pydantic as pyd +import bpy + from ..bl import ManagedObjName, SocketName from ..managed_obj_type import ManagedObjType diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_bl_maps.py b/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_bl_maps.py deleted file mode 100644 index 7e69cc9..0000000 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_bl_maps.py +++ /dev/null @@ -1,119 +0,0 @@ -import sympy.physics.units as spu -from ....utils import extra_sympy_units as spuex - -from .socket_types import SocketType as ST - -Dimensions = int ## Num. Elements in the Socket -BLSocketType = str ## A Blender-Defined Socket Type - -class BLSocketToSocket: - """Encodes ways of converting blender sockets of known dimensionality - to a corresponding SocketType. - - "Dimensionality" is simply how many elements the blender socket has. - The user must explicitly specify this, as blender allows a variable - number of elements for some sockets, and we do not. - """ - #################### - # - Direct BLSocketType -> SocketType - #################### - by_bl_socket_type: dict[Dimensions, dict[BLSocketType, ST]] = { - 1: { - "NodeSocketStandard": ST.Any, - "NodeSocketVirtual": ST.Any, - "NodeSocketGeometry": ST.Any, - "NodeSocketTexture": ST.Any, - "NodeSocketShader": ST.Any, - "NodeSocketMaterial": ST.Any, - - "NodeSocketString": ST.Text, - "NodeSocketBool": ST.Bool, - "NodeSocketCollection": ST.BlenderCollection, - "NodeSocketImage": ST.BlenderImage, - "NodeSocketObject": ST.BlenderObject, - - "NodeSocketFloat": ST.RealNumber, - "NodeSocketFloatAngle": ST.PhysicalAngle, - "NodeSocketFloatDistance": ST.PhysicalLength, - "NodeSocketFloatFactor": ST.RealNumber, - "NodeSocketFloatPercentage": ST.RealNumber, - "NodeSocketFloatTime": ST.PhysicalTime, - "NodeSocketFloatTimeAbsolute": ST.RealNumber, - "NodeSocketFloatUnsigned": ST.RealNumber, - - "NodeSocketInt": ST.IntegerNumber, - "NodeSocketIntFactor": ST.IntegerNumber, - "NodeSocketIntPercentage": ST.IntegerNumber, - "NodeSocketIntUnsigned": ST.IntegerNumber, - }, - 2: { - "NodeSocketVector": ST.Real3DVector, - "NodeSocketVectorAcceleration": ST.Real3DVector, - "NodeSocketVectorDirection": ST.Real3DVector, - "NodeSocketVectorEuler": ST.Real3DVector, - "NodeSocketVectorTranslation": ST.Real3DVector, - "NodeSocketVectorVelocity": ST.Real3DVector, - "NodeSocketVectorXYZ": ST.Real3DVector, - #"NodeSocketVector": ST.Real2DVector, - #"NodeSocketVectorAcceleration": ST.PhysicalAccel2D, - #"NodeSocketVectorDirection": ST.PhysicalDir2D, - #"NodeSocketVectorEuler": ST.PhysicalEuler2D, - #"NodeSocketVectorTranslation": ST.PhysicalDispl2D, - #"NodeSocketVectorVelocity": ST.PhysicalVel2D, - #"NodeSocketVectorXYZ": ST.Real2DPoint, - }, - 3: { - "NodeSocketRotation": ST.Real3DVector, - - "NodeSocketColor": ST.Any, - - "NodeSocketVector": ST.Real3DVector, - #"NodeSocketVectorAcceleration": ST.PhysicalAccel3D, - #"NodeSocketVectorDirection": ST.PhysicalDir3D, - #"NodeSocketVectorEuler": ST.PhysicalEuler3D, - #"NodeSocketVectorTranslation": ST.PhysicalDispl3D, - "NodeSocketVectorTranslation": ST.PhysicalPoint3D, - #"NodeSocketVectorVelocity": ST.PhysicalVel3D, - "NodeSocketVectorXYZ": ST.PhysicalPoint3D, - }, - } - - #################### - # - BLSocket Description-Driven SocketType Choice - #################### - by_description = { - 1: { - "Angle": ST.PhysicalAngle, - - "Length": ST.PhysicalLength, - "Area": ST.PhysicalArea, - "Volume": ST.PhysicalVolume, - - "Mass": ST.PhysicalMass, - - "Speed": ST.PhysicalSpeed, - "Accel": ST.PhysicalAccelScalar, - "Force": ST.PhysicalForceScalar, - - "Freq": ST.PhysicalFreq, - }, - 2: { - #"2DCount": ST.Int2DVector, - - #"2DPoint": ST.PhysicalPoint2D, - #"2DSize": ST.PhysicalSize2D, - #"2DPol": ST.PhysicalPol, - "2DPoint": ST.PhysicalPoint3D, - "2DSize": ST.PhysicalSize3D, - }, - 3: { - #"Count": ST.Int3DVector, - - "Point": ST.PhysicalPoint3D, - "Size": ST.PhysicalSize3D, - - #"Force": ST.PhysicalForce3D, - - "Freq": ST.PhysicalSize3D, - }, - } diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_colors.py b/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_colors.py index 3cdaed8..e47e980 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_colors.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_colors.py @@ -8,9 +8,8 @@ SOCKET_COLORS = { # Basic ST.Any: (0.8, 0.8, 0.8, 1.0), # Light Grey ST.Bool: (0.7, 0.7, 0.7, 1.0), # Medium Light Grey - ST.Text: (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 - ST.Secret: (0.0, 0.0, 0.0, 1.0), # Black # Number ST.IntegerNumber: (0.5, 0.5, 1.0, 1.0), # Light Blue @@ -39,8 +38,8 @@ SOCKET_COLORS = { ST.PhysicalSpeed: (0.8, 0.55, 0.35, 1.0), # Medium Light Orange ST.PhysicalAccelScalar: (0.7, 0.5, 0.3, 1.0), # Medium Orange ST.PhysicalForceScalar: (0.6, 0.45, 0.25, 1.0), # Medium Dark Orange - ST.PhysicalAccel3DVector: (0.7, 0.5, 0.3, 1.0), # Medium Orange - ST.PhysicalForce3DVector: (0.6, 0.45, 0.25, 1.0), # Medium Dark Orange + ST.PhysicalAccel3D: (0.7, 0.5, 0.3, 1.0), # Medium Orange + 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 @@ -48,10 +47,8 @@ SOCKET_COLORS = { 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.BlenderVolume: (0.4, 0.35, 0.7, 1.0), # Medium Dark 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 - ST.BlenderPreviewTarget: (0.5, 0.5, 0.75, 1.0), # Light Lavender # Maxwell ST.MaxwellSource: (1.0, 1.0, 0.5, 1.0), # Light Yellow @@ -59,8 +56,8 @@ SOCKET_COLORS = { ST.MaxwellMedium: (0.8, 0.8, 0.4, 1.0), # Medium Yellow ST.MaxwellMediumNonLinearity: (0.7, 0.7, 0.35, 1.0), # Medium Dark Yellow ST.MaxwellStructure: (0.6, 0.6, 0.3, 1.0), # Dark Yellow - ST.MaxwellBoundBox: (0.9, 0.8, 0.5, 1.0), # Light Gold - ST.MaxwellBoundFace: (0.8, 0.7, 0.45, 1.0), # Medium Light Gold + ST.MaxwellBoundConds: (0.9, 0.8, 0.5, 1.0), # Light Gold + ST.MaxwellBoundCond: (0.8, 0.7, 0.45, 1.0), # Medium Light Gold ST.MaxwellMonitor: (0.7, 0.6, 0.4, 1.0), # Medium Gold ST.MaxwellFDTDSim: (0.6, 0.5, 0.35, 1.0), # Medium Dark Gold ST.MaxwellSimGrid: (0.5, 0.4, 0.3, 1.0), # Dark Gold diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_from_bl_desc.py b/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_from_bl_desc.py new file mode 100644 index 0000000..0a68a5d --- /dev/null +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_from_bl_desc.py @@ -0,0 +1,78 @@ +from .socket_types import SocketType as ST + +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, + + ## 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, + + ## 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, + + ## Energetic + ("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, + + ## Luminal + ("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, +} diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_from_bl_direct.py b/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_from_bl_direct.py new file mode 100644 index 0000000..d48be3a --- /dev/null +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_from_bl_direct.py @@ -0,0 +1,36 @@ +from .socket_types import SocketType as ST + +BL_SOCKET_DIRECT_TYPE_MAP = { + ("NodeSocketString", 1): ST.String, + ("NodeSocketBool", 1): ST.Bool, + ("NodeSocketCollection", 1): ST.BlenderCollection, + ("NodeSocketImage", 1): ST.BlenderImage, + ("NodeSocketObject", 1): ST.BlenderObject, + + ("NodeSocketFloat", 1): ST.RealNumber, + #("NodeSocketFloatAngle", 1): ST.PhysicalAngle, + #("NodeSocketFloatDistance", 1): ST.PhysicalLength, + ("NodeSocketFloatFactor", 1): ST.RealNumber, + ("NodeSocketFloatPercentage", 1): ST.RealNumber, + #("NodeSocketFloatTime", 1): ST.PhysicalTime, + #("NodeSocketFloatTimeAbsolute", 1): ST.PhysicalTime, + + ("NodeSocketInt", 1): ST.IntegerNumber, + ("NodeSocketIntFactor", 1): ST.IntegerNumber, + ("NodeSocketIntPercentage", 1): ST.IntegerNumber, + ("NodeSocketIntUnsigned", 1): ST.IntegerNumber, + + ("NodeSocketRotation", 2): ST.PhysicalRot2D, + ("NodeSocketColor", 3): ST.Color, + ("NodeSocketVector", 2): ST.Real2DVector, + ("NodeSocketVector", 3): ST.Real3DVector, + #("NodeSocketVectorAcceleration", 2): ST.PhysicalAccel2D, + #("NodeSocketVectorAcceleration", 3): ST.PhysicalAccel3D, + #("NodeSocketVectorDirection", 2): ST.Real2DVectorDir, + #("NodeSocketVectorDirection", 3): ST.Real3DVectorDir, + ("NodeSocketVectorEuler", 2): ST.PhysicalRot2D, + ("NodeSocketVectorEuler", 3): ST.PhysicalRot3D, + #("NodeSocketVectorTranslation", 3): ST.PhysicalDisp3D, + #("NodeSocketVectorVelocity", 3): ST.PhysicalVel3D, + #("NodeSocketVectorXYZ", 3): ST.PhysicalPoint3D, +} diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_shapes.py b/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_shapes.py index b3fbdfc..56f578c 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_shapes.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_shapes.py @@ -4,9 +4,8 @@ SOCKET_SHAPES = { # Basic ST.Any: "CIRCLE", ST.Bool: "CIRCLE", - ST.Text: "SQUARE", + ST.String: "SQUARE", ST.FilePath: "SQUARE", - ST.Secret: "SQUARE", # Number ST.IntegerNumber: "CIRCLE", @@ -35,8 +34,8 @@ SOCKET_SHAPES = { ST.PhysicalSpeed: "CIRCLE", ST.PhysicalAccelScalar: "CIRCLE", ST.PhysicalForceScalar: "CIRCLE", - ST.PhysicalAccel3DVector: "SQUARE_DOT", - ST.PhysicalForce3DVector: "SQUARE_DOT", + ST.PhysicalAccel3D: "SQUARE_DOT", + ST.PhysicalForce3D: "SQUARE_DOT", ST.PhysicalPol: "DIAMOND", ST.PhysicalFreq: "CIRCLE", @@ -44,10 +43,8 @@ SOCKET_SHAPES = { ST.BlenderObject: "SQUARE", ST.BlenderCollection: "SQUARE", ST.BlenderImage: "DIAMOND", - ST.BlenderVolume: "DIAMOND", ST.BlenderGeoNodes: "DIAMOND", ST.BlenderText: "SQUARE", - ST.BlenderPreviewTarget: "SQUARE", # Maxwell ST.MaxwellSource: "CIRCLE", @@ -55,8 +52,8 @@ SOCKET_SHAPES = { ST.MaxwellMedium: "CIRCLE", ST.MaxwellMediumNonLinearity: "CIRCLE", ST.MaxwellStructure: "SQUARE", - ST.MaxwellBoundBox: "SQUARE", - ST.MaxwellBoundFace: "DIAMOND", + ST.MaxwellBoundConds: "SQUARE", + ST.MaxwellBoundCond: "DIAMOND", ST.MaxwellMonitor: "CIRCLE", ST.MaxwellFDTDSim: "SQUARE", ST.MaxwellSimGrid: "SQUARE", diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_types.py b/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_types.py index a88c3f2..0cdd0b6 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_types.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_types.py @@ -9,9 +9,9 @@ class SocketType(BlenderTypeEnum): # Base Any = enum.auto() Bool = enum.auto() - Text = enum.auto() + String = enum.auto() FilePath = enum.auto() - Secret = enum.auto() + Color = enum.auto() # Number IntegerNumber = enum.auto() @@ -21,69 +21,116 @@ class SocketType(BlenderTypeEnum): # Vector Real2DVector = enum.auto() + Real2DVectorDir = enum.auto() Complex2DVector = enum.auto() Real3DVector = enum.auto() + Real3DVectorDir = enum.auto() Complex3DVector = enum.auto() - # Physical - PhysicalUnitSystem = enum.auto() - PhysicalTime = enum.auto() - - PhysicalAngle = enum.auto() - - PhysicalLength = enum.auto() - PhysicalArea = enum.auto() - PhysicalVolume = enum.auto() - - PhysicalPoint2D = enum.auto() - PhysicalPoint3D = enum.auto() - - PhysicalSize2D = enum.auto() - PhysicalSize3D = enum.auto() - - PhysicalMass = enum.auto() - - PhysicalSpeed = enum.auto() - PhysicalAccelScalar = enum.auto() - PhysicalForceScalar = enum.auto() - PhysicalAccel3DVector = enum.auto() - PhysicalForce3DVector = enum.auto() - - PhysicalPol = enum.auto() - - PhysicalFreq = enum.auto() - # Blender BlenderObject = enum.auto() BlenderCollection = enum.auto() BlenderImage = enum.auto() - BlenderVolume = enum.auto() BlenderGeoNodes = enum.auto() BlenderText = enum.auto() - BlenderPreviewTarget = enum.auto() - # Maxwell - MaxwellSource = enum.auto() - MaxwellTemporalShape = enum.auto() + MaxwellBoundConds = enum.auto() + MaxwellBoundCond = enum.auto() MaxwellMedium = enum.auto() MaxwellMediumNonLinearity = enum.auto() + MaxwellSource = enum.auto() + MaxwellTemporalShape = enum.auto() + MaxwellStructure = enum.auto() - - MaxwellBoundBox = enum.auto() - MaxwellBoundFace = enum.auto() - MaxwellMonitor = enum.auto() MaxwellFDTDSim = enum.auto() + MaxwellSimDomain = enum.auto() MaxwellSimGrid = enum.auto() MaxwellSimGridAxis = enum.auto() - MaxwellSimDomain = enum.auto() # Tidy3D Tidy3DCloudTask = enum.auto() + + # Physical + PhysicalUnitSystem = enum.auto() + + PhysicalTime = enum.auto() + + PhysicalAngle = enum.auto() + PhysicalSolidAngle = enum.auto() + + PhysicalRot2D = enum.auto() + PhysicalRot3D = enum.auto() + + PhysicalFreq = enum.auto() + PhysicalAngFreq = enum.auto() + + ## Cartesian + PhysicalLength = enum.auto() + PhysicalArea = enum.auto() + PhysicalVolume = enum.auto() + + PhysicalDisp2D = enum.auto() + PhysicalDisp3D = enum.auto() + + PhysicalPoint1D = enum.auto() + PhysicalPoint2D = enum.auto() + PhysicalPoint3D = enum.auto() + + PhysicalSize2D = enum.auto() + PhysicalSize3D = enum.auto() + + ## Mechanical + PhysicalMass = enum.auto() + + PhysicalSpeed = enum.auto() + PhysicalVel2D = enum.auto() + PhysicalVel3D = enum.auto() + PhysicalAccelScalar = enum.auto() + PhysicalAccel2D = enum.auto() + PhysicalAccel3D = enum.auto() + PhysicalForceScalar = enum.auto() + PhysicalForce2D = enum.auto() + PhysicalForce3D = enum.auto() + PhysicalPressure = enum.auto() + + ## Energetic + PhysicalEnergy = enum.auto() + PhysicalPower = enum.auto() + PhysicalTemp = enum.auto() + + ## Electrodynamical + PhysicalCurr = enum.auto() + PhysicalCurrDens2D = enum.auto() + PhysicalCurrDens3D = enum.auto() + + PhysicalCharge = enum.auto() + PhysicalVoltage = enum.auto() + PhysicalCapacitance = enum.auto() + PhysicalResistance = enum.auto() + PhysicalConductance = enum.auto() + + PhysicalMagFlux = enum.auto() + PhysicalMagFluxDens = enum.auto() + PhysicalInductance = enum.auto() + + PhysicalEField2D = enum.auto() + PhysicalEField3D = enum.auto() + PhysicalHField2D = enum.auto() + PhysicalHField3D = enum.auto() + + ## Luminal + PhysicalLumIntensity = enum.auto() + PhysicalLumFlux = enum.auto() + PhysicalIlluminance = enum.auto() + + ## Optical + PhysicalPolJones = enum.auto() + PhysicalPol = enum.auto() diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_units.py b/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_units.py index 8236d66..65b664a 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_units.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_units.py @@ -194,7 +194,7 @@ SOCKET_UNITS = { "NEWT": spu.newton, }, }, - ST.PhysicalAccel3DVector: { + ST.PhysicalAccel3D: { "default": "UM_S_SQ", "values": { "PM_S_SQ": spu.picometer / spu.second**2, @@ -206,7 +206,7 @@ SOCKET_UNITS = { "FT_S_SQ": spu.feet / spu.second**2, }, }, - ST.PhysicalForce3DVector: { + ST.PhysicalForce3D: { "default": "UNEWT", "values": { "KG_M_S_SQ": spu.kg * spu.m/spu.second**2, diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/managed_objs/managed_bl_image.py b/blender_maxwell/node_trees/maxwell_sim_nodes/managed_objs/managed_bl_image.py index 72b4f48..ec36aa8 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/managed_objs/managed_bl_image.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/managed_objs/managed_bl_image.py @@ -27,15 +27,30 @@ class ManagedBLImage(ct.schemas.ManagedObj): @name.setter def name(self, value: str): - ## TODO: Check that blender doesn't have any other images by the same name. - if (bl_image := bpy.data.images.get(self.name)): - bl_image.name = value + # Image Doesn't Exist + if not (bl_image := bpy.data.images.get(self._bl_image_name)): + # ...AND Desired Image Name is Not Taken + if not bpy.data.objects.get(value): + self._bl_image_name = value + return + + # ...AND Desired Image Name is Taken + else: + msg = f"Desired name {value} for BL image is taken" + raise ValueError(msg) - self._bl_image_name = value + # Object DOES Exist + bl_image.name = value + self._bl_image_name = bl_image.name + ## - When name exists, Blender adds .### to prevent overlap. + ## - `set_name` is allowed to change the name; nodes account for this. def free(self): - if (bl_image := bpy.data.images.get(self.name)): - bpy.data.images.remove(bl_image) + if not (bl_image := bpy.data.images.get(self.name)): + msg = "Can't free BL image that doesn't exist" + raise ValueError(msg) + + bpy.data.images.remove(bl_image) #################### # - Managed Object Management diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/managed_objs/managed_bl_object.py b/blender_maxwell/node_trees/maxwell_sim_nodes/managed_objs/managed_bl_object.py index be27086..35c234e 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/managed_objs/managed_bl_object.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/managed_objs/managed_bl_object.py @@ -13,75 +13,146 @@ import bmesh from .. import contracts as ct +ModifierType = typx.Literal["NODES", "ARRAY"] +MODIFIER_NAMES = { + "NODES": "BLMaxwell_GeoNodes", + "ARRAY": "BLMaxwell_Array", +} +MANAGED_COLLECTION_NAME = "BLMaxwell" +PREVIEW_COLLECTION_NAME = "BLMaxwell Visible" + +def bl_collection( + collection_name: str, view_layer_exclude: bool +) -> bpy.types.Collection: + # Init the "Managed Collection" + # Ensure Collection exists (and is in the Scene collection) + if collection_name not in bpy.data.collections: + collection = bpy.data.collections.new(collection_name) + bpy.context.scene.collection.children.link(collection) + else: + collection = bpy.data.collections[collection_name] + + ## Ensure synced View Layer exclusion + if (layer_collection := bpy.context.view_layer.layer_collection.children[ + collection_name + ]).exclude != view_layer_exclude: + layer_collection.exclude = view_layer_exclude + + return collection + class ManagedBLObject(ct.schemas.ManagedObj): managed_obj_type = ct.ManagedObjType.ManagedBLObject _bl_object_name: str def __init__(self, name: str): - ## TODO: Check that blender doesn't have any other objects by the same name. self._bl_object_name = name # Object Name @property - def bl_object_name(self): + def name(self): return self._bl_object_name - @bl_object_name.setter - def set_bl_object_name(self, value: str): - ## TODO: Check that blender doesn't have any other objects by the same name. - if (bl_object := bpy.data.objects.get(self.bl_object_name)): - bl_object.name = value + @name.setter + def set_name(self, value: str) -> None: + # Object Doesn't Exist + if not (bl_object := bpy.data.objects.get(self._bl_object_name)): + # ...AND Desired Object Name is Not Taken + if not bpy.data.objects.get(value): + self._bl_object_name = value + return + + # ...AND Desired Object Name is Taken + else: + msg = f"Desired name {value} for BL object is taken" + raise ValueError(msg) - self._bl_object_name = value + # Object DOES Exist + bl_object.name = value + self._bl_object_name = bl_object.name + ## - When name exists, Blender adds .### to prevent overlap. + ## - `set_name` is allowed to change the name; nodes account for this. # Object Datablock Name @property def bl_mesh_name(self): - return self.bl_object_name + "Mesh" + return self.name @property def bl_volume_name(self): - return self.bl_object_name + "Volume" + return self.name # Deallocation def free(self): - if (bl_object := bpy.data.objects.get(self.bl_object_name)): - # Delete the Underlying Datablock - if bl_object.type == "MESH": - bpy.data.meshes.remove(bl_object.data) - 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" - raise ValueError(msg) + if not (bl_object := bpy.data.objects.get(self.name)): + return ## Nothing to do + + # Delete the Underlying Datablock + ## This automatically deletes the object too + if bl_object.type == "MESH": + bpy.data.meshes.remove(bl_object.data) + elif bl_object.type == "EMPTY": + bpy.data.meshes.remove(bl_object.data) + 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" + raise ValueError(msg) #################### # - Actions #################### - def trigger_action( - self, - action: typx.Literal["report", "enable_previews"], - ): - if action == "report": - pass ## TODO: Cache invalidation. + def show_preview( + self, + kind: typx.Literal["MESH", "EMPTY", "VOLUME"], + empty_display_type: typx.Literal[ + "PLAIN_AXES", "ARROWS", "SINGLE_ARROW", "CIRCLE", "CUBE", + "SPHERE", "CONE", "IMAGE", + ] | None = None, + ) -> None: + """Moves the managed Blender object to the preview collection. - if action == "enable_previews": - - pass ## Image "previews" don't need enabling. + If it's already included, do nothing. + """ + bl_object = self.bl_object(kind) + if bl_object.name not in (preview_collection := bl_collection( + PREVIEW_COLLECTION_NAME, view_layer_exclude=False + )).objects: + preview_collection.objects.link(bl_object) + + 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"], + ) -> None: + """Removes the managed Blender object from the preview collection. + + If it's already removed, do nothing. + """ + bl_object = self.bl_object(kind) + if bl_object.name not in (preview_collection := bl_collection( + PREVIEW_COLLECTION_NAME, view_layer_exclude=False + )).objects: + preview_collection.objects.unlink(bl_object) def bl_select(self) -> None: """Selects the managed Blender object globally, causing it to be ex. outlined in the 3D viewport. """ + if not (bl_object := bpy.data.objects.get(self.name)): + msg = "Managed BLObject does not exist" + raise ValueError(msg) + bpy.ops.object.select_all(action='DESELECT') - bpy.data.objects['Suzanne'].select_set(True) + bl_object.select_set(True) #################### # - Managed Object Management #################### def bl_object( self, - kind: typx.Literal["MESH", "VOLUME"], + kind: typx.Literal["MESH", "EMPTY", "VOLUME"], ): """Returns the managed blender object. @@ -90,27 +161,32 @@ class ManagedBLObject(ct.schemas.ManagedObj): """ # Remove Object (if mismatch) if ( - (bl_object := bpy.data.images.get(self.bl_object_name)) + (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.images.get(self.bl_object_name)): - if bl_object.type == "MESH": + if not (bl_object := bpy.data.objects.get(self.name)): + if kind == "MESH": bl_data = bpy.data.meshes.new(self.bl_mesh_name) - elif bl_object.type == "VOLUME": + elif kind == "EMPTY": + bl_data = None + elif kind == "VOLUME": raise NotImplementedError else: msg = f"Requested `bl_object` type {bl_object.type} is not valid" raise ValueError(msg) - bl_object = bpy.data.objects.new(self.bl_object_name, bl_data) + bl_object = bpy.data.objects.new(self.name, bl_data) + bl_collection( + MANAGED_COLLECTION_NAME, view_layer_exclude=True + ).objects.link(bl_object) return bl_object #################### - # - Data Properties + # - Mesh Data Properties #################### @property def raw_mesh(self) -> bpy.types.Mesh: @@ -119,7 +195,7 @@ class ManagedBLObject(ct.schemas.ManagedObj): Raises an error if the object has no mesh data. """ if ( - (bl_object := bpy.data.objects.get(self.bl_object_name)) + (bl_object := bpy.data.objects.get(self.name)) and bl_object.type == "MESH" ): return bl_object.data @@ -128,13 +204,13 @@ class ManagedBLObject(ct.schemas.ManagedObj): raise ValueError(msg) @contextlib.contextmanager - def as_bmesh( + def mesh_as_bmesh( self, evaluate: bool = True, triangulate: bool = False, ) -> bpy.types.Mesh: if ( - (bl_object := bpy.data.objects.get(self.bl_object_name)) + (bl_object := bpy.data.objects.get(self.name)) and bl_object.type == "MESH" ): bmesh_mesh = None @@ -155,15 +231,22 @@ class ManagedBLObject(ct.schemas.ManagedObj): finally: if bmesh_mesh: bmesh_mesh.free() + + else: + msg = f"Requested BMesh from `bl_object` of type {bl_object.type}" + raise ValueError(msg) - msg = f"Requested BMesh from `bl_object` of type {bl_object.type}" - raise ValueError(msg) - - @functools.cached_property - def as_arrays(self) -> dict: + @property + def mesh_as_arrays(self) -> dict: + ## TODO: Cached + + # Ensure Updated Geometry + bpy.context.view_layer.update() + ## TODO: Must we? + # Compute Evaluted + Triangulated Mesh _mesh = bpy.data.meshes.new(name="TemporaryMesh") - with self.as_bmesh(evaluate=True, triangulate=True) as bmesh_mesh: + with self.mesh_as_bmesh(evaluate=True, triangulate=True) as bmesh_mesh: bmesh_mesh.to_mesh(_mesh) # Optimized Vertex Copy @@ -186,6 +269,113 @@ class ManagedBLObject(ct.schemas.ManagedObj): "faces": faces, } + #################### + # - Modifier Methods + #################### + def bl_modifier( + self, + modifier_type: ModifierType, + ): + """Creates a new modifier for the current `bl_object`. + + For all Blender modifier type names, see: + """ + if not (bl_object := bpy.data.objects.get(self.name)): + msg = "Can't add modifier to BL object that doesn't exist" + raise ValueError(msg) + + # (Create and) Return Modifier + bl_modifier_name = MODIFIER_NAMES[modifier_type] + if bl_modifier_name not in bl_object.modifiers: + return bl_object.modifiers.new( + name=bl_modifier_name, + type=modifier_type, + ) + return bl_object.modifiers[bl_modifier_name] + + def modifier_attrs(self, modifier_type: ModifierType) -> dict: + """Based on the modifier type, retrieve a representative dictionary of modifier attributes. + The attributes can then easily be set using `setattr`. + """ + bl_modifier = self.bl_modifier(modifier_type) + + if modifier_type == "NODES": + return { + "node_group": bl_modifier.node_group, + } + elif modifier_type == "ARRAY": + raise NotImplementedError + + def s_modifier_attrs( + self, + modifier_type: ModifierType, + modifier_attrs: dict, + ): + bl_modifier = self.bl_modifier(modifier_type) + + if modifier_type == "NODES": + if bl_modifier.node_group != modifier_attrs["node_group"]: + bl_modifier.node_group = modifier_attrs["node_group"] + elif modifier_type == "ARRAY": + raise NotImplementedError + + #################### + # - GeoNodes Modifier + #################### + def sync_geonodes_modifier( + self, + geonodes_node_group, + geonodes_identifier_to_value: dict, + ): + """Push the given GeoNodes Interface values to a GeoNodes modifier attached to a managed MESH object. + + The values must be compatible with the `default_value`s of the interface sockets. + + If there is no object, it is created. + If the object isn't a MESH object, it is made so. + If the GeoNodes modifier doesn't exist, it is created. + If the GeoNodes node group doesn't match, it is changed. + Only differing interface values are actually changed. + """ + bl_object = self.bl_object("MESH") + + # Get (/make) a GeoModes Modifier + bl_modifier = self.bl_modifier("NODES") + + # Set GeoNodes Modifier Attributes (specifically, the '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() + ): + 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: + continue + + # Quickly Determine int/float Mismatch + if isinstance( + bl_modifier[interface_identifier], + float, + ) and isinstance(value, int): + value = float(value) + + bl_modifier[interface_identifier] = value + + modifier_altered = True + + # Update DepGraph (if anything changed) + if modifier_altered: + bl_object.data.update() + + #@property #def volume(self) -> bpy.types.Volume: # """Returns the object's volume data. diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/node_tree.py b/blender_maxwell/node_trees/maxwell_sim_nodes/node_tree.py index 7527d50..e5eb923 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/node_tree.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/node_tree.py @@ -61,17 +61,6 @@ class MaxwellSimTree(bpy.types.NodeTree): bl_label = "Maxwell Sim Editor" bl_icon = ct.Icon.SimNodeEditor.value - managed_collection: bpy.props.PointerProperty( - name="Managed Collection", - description="Collection of Blender objects managed by this tree", - type=bpy.types.Collection, - ) - preview_collection: bpy.props.PointerProperty( - name="Preview Collection", - description="Collection of Blender objects that will be previewed", - type=bpy.types.Collection, - ) - #################### # - Lock Methods #################### @@ -81,6 +70,18 @@ class MaxwellSimTree(bpy.types.NodeTree): for bl_socket in [*node.inputs, *node.outputs]: bl_socket.locked = False + #################### + # - Init Methods + #################### + def on_load(self): + """Run by Blender when loading the NodeSimTree, ex. on file load, on creation, etc. . + + It's a bit of a "fake" function - in practicality, it's triggered on the first update() function. + """ + ## TODO: Consider tying this to an "on_load" handler + self._node_link_cache = NodeLinkCache(self) + + #################### # - Update Methods #################### @@ -105,7 +106,7 @@ class MaxwellSimTree(bpy.types.NodeTree): Updates an internal node link cache, then updates sockets that just lost/gained an input link. """ if not hasattr(self, "_node_link_cache"): - self._node_link_cache = NodeLinkCache(self) + self.on_load() ## We presume update() is run before the first link is altered. ## - Else, the first link of the session will not update caches. ## - We remain slightly unsure of the semantics. diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/__init__.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/__init__.py index 40fd4ed..68312cb 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/__init__.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/__init__.py @@ -1,4 +1,4 @@ -from . import kitchen_sink +#from . import kitchen_sink from . import inputs from . import outputs @@ -11,7 +11,7 @@ from . import simulations #from . import utilities BL_REGISTER = [ - *kitchen_sink.BL_REGISTER, + #*kitchen_sink.BL_REGISTER, *inputs.BL_REGISTER, *outputs.BL_REGISTER, *sources.BL_REGISTER, @@ -23,7 +23,7 @@ BL_REGISTER = [ # *utilities.BL_REGISTER, ] BL_NODES = { - **kitchen_sink.BL_NODES, + #**kitchen_sink.BL_NODES, **inputs.BL_NODES, **outputs.BL_NODES, **sources.BL_NODES, diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/base.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/base.py index d29bcb4..f3b5e2c 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/base.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/base.py @@ -67,7 +67,7 @@ class MaxwellSimNode(bpy.types.Node): 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)) + update=(lambda self, context: self.sync_sim_node_name(context)) ) # Setup Locked Property for Node @@ -151,7 +151,7 @@ class MaxwellSimNode(bpy.types.Node): ) ], default=socket_set_names[0], - update=(lambda self, _: self._sync_sockets()), + update=(lambda self, _: self.sync_sockets()), ) # Setup Preset Dropdown @@ -172,24 +172,27 @@ class MaxwellSimNode(bpy.types.Node): ], default=list(cls.presets.keys())[0], update=lambda self, context: ( - self._sync_active_preset()() + self.sync_active_preset()() ), ) #################### # - Generic Properties #################### - def _sync_sim_node_name(self, context): - for managed_obj in self.managed_objs.values(): - managed_obj.name = self.sim_node_name + def sync_sim_node_name(self, context): + if (mobjs := CACHE[self.instance_id].get("managed_objs")) is None: + return + + for mobj_id, mobj in mobjs.items(): + # Retrieve Managed Obj Definition + mobj_def = self.managed_obj_defs[mobj_id] - # Recurse Until Equal - if managed_obj.name != self.sim_node_name: - self.sim_node_name = managed_obj.name - ## ManagedObj is allowed to alter the name when setting it. - ## - This will happen whenever the name is taken. - ## - If altered, set the 'sim_node_name' to the altered name. - ## - This will cause recursion, but only once. + # Set Managed Obj Name + mobj.name = mobj_def.name_prefix + self.sim_node_name + ## ManagedObj is allowed to alter the name when setting it. + ## - This will happen whenever the name is taken. + ## - If altered, set the 'sim_node_name' to the altered name. + ## - This will cause recursion, but only once. #################### # - Managed Object Properties @@ -205,7 +208,7 @@ class MaxwellSimNode(bpy.types.Node): ## - ManagedObjects MUST the same object by name. ## - We sync our 'sim_node_name' with all managed objects. ## - (There is also a class-defined 'name_prefix' to differentiate) - ## - See the 'sim_node_name' w/its _sync function. + ## - See the 'sim_node_name' w/its sync function. if CACHE[self.instance_id].get("managed_objs") is None: # Initialize the Managed Object Instance Cache CACHE[self.instance_id]["managed_objs"] = {} @@ -304,7 +307,7 @@ class MaxwellSimNode(bpy.types.Node): for model in deser.values() ], "models": [ - dict(model) + model.model_dump() for model in deser.values() if isinstance(model, pyd.BaseModel) ], @@ -336,7 +339,7 @@ class MaxwellSimNode(bpy.types.Node): self.ser_loose_input_sockets = self._ser_loose_sockets(value) # Synchronize Sockets - self._sync_sockets() + self.sync_sockets() ## TODO: Perhaps re-init() all loose sockets anyway? @loose_output_sockets.setter @@ -346,7 +349,7 @@ class MaxwellSimNode(bpy.types.Node): self.ser_loose_output_sockets = self._ser_loose_sockets(value) # Synchronize Sockets - self._sync_sockets() + self.sync_sockets() ## TODO: Perhaps re-init() all loose sockets anyway? #################### @@ -402,7 +405,7 @@ class MaxwellSimNode(bpy.types.Node): for socket_name, socket_def in created_sockets.items(): socket_def.init(bl_sockets[socket_name]) - def _sync_sockets(self) -> None: + def sync_sockets(self) -> None: """Synchronize the node's sockets with the active sockets. - Any non-existing active socket will be added and initialized. @@ -418,7 +421,7 @@ class MaxwellSimNode(bpy.types.Node): #################### # - Preset Management #################### - def _sync_active_preset(self) -> None: + def sync_active_preset(self) -> None: """Applies the active preset by overwriting the value of preset-defined input sockets. """ @@ -555,7 +558,11 @@ class MaxwellSimNode(bpy.types.Node): and socket_name == method._extra_data.get("changed_socket") ) or ( prop_name - and socket_name == method._extra_data.get("changed_prop") + and prop_name == method._extra_data.get("changed_prop") + ) or ( + socket_name + and method._extra_data.get("changed_loose_input") + and socket_name in self.loose_input_sockets ): method(self) @@ -626,11 +633,11 @@ class MaxwellSimNode(bpy.types.Node): ## Only shown in draw_buttons if 'self.use_sim_node_name' # Initialize Sockets - self._sync_sockets() + self.sync_sockets() # Apply Default Preset if self.active_preset: - self._sync_active_preset() + self.sync_active_preset() def update(self) -> None: pass @@ -672,6 +679,8 @@ def chain_event_decorator( kind: ct.DataFlowKind = ct.DataFlowKind.Value, input_sockets: set[str] = set(), ## For now, presume output_sockets: set[str] = set(), ## For now, presume + loose_input_sockets: bool = False, + loose_output_sockets: bool = False, props: set[str] = set(), managed_objs: set[str] = set(), @@ -715,6 +724,24 @@ def chain_event_decorator( } method_kw_args |= dict(output_sockets=_output_sockets) + ## Add Loose Sockets + if loose_input_sockets: + _loose_input_sockets = { + input_socket_name: node._compute_input(input_socket_name, kind) + for input_socket_name in node.loose_input_sockets + } + method_kw_args |= dict( + loose_input_sockets=_loose_input_sockets + ) + if loose_output_sockets: + _loose_output_sockets = { + output_socket_name: node.compute_output(output_socket_name, kind) + for output_socket_name in node.loose_output_sockets + } + method_kw_args |= dict( + loose_output_sockets=_loose_output_sockets + ) + ## Add Props if props: _props = { @@ -808,17 +835,25 @@ def computes_output_socket( def on_value_changed( socket_name: ct.SocketName | None = None, prop_name: 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 socket_name is not None and prop_name is not None: - msg = "Either socket_name or prop_name, not both" + if sum([ + int(socket_name is not None), + int(prop_name is not None), + int(any_loose_input_socket), + ]) > 1: + msg = "Define only one of socket_name, prop_name or any_loose_input_socket" raise ValueError(msg) req_params = {"self"} | ( {"input_sockets"} if input_sockets else set() + ) | ( + {"loose_input_sockets"} if any_loose_input_socket else set() ) | ( {"props"} if props else set() ) | ( @@ -827,13 +862,14 @@ def on_value_changed( return chain_event_decorator( callback_type="on_value_changed", - index_by=(socket_name, prop_name), extra_data={ "changed_socket": socket_name, "changed_prop": prop_name, + "changed_loose_input": any_loose_input_socket, }, kind=kind, input_sockets=input_sockets, + loose_input_sockets=any_loose_input_socket, props=props, managed_objs=managed_objs, req_params=req_params, @@ -841,8 +877,8 @@ def on_value_changed( def on_show_preview( kind: ct.DataFlowKind = ct.DataFlowKind.Value, - input_sockets: set[str] = set(), ## For now, presume - output_sockets: set[str] = set(), ## For now, presume + input_sockets: set[str] = set(), ## For now, presume only same kind + output_sockets: set[str] = set(), ## For now, presume only same kind props: set[str] = set(), managed_objs: set[str] = set(), ): diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/bounds/bound_box.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/bounds/bound_box.py index a715dac..bbfbed1 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/bounds/bound_box.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/bounds/bound_box.py @@ -2,12 +2,12 @@ import tidy3d as td import sympy as sp import sympy.physics.units as spu -from ... import contracts +from ... import contracts as ct from ... import sockets from .. import base -class BoundBoxNode(base.MaxwellSimTreeNode): - node_type = contracts.NodeType.BoundBox +class BoundCondsNode(base.MaxwellSimNode): + node_type = ct.NodeType.BoundConds bl_label = "Bound Box" #bl_icon = ... @@ -15,42 +15,31 @@ class BoundBoxNode(base.MaxwellSimTreeNode): # - Sockets #################### input_sockets = { - "x_pos": sockets.MaxwellBoundFaceSocketDef( - label="+x", - ), - "x_neg": sockets.MaxwellBoundFaceSocketDef( - label="-x", - ), - "y_pos": sockets.MaxwellBoundFaceSocketDef( - label="+y", - ), - "y_neg": sockets.MaxwellBoundFaceSocketDef( - label="-y", - ), - "z_pos": sockets.MaxwellBoundFaceSocketDef( - label="+z", - ), - "z_neg": sockets.MaxwellBoundFaceSocketDef( - label="-z", - ), + "+X": sockets.MaxwellBoundCondSocketDef(), + "-X": sockets.MaxwellBoundCondSocketDef(), + "+Y": sockets.MaxwellBoundCondSocketDef(), + "-Y": sockets.MaxwellBoundCondSocketDef(), + "+Z": sockets.MaxwellBoundCondSocketDef(), + "-Z": sockets.MaxwellBoundCondSocketDef(), } output_sockets = { - "bound": sockets.MaxwellBoundBoxSocketDef( - label="Bound", - ), + "BCs": sockets.MaxwellBoundCondsSocketDef(), } #################### # - Output Socket Computation #################### - @base.computes_output_socket("bound") - def compute_simulation(self: contracts.NodeTypeProtocol) -> td.BoundarySpec: - x_pos = self.compute_input("x_pos") - x_neg = self.compute_input("x_neg") - y_pos = self.compute_input("x_pos") - y_neg = self.compute_input("x_neg") - z_pos = self.compute_input("x_pos") - z_neg = self.compute_input("x_neg") + @base.computes_output_socket( + "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"] return td.BoundarySpec( x=td.Boundary( @@ -73,10 +62,10 @@ class BoundBoxNode(base.MaxwellSimTreeNode): # - Blender Registration #################### BL_REGISTER = [ - BoundBoxNode, + BoundCondsNode, ] BL_NODES = { - contracts.NodeType.BoundBox: ( - contracts.NodeCategory.MAXWELLSIM_BOUNDS + ct.NodeType.BoundConds: ( + ct.NodeCategory.MAXWELLSIM_BOUNDS ) } diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/__init__.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/__init__.py index e8e2741..383022d 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/__init__.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/__init__.py @@ -1,23 +1,26 @@ +from . import wave_constant #from . import unit_system -from . import importers from . import constants -#from . import lists -#from . import scene + +from . import web_importers +#from . import file_importers BL_REGISTER = [ - *importers.BL_REGISTER, + *wave_constant.BL_REGISTER, # *unit_system.BL_REGISTER, -# -# *scene.BL_REGISTER, + *constants.BL_REGISTER, - #*lists.BL_REGISTER, + + *web_importers.BL_REGISTER, +# *file_importers.BL_REGISTER, ] BL_NODES = { - **importers.BL_NODES, + **wave_constant.BL_NODES, # **unit_system.BL_NODES, -# -# **scene.BL_NODES, + **constants.BL_NODES, -# **lists.BL_NODES, + + **web_importers.BL_NODES, +# *file_importers.BL_REGISTER, } diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/__init__.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/__init__.py index 1ef23e8..c731c46 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/__init__.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/__init__.py @@ -1,23 +1,17 @@ -from . import wave_constant #from . import scientific_constant -# -#from . import number_constant +from . import number_constant #from . import physical_constant -#from . import blender_constant +from . import blender_constant BL_REGISTER = [ - *wave_constant.BL_REGISTER, # *scientific_constant.BL_REGISTER, -# -# *number_constant.BL_REGISTER, + *number_constant.BL_REGISTER, # *physical_constant.BL_REGISTER, -# *blender_constant.BL_REGISTER, + *blender_constant.BL_REGISTER, ] BL_NODES = { - **wave_constant.BL_NODES, # **scientific_constant.BL_NODES, -# -# **number_constant.BL_NODES, + **number_constant.BL_NODES, # **physical_constant.BL_NODES, -# **blender_constant.BL_NODES, + **blender_constant.BL_NODES, } diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/blender_constant.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/blender_constant.py index 2a44773..e6bdd10 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/blender_constant.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/blender_constant.py @@ -1,57 +1,41 @@ import typing as typ -from .... import contracts +from .... import contracts as ct from .... import sockets from ... import base -class BlenderConstantNode(base.MaxwellSimTreeNode): - node_type = contracts.NodeType.BlenderConstant - +class BlenderConstantNode(base.MaxwellSimNode): + node_type = ct.NodeType.BlenderConstant bl_label = "Blender Constant" - #bl_icon = constants.ICON_SIM_INPUT - input_sockets = {} input_socket_sets = { - "object": { - "value": sockets.BlenderObjectSocketDef( - label="Object", - ), + "Object": { + "Value": sockets.BlenderObjectSocketDef(), }, - "collection": { - "value": sockets.BlenderCollectionSocketDef( - label="Collection", - ), + "Collection": { + "Value": sockets.BlenderCollectionSocketDef(), }, - "image": { - "value": sockets.BlenderImageSocketDef( - label="Image", - ), + "Text": { + "Value": sockets.BlenderTextSocketDef(), }, - "volume": { - "value": sockets.BlenderVolumeSocketDef( - label="Volume", - ), + "Image": { + "Value": sockets.BlenderImageSocketDef(), }, - "text": { - "value": sockets.BlenderTextSocketDef( - label="Text", - ), - }, - "geonodes": { - "value": sockets.BlenderGeoNodesSocketDef( - label="GeoNodes", - ), + "GeoNode Tree": { + "Value": sockets.BlenderGeoNodesSocketDef(), }, } - output_sockets = {} output_socket_sets = input_socket_sets #################### # - Callbacks #################### - @base.computes_output_socket("value") - def compute_value(self: contracts.NodeTypeProtocol) -> typ.Any: - return self.compute_input("value") + @base.computes_output_socket( + "Value", + input_sockets={"Value"} + ) + def compute_value(self, input_sockets) -> typ.Any: + return input_sockets["Value"] @@ -62,7 +46,7 @@ BL_REGISTER = [ BlenderConstantNode, ] BL_NODES = { - contracts.NodeType.BlenderConstant: ( - contracts.NodeCategory.MAXWELLSIM_INPUTS_CONSTANTS + ct.NodeType.BlenderConstant: ( + ct.NodeCategory.MAXWELLSIM_INPUTS_CONSTANTS ) } diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/number_constant.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/number_constant.py index 7f5d904..1bff953 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/number_constant.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/number_constant.py @@ -1,43 +1,41 @@ +import typing as typ + import bpy import sympy as sp -from .... import contracts +from .... import contracts as ct from .... import sockets from ... import base -class NumberConstantNode(base.MaxwellSimTreeNode): - node_type = contracts.NodeType.NumberConstant - +class NumberConstantNode(base.MaxwellSimNode): + node_type = ct.NodeType.NumberConstant bl_label = "Numerical Constant" - #bl_icon = constants.ICON_SIM_INPUT - input_sockets = {} input_socket_sets = { - "integer": { - "value": sockets.IntegerNumberSocketDef( - label="Integer", - ), + "Integer": { + "Value": sockets.IntegerNumberSocketDef(), }, - "real": { - "value": sockets.RealNumberSocketDef( - label="Real", - ), + "Rational": { + "Value": sockets.RationalNumberSocketDef(), }, - "complex": { - "value": sockets.ComplexNumberSocketDef( - label="Complex", - ), + "Real": { + "Value": sockets.RealNumberSocketDef(), + }, + "Complex": { + "Value": sockets.ComplexNumberSocketDef(), }, } - output_sockets = {} output_socket_sets = input_socket_sets #################### # - Callbacks #################### - @base.computes_output_socket("value") - def compute_value(self: contracts.NodeTypeProtocol) -> sp.Expr: - return self.compute_input("value") + @base.computes_output_socket( + "Value", + input_sockets={"Value"} + ) + def compute_value(self, input_sockets) -> typ.Any: + return input_sockets["Value"] @@ -48,7 +46,7 @@ BL_REGISTER = [ NumberConstantNode, ] BL_NODES = { - contracts.NodeType.NumberConstant: ( - contracts.NodeCategory.MAXWELLSIM_INPUTS_CONSTANTS + ct.NodeType.NumberConstant: ( + ct.NodeCategory.MAXWELLSIM_INPUTS_CONSTANTS ) } diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/lists/__init__.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/lists/__init__.py deleted file mode 100644 index 5104639..0000000 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/lists/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -from . import number_list -from . import physical_list - -BL_REGISTER = [ - *number_list.BL_REGISTER, - *physical_list.BL_REGISTER, -] -BL_NODES = { - **number_list.BL_NODES, - **physical_list.BL_NODES, -} diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/lists/number_list.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/lists/number_list.py deleted file mode 100644 index 41fac16..0000000 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/lists/number_list.py +++ /dev/null @@ -1,5 +0,0 @@ -#################### -# - Blender Registration -#################### -BL_REGISTER = [] -BL_NODES = {} diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/lists/physical_list.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/lists/physical_list.py deleted file mode 100644 index 41fac16..0000000 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/lists/physical_list.py +++ /dev/null @@ -1,5 +0,0 @@ -#################### -# - Blender Registration -#################### -BL_REGISTER = [] -BL_NODES = {} diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/scene/__init__.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/scene/__init__.py deleted file mode 100644 index 89db325..0000000 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/scene/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -from . import time -from . import unit_system - -BL_REGISTER = [ - *time.BL_REGISTER, - *unit_system.BL_REGISTER, -] -BL_NODES = { - **time.BL_NODES, - **unit_system.BL_NODES, -} diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/scene/time.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/scene/time.py deleted file mode 100644 index 41fac16..0000000 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/scene/time.py +++ /dev/null @@ -1,5 +0,0 @@ -#################### -# - Blender Registration -#################### -BL_REGISTER = [] -BL_NODES = {} diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/scene/unit_system.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/scene/unit_system.py deleted file mode 100644 index 41fac16..0000000 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/scene/unit_system.py +++ /dev/null @@ -1,5 +0,0 @@ -#################### -# - Blender Registration -#################### -BL_REGISTER = [] -BL_NODES = {} diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/unit_system.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/unit_system.py index 95417a8..c26d1e9 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/unit_system.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/unit_system.py @@ -1,33 +1,32 @@ import bpy import sympy as sp -from ... import contracts +from ... import contracts as ct from ... import sockets from .. import base -class PhysicalUnitSystemNode(base.MaxwellSimTreeNode): - node_type = contracts.NodeType.UnitSystem - - bl_label = "Unit System Constant" +class PhysicalUnitSystemNode(base.MaxwellSimNode): + node_type = ct.NodeType.UnitSystem + bl_label = "Unit System" input_sockets = { - "unit_system": sockets.PhysicalUnitSystemSocketDef( - label="Unit System", + "Unit System": sockets.PhysicalUnitSystemSocketDef( show_by_default=True, ), } output_sockets = { - "unit_system": sockets.PhysicalUnitSystemSocketDef( - label="Unit System", - ), + "Unit System": sockets.PhysicalUnitSystemSocketDef(), } #################### # - Callbacks #################### - @base.computes_output_socket("unit_system") - def compute_value(self: contracts.NodeTypeProtocol) -> sp.Expr: - return self.compute_input("unit_system") + @base.computes_output_socket( + "Unit System", + input_sockets = {"Unit System"}, + ) + def compute_value(self, input_sockets) -> dict: + return input_sockets["Unit System"] @@ -38,7 +37,7 @@ BL_REGISTER = [ PhysicalUnitSystemNode, ] BL_NODES = { - contracts.NodeType.UnitSystem: ( - contracts.NodeCategory.MAXWELLSIM_INPUTS + ct.NodeType.UnitSystem: ( + ct.NodeCategory.MAXWELLSIM_INPUTS ) } diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/wave_constant.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/wave_constant.py similarity index 95% rename from blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/wave_constant.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/wave_constant.py index 2651f92..203e563 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/wave_constant.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/wave_constant.py @@ -3,9 +3,9 @@ import sympy as sp import sympy.physics.units as spu import scipy as sc -from .... import contracts as ct -from .... import sockets -from ... import base +from ... import contracts as ct +from ... import sockets +from .. import base VAC_SPEED_OF_LIGHT = ( sc.constants.speed_of_light diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/importers/__init__.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/web_importers/__init__.py similarity index 100% rename from blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/importers/__init__.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/web_importers/__init__.py diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/importers/tidy_3d_web_importer.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/web_importers/tidy_3d_web_importer.py similarity index 100% rename from blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/importers/tidy_3d_web_importer.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/web_importers/tidy_3d_web_importer.py diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/exporters/json_file_exporter.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/exporters/json_file_exporter.py index 4c5bed2..de97103 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/exporters/json_file_exporter.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/exporters/json_file_exporter.py @@ -46,7 +46,7 @@ class JSONFileExporterNode(base.MaxwellSimNode): ), } output_sockets = { - "JSON String": sockets.TextSocketDef(), + "JSON String": sockets.StringSocketDef(), } #################### diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/viewer.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/viewer.py index 38944f8..444bb43 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/viewer.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/viewer.py @@ -11,6 +11,7 @@ import tidy3d as td from ... import contracts as ct from ... import sockets from .. import base +from ...managed_objs import managed_bl_object class ConsoleViewOperator(bpy.types.Operator): @@ -50,17 +51,53 @@ class ViewerNode(base.MaxwellSimNode): "Data": sockets.AnySocketDef(), } + #################### + # - Properties + #################### + auto_plot: bpy.props.BoolProperty( + 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), + ) + + auto_3d_preview: bpy.props.BoolProperty( + 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), + ) + #################### # - UI #################### def draw_operators(self, context, layout): - row = layout.row(align=True) - row.label(text="Console") - row.operator(ConsoleViewOperator.bl_idname, text="Print") + split = layout.split(factor=0.4) - row = layout.row(align=True) - row.label(text="Plot") - row.operator(RefreshPlotViewOperator.bl_idname, text="", icon="FILE_REFRESH") + # Split LHS + col = split.column(align=False) + 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") + + ## Plot Options + row = col.row(align=True) + row.prop(self, "auto_plot", text="Plot", toggle=True) + row.operator( + RefreshPlotViewOperator.bl_idname, + text="", + icon="FILE_REFRESH", + ) + + ## 3D Preview Options + row = col.row(align=True) + row.prop(self, "auto_3d_preview", text="3D Preview", toggle=True) #################### # - Methods @@ -75,11 +112,46 @@ class ViewerNode(base.MaxwellSimNode): print(str(data)) #################### - # - Update + # - Updates #################### - @base.on_value_changed(socket_name="Data") - def on_value_changed__data(self): - self.trigger_action("show_plot") + @base.on_value_changed( + 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") + + # Remove Anything Previewed + preview_collection = managed_bl_object.bl_collection( + managed_bl_object.PREVIEW_COLLECTION_NAME, + view_layer_exclude=False, + ) + for bl_object in preview_collection.objects.values(): + preview_collection.objects.unlink(bl_object) + + # Preview Anything that Should be Previewed (maybe) + if props["auto_3d_preview"]: + self.trigger_action("show_preview") + + @base.on_value_changed( + prop_name="auto_3d_preview", + props={"auto_3d_preview"}, + ) + def on_value_changed__auto_3d_preview(self, props): + # Remove Anything Previewed + preview_collection = managed_bl_object.bl_collection( + managed_bl_object.PREVIEW_COLLECTION_NAME, + view_layer_exclude=False, + ) + for bl_object in preview_collection.objects.values(): + preview_collection.objects.unlink(bl_object) + + # Preview Anything that Should be Previewed (maybe) + if props["auto_3d_preview"]: + self.trigger_action("show_preview") #################### diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/simulations/fdtd_sim.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/simulations/fdtd_sim.py index 340ced6..71fd520 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/simulations/fdtd_sim.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/simulations/fdtd_sim.py @@ -15,7 +15,7 @@ class FDTDSimNode(base.MaxwellSimNode): #################### input_sockets = { "Domain": sockets.MaxwellSimDomainSocketDef(), - "BCs": sockets.MaxwellBoundBoxSocketDef(), + "BCs": sockets.MaxwellBoundCondsSocketDef(), "Sources": sockets.MaxwellSourceSocketDef(), "Structures": sockets.MaxwellStructureSocketDef(), "Monitors": sockets.MaxwellMonitorSocketDef(), diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/simulations/sim_domain.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/simulations/sim_domain.py index 3754017..960fe02 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/simulations/sim_domain.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/simulations/sim_domain.py @@ -3,9 +3,13 @@ import sympy as sp import sympy.physics.units as spu import scipy as sc +from .....utils import analyze_geonodes from ... import contracts as ct from ... import sockets from .. import base +from ... import managed_objs + +GEONODES_DOMAIN_BOX = "domain_box" class SimDomainNode(base.MaxwellSimNode): node_type = ct.NodeType.SimDomain @@ -24,6 +28,13 @@ class SimDomainNode(base.MaxwellSimNode): "Domain": sockets.MaxwellSimDomainSocketDef(), } + managed_obj_defs = { + "domain_box": ct.schemas.ManagedObjDef( + mk=lambda name: managed_objs.ManagedBLObject(name), + name_prefix="domain_box_", + ) + } + #################### # - Callbacks #################### @@ -46,6 +57,54 @@ class SimDomainNode(base.MaxwellSimNode): grid_spec=grid, medium=medium, ) + + #################### + # - Preview + #################### + @base.on_value_changed( + socket_name="Size", + input_sockets={"Size"}, + managed_objs={"domain_box"}, + ) + def on_value_changed__center( + self, + input_sockets: dict, + managed_objs: dict[str, ct.schemas.ManagedObj], + ): + _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" + ) + + # Sync Modifier Inputs + managed_objs["domain_box"].sync_geonodes_modifier( + geonodes_node_group=geo_nodes, + geonodes_identifier_to_value={ + 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. + } + ) + + @base.on_show_preview( + managed_objs={"domain_box"}, + ) + def on_show_preview( + self, + managed_objs: dict[str, ct.schemas.ManagedObj], + ): + managed_objs["domain_box"].show_preview("MESH") + self.on_value_changed__center() #################### # - Blender Registration diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/point_dipole_source.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/point_dipole_source.py index e7264e7..f06ba30 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/point_dipole_source.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/point_dipole_source.py @@ -8,6 +8,7 @@ import bpy from ... import contracts as ct from ... import sockets from .. import base +from ... import managed_objs class PointDipoleSourceNode(base.MaxwellSimNode): node_type = ct.NodeType.PointDipoleSource @@ -27,6 +28,13 @@ class PointDipoleSourceNode(base.MaxwellSimNode): "Source": sockets.MaxwellSourceSocketDef(), } + managed_obj_defs = { + "sphere_empty": ct.schemas.ManagedObjDef( + mk=lambda name: managed_objs.ManagedBLObject(name), + name_prefix="point_dipole_", + ) + } + #################### # - Properties #################### @@ -76,6 +84,39 @@ class PointDipoleSourceNode(base.MaxwellSimNode): polarization=pol_axis, ) return _res + + #################### + # - Preview + #################### + @base.on_value_changed( + 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 = tuple(spu.convert_to(_center, spu.um) / spu.um) + ## TODO: Preview unit system?? Presume um for now + + mobj = managed_objs["sphere_empty"] + bl_object = mobj.bl_object("EMPTY") + bl_object.location = center #tuple([float(el) for el in center]) + + @base.on_show_preview( + 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", + ) diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/__init__.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/__init__.py index 358a4a8..910d751 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/__init__.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/__init__.py @@ -1,18 +1,17 @@ #from . import object_structure -#from . import geonodes_structure -#from . import scripted_structure +from . import geonodes_structure from . import primitives BL_REGISTER = [ # *object_structure.BL_REGISTER, -# *geonodes_structure.BL_REGISTER, -# *scripted_structure.BL_REGISTER, + *geonodes_structure.BL_REGISTER, + *primitives.BL_REGISTER, ] BL_NODES = { # **object_structure.BL_NODES, -# **geonodes_structure.BL_NODES, -# **scripted_structure.BL_NODES, + **geonodes_structure.BL_NODES, + **primitives.BL_NODES, } diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/geonodes_structure.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/geonodes_structure.py index b0eeaef..65df5b2 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/geonodes_structure.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/geonodes_structure.py @@ -1,3 +1,5 @@ +import typing as typ + import tidy3d as td import numpy as np import sympy as sp @@ -7,290 +9,174 @@ import bpy from bpy_types import bpy_types import bmesh -from ... import contracts +from .....utils import analyze_geonodes +from ... import bl_socket_map +from ... import contracts as ct from ... import sockets from .. import base +from ... import managed_objs -GEONODES_MODIFIER_NAME = "BLMaxwell_GeoNodes" - -# Monkey-Patch Sympy Types -## TODO: This needs to be a more generic thing, this isn't the only place we're setting blender interface values. -def parse_scalar(scalar): - if isinstance(scalar, sp.Integer): - return int(scalar) - elif isinstance(scalar, sp.Float): - return float(scalar) - elif isinstance(scalar, sp.Rational): - return float(scalar) - elif isinstance(scalar, sp.Expr): - return float(scalar.n()) - - return scalar - -def parse_bl_to_sp(scalar): - if isinstance(scalar, bpy_types.bpy_prop_array): - return sp.Matrix(tuple(scalar)) - - return scalar - -class GeoNodesStructureNode(base.MaxwellSimTreeNode): - node_type = contracts.NodeType.GeoNodesStructure +class GeoNodesStructureNode(base.MaxwellSimNode): + node_type = ct.NodeType.GeoNodesStructure bl_label = "GeoNodes Structure" - #bl_icon = ... #################### # - Sockets #################### input_sockets = { - "preview_target": sockets.BlenderPreviewTargetSocketDef( - label="Preview Target", - ), - "blender_unit_system": sockets.PhysicalUnitSystemSocketDef( - label="Blender Units", - ), - "medium": sockets.MaxwellMediumSocketDef( - label="Medium", - ), - "geo_nodes": sockets.BlenderGeoNodesSocketDef( - label="GeoNodes", - ), + "Unit System": sockets.PhysicalUnitSystemSocketDef(), + "Medium": sockets.MaxwellMediumSocketDef(), + "GeoNodes": sockets.BlenderGeoNodesSocketDef(), } output_sockets = { - "structure": sockets.MaxwellStructureSocketDef( - label="Structure", - ), + "Structure": sockets.MaxwellStructureSocketDef(), + } + + managed_obj_defs = { + "geometry": ct.schemas.ManagedObjDef( + mk=lambda name: managed_objs.ManagedBLObject(name), + name_prefix="geonodes_", + ) } #################### # - Output Socket Computation #################### - @base.computes_output_socket("structure") - def compute_simulation(self: contracts.NodeTypeProtocol) -> td.TriangleMesh: - # Extract the Blender Object - bl_object = self.compute_input("object") + @base.computes_output_socket( + "Structure", + input_sockets={"Medium"}, + managed_objs={"geometry"}, + ) + def compute_structure( + self, + input_sockets: dict[str, typ.Any], + managed_objs: dict[str, ct.schemas.ManagedObj], + ) -> td.Structure: + # Extract the Managed Blender Object + mobj = managed_objs["geometry"] - # Ensure Updated Geometry - bpy.context.view_layer.update() - - # Triangulate Object Mesh - bmesh_mesh = bmesh.new() - bmesh_mesh.from_object( - bl_object, - bpy.context.evaluated_depsgraph_get(), - ) - bmesh.ops.triangulate(bmesh_mesh, faces=bmesh_mesh.faces) - - mesh = bpy.data.meshes.new(name="TriangulatedMesh") - bmesh_mesh.to_mesh(mesh) - bmesh_mesh.free() - - # Extract Vertices and Faces - vertices = np.array([vert.co for vert in mesh.vertices]) - faces = np.array([ - [vert for vert in poly.vertices] - for poly in mesh.polygons - ]) - - # Remove Temporary Mesh - bpy.data.meshes.remove(mesh) + # Extract Geometry as Arrays + geometry_as_arrays = mobj.mesh_as_arrays + # Return TriMesh Structure return td.Structure( - geometry=td.TriangleMesh.from_vertices_faces(vertices, faces), - medium=self.compute_input("medium") + geometry=td.TriangleMesh.from_vertices_faces( + geometry_as_arrays["verts"], + geometry_as_arrays["faces"], + ), + medium=input_sockets["Medium"], ) #################### - # - Update Function + # - Event Methods #################### - def free(self) -> None: - bl_socket = self.g_input_bl_socket("preview_target") + @base.on_value_changed( + socket_name="GeoNodes", - bl_socket.free() + managed_objs={"geometry"}, + input_sockets={"GeoNodes"}, + ) + def on_value_changed__geonodes( + self, + managed_objs: dict[str, ct.schemas.ManagedObj], + input_sockets: dict[str, typ.Any], + ) -> None: + """Called whenever the GeoNodes socket is changed. - def update_cb(self) -> None: - bl_object = self.compute_input("preview_target") - if bl_object is None: return + 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() + self.loose_input_sockets = {} + return - geo_nodes = self.compute_input("geo_nodes") - if geo_nodes is None: return + # Analyze GeoNodes + ## Extract Valid Inputs (via GeoNodes Tree "Interface") + geonodes_interface = analyze_geonodes.interface( + geo_nodes, direc="INPUT" + ) - bl_modifier = bl_object.modifiers.get(GEONODES_MODIFIER_NAME) - if bl_modifier is None: return - - # Set GeoNodes Modifier Attributes - for idx, interface_item in enumerate( - geo_nodes.interface.items_tree.values() - ): - if idx == 0: continue ## Always-on "Geometry" Input (from Object) - - # Retrieve Input Socket - bl_socket = self.inputs[ - interface_item.name - ] - - # Retrieve Linked/Unlinked Input Socket Value - if bl_socket.is_linked: - linked_bl_socket = bl_socket.links[0].from_socket - linked_bl_node = bl_socket.links[0].from_node - val = linked_bl_node.compute_output( - linked_bl_node.g_output_socket_name( - linked_bl_socket.name - ) - ) ## What a bunch of spaghetti - else: - val = bl_socket.default_value - - # Retrieve Unit-System Corrected Modifier Value - bl_unit_system = self.compute_input("blender_unit_system") - - socket_type = contracts.SocketType[ - bl_socket.bl_idname.removesuffix("SocketType") - ] - if socket_type in bl_unit_system: - unitless_val = spu.convert_to( - val, - bl_unit_system[socket_type], - ) / bl_unit_system[socket_type] - else: - unitless_val = val - - if isinstance(unitless_val, sp.matrices.MatrixBase): - unitless_val = tuple( - parse_scalar(scalar) - for scalar in unitless_val - ) - else: - unitless_val = parse_scalar(unitless_val) - - # Conservatively Set Differing Values - if bl_modifier[interface_item.identifier] != unitless_val: - bl_modifier[interface_item.identifier] = unitless_val - - # Update DepGraph - bl_object.data.update() - - def update_sockets_from_geonodes(self) -> None: - # Remove All "Loose" Sockets - socket_labels = { - socket_def.label - for socket_def in self.input_sockets.values() - } | { - socket_def.label - for socket_set_name, socket_set in self.input_socket_sets.items() - for socket_name, socket_def in socket_set.items() - } - bl_sockets_to_remove = { - bl_socket - for bl_socket_name, bl_socket in self.inputs.items() - if bl_socket_name not in socket_labels + # Set Loose Input Sockets + ## Retrieve the appropriate SocketDef for the Blender Interface Socket + self.loose_input_sockets = { + socket_name: bl_socket_map.socket_def_from_bl_interface_socket( + bl_interface_socket + )() ## === SocketDef(), but with dynamic SocketDef + for socket_name, bl_interface_socket in geonodes_interface.items() } - for bl_socket in bl_sockets_to_remove: - self.inputs.remove(bl_socket) - - # Query for Blender Object / Geo Nodes - bl_object = self.compute_input("preview_target") - if bl_object is None: return - - # Remove Existing GeoNodes Modifier - if GEONODES_MODIFIER_NAME in bl_object.modifiers: - modifier_to_remove = bl_object.modifiers[GEONODES_MODIFIER_NAME] - bl_object.modifiers.remove(modifier_to_remove) - - # Retrieve GeoNodes Tree - geo_nodes = self.compute_input("geo_nodes") - if geo_nodes is None: return - - # Add Non-Static Sockets from GeoNodes - for bl_socket_name, bl_socket in geo_nodes.interface.items_tree.items(): - # For now, don't allow Geometry inputs. - if bl_socket.socket_type == "NodeSocketGeometry": continue + ## Set Loose `socket.value` from Interface `default_value` + for socket_name in self.loose_input_sockets: + socket = self.inputs[socket_name] + bl_interface_socket = geonodes_interface[socket_name] - # Establish Dimensions of GeoNodes Input Sockets - if ( - bl_socket.description.startswith("2D") - ): - dimensions = 2 - elif ( - bl_socket.socket_type.startswith("NodeSocketVector") - or bl_socket.socket_type.startswith("NodeSocketColor") - or bl_socket.socket_type.startswith("NodeSocketRotation") - ): - dimensions = 3 - else: - dimensions = 1 - - # Choose Socket via. Description Hint (if exists) - if ( - ":" in bl_socket.description - and "(" in (desc_hint := bl_socket.description.split(":")[0]) - and ")" in desc_hint - ): - for tag in contracts.BLNodeSocket_to_SocketType_by_desc[ - dimensions - ]: - if desc_hint.startswith(tag): - self.inputs.new( - contracts.BLNodeSocket_to_SocketType_by_desc[ - dimensions - ][tag], - bl_socket_name, - ) - - if len([ - (unit := _unit) - for _unit in contracts.SocketType_to_units[ - contracts.SocketType[ - self.inputs[bl_socket_name].bl_idname.removesuffix("SocketType") - ] - ]["values"].values() - if desc_hint[ - desc_hint.find("(")+1 : desc_hint.find(")") - ] == str(_unit) - ]) > 0: - self.inputs[bl_socket_name].unit = unit - - elif bl_socket.socket_type in contracts.BLNodeSocket_to_SocketType[ - dimensions - ]: - self.inputs.new( - contracts.BLNodeSocket_to_SocketType[ - dimensions - ][bl_socket.socket_type], - bl_socket_name, + socket.value = bl_socket_map.value_from_bl(bl_interface_socket) + + ## Implicitly triggers the loose-input `on_value_changed` for each. + + @base.on_value_changed( + any_loose_input_socket=True, + + managed_objs={"geometry"}, + input_sockets={"Unit System", "GeoNodes"}, + ) + def on_value_changed__loose_inputs( + self, + managed_objs: dict[str, ct.schemas.ManagedObj], + input_sockets: dict[str, typ.Any], + loose_input_sockets: dict[str, typ.Any], + ): + """Called whenever a Loose Input Socket is altered. + + Synchronizes the change to the actual GeoNodes modifier, so that the change is immediately visible. + """ + # Retrieve Data + unit_system = input_sockets["Unit System"] + mobj = managed_objs["geometry"] + + if not (geo_nodes := input_sockets["GeoNodes"]): return + + # Analyze GeoNodes Interface (input direction) + ## This retrieves NodeTreeSocketInterface elements + geonodes_interface = analyze_geonodes.interface( + geo_nodes, direc="INPUT" + ) + + ## TODO: Check that Loose Sockets matches the Interface + ## - If the user deletes an interface socket, bad things will happen. + ## - We will try to set an identifier that doesn't exist! + ## - Instead, this should update the loose input sockets. + + ## Push Values to the GeoNodes Modifier + mobj.sync_geonodes_modifier( + geonodes_node_group=geo_nodes, + geonodes_identifier_to_value={ + bl_interface_socket.identifier: bl_socket_map.value_to_bl( + bl_interface_socket, + loose_input_sockets[socket_name], + unit_system, ) - + for socket_name, bl_interface_socket in ( + geonodes_interface.items() + ) + } + ) + + #################### + # - Event Methods + #################### + @base.on_show_preview( + managed_objs={"geometry"}, + ) + def on_show_preview( + self, + managed_objs: dict[str, ct.schemas.ManagedObj], + ): + """Called whenever a Loose Input Socket is altered. - # Create New GeoNodes Modifier - if GEONODES_MODIFIER_NAME not in bl_object.modifiers: - modifier = bl_object.modifiers.new( - name=GEONODES_MODIFIER_NAME, - type="NODES", - ) - modifier.node_group = geo_nodes - - # Set Default Values - for interface_item in geo_nodes.interface.items_tree.values(): - if ( - interface_item.name in self.inputs - and hasattr(interface_item, "default_value") - ): - bl_socket = self.inputs[ - interface_item.name - ] - if hasattr(bl_socket, "use_units"): - bl_unit_system = self.compute_input("blender_unit_system") - socket_type = contracts.SocketType[ - bl_socket.bl_idname.removesuffix("SocketType") - ] - - bl_socket.default_value = ( - parse_bl_to_sp(interface_item.default_value) - * bl_unit_system[socket_type] - ) - else: - bl_socket.default_value = parse_bl_to_sp(interface_item.default_value) - + Synchronizes the change to the actual GeoNodes modifier, so that the change is immediately visible. + """ + managed_objs["geometry"].show_preview("MESH") #################### @@ -300,7 +186,7 @@ BL_REGISTER = [ GeoNodesStructureNode, ] BL_NODES = { - contracts.NodeType.GeoNodesStructure: ( - contracts.NodeCategory.MAXWELLSIM_STRUCTURES + ct.NodeType.GeoNodesStructure: ( + ct.NodeCategory.MAXWELLSIM_STRUCTURES ) } diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/__init__.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/__init__.py index f2b517a..e9df45c 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/__init__.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/__init__.py @@ -1,7 +1,9 @@ +from . import base + from . import basic AnySocketDef = basic.AnySocketDef BoolSocketDef = basic.BoolSocketDef -TextSocketDef = basic.TextSocketDef +StringSocketDef = basic.StringSocketDef FilePathSocketDef = basic.FilePathSocketDef from . import number @@ -40,8 +42,8 @@ BlenderGeoNodesSocketDef = blender.BlenderGeoNodesSocketDef BlenderTextSocketDef = blender.BlenderTextSocketDef from . import maxwell -MaxwellBoundBoxSocketDef = maxwell.MaxwellBoundBoxSocketDef -MaxwellBoundFaceSocketDef = maxwell.MaxwellBoundFaceSocketDef +MaxwellBoundCondSocketDef = maxwell.MaxwellBoundCondSocketDef +MaxwellBoundCondsSocketDef = maxwell.MaxwellBoundCondsSocketDef MaxwellMediumSocketDef = maxwell.MaxwellMediumSocketDef MaxwellMediumNonLinearitySocketDef = maxwell.MaxwellMediumNonLinearitySocketDef MaxwellSourceSocketDef = maxwell.MaxwellSourceSocketDef diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/base.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/base.py index ecd1197..1a5c567 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/base.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/base.py @@ -170,6 +170,19 @@ class MaxwellSimSocket(bpy.types.NodeSocket): def value(self, value: typ.Any) -> None: raise NotImplementedError + def value_as_unit_system( + self, + unit_system: dict, + dimensionless: bool = True + ) -> typ.Any: + ## TODO: Caching could speed this boi up quite a bit + + unit_system_unit = unit_system[self.socket_type] + return spu.convert_to( + self.value, + unit_system_unit, + ) / unit_system_unit + @property def lazy_value(self) -> None: raise NotImplementedError diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/__init__.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/__init__.py index db03d73..11935e5 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/__init__.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/__init__.py @@ -1,19 +1,19 @@ -from . import any_socket +from . import any as any_socket AnySocketDef = any_socket.AnySocketDef -from . import bool_socket +from . import bool as bool_socket BoolSocketDef = bool_socket.BoolSocketDef -from . import text_socket -TextSocketDef = text_socket.TextSocketDef +from . import string +StringSocketDef = string.StringSocketDef -from . import file_path_socket -FilePathSocketDef = file_path_socket.FilePathSocketDef +from . import file_path +FilePathSocketDef = file_path.FilePathSocketDef BL_REGISTER = [ *any_socket.BL_REGISTER, *bool_socket.BL_REGISTER, - *text_socket.BL_REGISTER, - *file_path_socket.BL_REGISTER, + *string.BL_REGISTER, + *file_path.BL_REGISTER, ] diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/any_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/any.py similarity index 100% rename from blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/any_socket.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/any.py diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/bool_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/bool.py similarity index 100% rename from blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/bool_socket.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/bool.py diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/file_path_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/file_path.py similarity index 96% rename from blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/file_path_socket.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/file_path.py index f81fc9f..c2ae889 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/file_path_socket.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/file_path.py @@ -24,7 +24,6 @@ class FilePathBLSocket(base.MaxwellSimSocket): subtype="FILE_PATH", update=(lambda self, context: self.sync_prop("raw_value", context)), ) - ## TODO: Use bpy methods to constrain the path #################### # - Socket UI diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/text_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/string.py similarity index 74% rename from blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/text_socket.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/string.py index 68a1a2b..5d5f59a 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/text_socket.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/string.py @@ -10,16 +10,16 @@ from ... import contracts as ct #################### # - Blender Socket #################### -class TextBLSocket(base.MaxwellSimSocket): - socket_type = ct.SocketType.Text - bl_label = "Text" +class StringBLSocket(base.MaxwellSimSocket): + socket_type = ct.SocketType.String + bl_label = "String" #################### # - Properties #################### raw_value: bpy.props.StringProperty( - name="Text", - description="Represents some text", + name="String", + description="Represents a string", default="", update=(lambda self, context: self.sync_prop("raw_value", context)), ) @@ -28,8 +28,6 @@ class TextBLSocket(base.MaxwellSimSocket): # - Socket UI #################### def draw_label_row(self, label_col_row: bpy.types.UILayout, text: str) -> None: - """Draw the value of the real number. - """ label_col_row.prop(self, "raw_value", text=text) #################### @@ -46,17 +44,17 @@ class TextBLSocket(base.MaxwellSimSocket): #################### # - Socket Configuration #################### -class TextSocketDef(pyd.BaseModel): - socket_type: ct.SocketType = ct.SocketType.Text +class StringSocketDef(pyd.BaseModel): + socket_type: ct.SocketType = ct.SocketType.String default_text: str = "" - def init(self, bl_socket: TextBLSocket) -> None: + def init(self, bl_socket: StringBLSocket) -> None: bl_socket.value = self.default_text #################### # - Blender Registration #################### BL_REGISTER = [ - TextBLSocket, + StringBLSocket, ] diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/__init__.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/__init__.py index 6d47392..3d41ae7 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/__init__.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/__init__.py @@ -1,22 +1,21 @@ -from . import object_socket -from . import collection_socket +from . import object as object_socket +from . import collection BlenderObjectSocketDef = object_socket.BlenderObjectSocketDef -BlenderCollectionSocketDef = collection_socket.BlenderCollectionSocketDef +BlenderCollectionSocketDef = collection.BlenderCollectionSocketDef -from . import image_socket -BlenderImageSocketDef = image_socket.BlenderImageSocketDef +from . import image +BlenderImageSocketDef = image.BlenderImageSocketDef -from . import geonodes_socket -from . import text_socket -BlenderGeoNodesSocketDef = geonodes_socket.BlenderGeoNodesSocketDef -BlenderTextSocketDef = text_socket.BlenderTextSocketDef +from . import geonodes +from . import text +BlenderGeoNodesSocketDef = geonodes.BlenderGeoNodesSocketDef +BlenderTextSocketDef = text.BlenderTextSocketDef BL_REGISTER = [ *object_socket.BL_REGISTER, - *collection_socket.BL_REGISTER, + *collection.BL_REGISTER, - *image_socket.BL_REGISTER, - - *geonodes_socket.BL_REGISTER, - *text_socket.BL_REGISTER, + *text.BL_REGISTER, + *image.BL_REGISTER, + *geonodes.BL_REGISTER, ] diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/collection_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/collection.py similarity index 96% rename from blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/collection_socket.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/collection.py index a684343..d454a25 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/collection_socket.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/collection.py @@ -18,7 +18,7 @@ class BlenderCollectionBLSocket(base.MaxwellSimSocket): #################### raw_value: bpy.props.PointerProperty( name="Blender Collection", - description="Represents a Blender collection", + description="A Blender collection", type=bpy.types.Collection, update=(lambda self, context: self.sync_prop("raw_value", context)), ) diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/geonodes_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/geonodes.py similarity index 70% rename from blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/geonodes_socket.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/geonodes.py index e8486b6..df9e099 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/geonodes_socket.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/geonodes.py @@ -13,8 +13,17 @@ class BlenderMaxwellResetGeoNodesSocket(bpy.types.Operator): bl_idname = "blender_maxwell.reset_geo_nodes_socket" bl_label = "Reset GeoNodes Socket" + node_tree_name: bpy.props.StringProperty(name="Node Tree Name") + node_name: bpy.props.StringProperty(name="Node Name") + socket_name: bpy.props.StringProperty(name="Socket Name") + def execute(self, context): - context.socket.update_geonodes_node() + node_tree = bpy.data.node_groups[self.node_tree_name] + node = node_tree.nodes[self.node_name] + socket = node.inputs[self.socket_name] + + # Report as though the GeoNodes Tree Changed + socket.sync_prop("raw_value", context) return {'FINISHED'} @@ -33,7 +42,7 @@ class BlenderGeoNodesBLSocket(base.MaxwellSimSocket): name="Blender GeoNodes Tree", description="Represents a Blender GeoNodes Tree", type=bpy.types.NodeTree, - poll=(lambda self, obj: obj.bl_idname == 'GeometryNodeTree'), + poll=(lambda self, obj: obj.bl_idname == "GeometryNodeTree"), update=(lambda self, context: self.sync_prop("raw_value", context)), ) @@ -42,12 +51,16 @@ class BlenderGeoNodesBLSocket(base.MaxwellSimSocket): #################### def draw_label_row(self, label_col_row, text): label_col_row.label(text=text) - if self.raw_value: - label_col_row.operator( - "blender_maxwell.reset_geo_nodes_socket", - text="", - icon="FILE_REFRESH", - ) + if not self.raw_value: return + + op = label_col_row.operator( + BlenderMaxwellResetGeoNodesSocket.bl_idname, + text="", + icon="FILE_REFRESH", + ) + op.socket_name = self.name + op.node_name = self.node.name + op.node_tree_name = self.node.id_data.name #################### # - UI diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/image_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/image.py similarity index 100% rename from blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/image_socket.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/image.py diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/object_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/object.py similarity index 67% rename from blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/object_socket.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/object.py index a2c41a2..8f0ef80 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/object_socket.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/object.py @@ -7,26 +7,22 @@ from .. import base from ... import contracts as ct #################### -# - Blender Socket +# - 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" - ## TODO: Refactor + 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): - mesh = bpy.data.meshes.new("GenMesh") - new_bl_object = bpy.data.objects.new("GenObj", mesh) + node_tree = bpy.data.node_groups[self.node_tree_name] + node = node_tree.nodes[self.node_name] + socket = node.inputs[self.socket_name] - context.collection.objects.link(new_bl_object) - - node = context.node - for bl_socket_name, bl_socket in node.inputs.items(): - if isinstance(bl_socket, BlenderObjectBLSocket): - bl_socket.default_value = new_bl_object - - if hasattr(node, "update_sockets_from_geonodes"): - node.update_sockets_from_geonodes() + socket.create_and_assign_bl_object() return {'FINISHED'} @@ -52,15 +48,31 @@ class BlenderObjectBLSocket(base.MaxwellSimSocket): #################### def draw_label_row(self, label_col_row, text): label_col_row.label(text=text) - label_col_row.operator( + + op = label_col_row.operator( "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="") + #################### + # - 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) + + bpy.context.collection.objects.link(new_bl_object) + + self.value = new_bl_object + #################### # - Default Value #################### diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/target_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/target_socket.py deleted file mode 100644 index 66c4dcd..0000000 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/target_socket.py +++ /dev/null @@ -1,268 +0,0 @@ -import typing as typ - -import bpy -import sympy as sp -import pydantic as pyd - -from .. import base -from ... import contracts - -def mk_and_assign_target_bl_obj(bl_socket, node, node_tree): - # Create Mesh and Object - mesh = bpy.data.meshes.new("Mesh" + bl_socket.node.name) - new_bl_object = bpy.data.objects.new(bl_socket.node.name, mesh) - - # Create Preview Collection and Object - if bl_socket.show_preview: - #if not node_tree.preview_collection: - # new_collection = bpy.data.collections.new("BLMaxwellPreview") - # node_tree.preview_collection = new_collection - # - # bpy.context.scene.collection.children.link(new_collection) - - node_tree.preview_collection.objects.link(new_bl_object) - - # Create Non-Preview Collection and Object - else: - #if not node_tree.non_preview_collection: - # new_collection = bpy.data.collections.new("BLMaxwellNonPreview") - # node_tree.non_preview_collection = new_collection - # - # bpy.context.scene.collection.children.link(new_collection) - - node_tree.non_preview_collection.objects.link(new_bl_object) - - bl_socket.local_target_object = new_bl_object - - if hasattr(node, "update_sockets_from_geonodes"): - node.update_sockets_from_geonodes() - -class BlenderMaxwellCreateAndAssignTargetBLObject(bpy.types.Operator): - bl_idname = "blender_maxwell.create_and_assign_target_bl_object" - bl_label = "Create and Assign Target BL Object" - - def execute(self, context): - bl_socket = context.socket - node = bl_socket.node - node_tree = node.id_data - - mk_and_assign_target_bl_obj(bl_socket, node, node_tree) - return {'FINISHED'} - -#################### -# - Blender Socket -#################### -class BlenderPreviewTargetBLSocket(base.BLSocket): - socket_type = contracts.SocketType.BlenderPreviewTarget - bl_label = "BlenderPreviewTarget" - - #################### - # - Properties - #################### - show_preview: bpy.props.BoolProperty( - name="Target Object Included in Preview", - description="Whether or not Blender will preview the target object", - default=True, - update=(lambda self, context: self.update_preview()), - ) - show_definition: bpy.props.BoolProperty( - name="Show Unit System Definition", - description="Toggle to show unit system definition", - default=False, - update=(lambda self, context: self.trigger_updates()), - ) - - - target_object_pinned: bpy.props.BoolProperty( - name="Target Object Pinned", - description="Whether or not Blender will manage the target object", - default=True, - ) - preview_collection_pinned: bpy.props.BoolProperty( - name="Global Preview Collection Pinned", - description="Whether or not Blender will use the global preview collection", - default=True, - ) - non_preview_collection_pinned: bpy.props.BoolProperty( - name="Global Non-Preview Collection Pinned", - description="Whether or not Blender will use the global non-preview collection", - default=True, - ) - - - local_target_object: bpy.props.PointerProperty( - name="Local Target Blender Object", - description="Represents a Blender object to apply a preview to", - type=bpy.types.Object, - update=(lambda self, context: self.trigger_updates()), - ) - local_preview_collection: bpy.props.PointerProperty( - name="Local Preview Collection", - description="Collection of Blender objects that will be previewed", - type=bpy.types.Collection, - update=(lambda self, context: self.trigger_updates()) - ) - local_non_preview_collection: bpy.props.PointerProperty( - name="Local Non-Preview Collection", - description="Collection of Blender objects that will NOT be previewed", - type=bpy.types.Collection, - update=(lambda self, context: self.trigger_updates()) - ) - - #################### - # - Methods - #################### - def update_preview(self): - node_tree = self.node.id_data - - # Target Object Pinned - if ( - self.show_preview - and self.local_target_object - and self.target_object_pinned - ): - node_tree.non_preview_collection.objects.unlink(self.local_target_object) - node_tree.preview_collection.objects.link(self.local_target_object) - - elif ( - not self.show_preview - and self.local_target_object - and self.target_object_pinned - ): - node_tree.preview_collection.objects.unlink(self.local_target_object) - node_tree.non_preview_collection.objects.link(self.local_target_object) - - # Target Object Not Pinned - if ( - self.show_preview - and self.local_target_object - and not self.target_object_pinned - and self.local_target_object.name in ( - node_tree.non_preview_collection.objects.keys() - ) - ): - node_tree.non_preview_collection.objects.unlink(self.local_target_object) - node_tree.preview_collection.objects.link(self.local_target_object) - elif ( - not self.show_preview - and self.local_target_object - and not self.target_object_pinned - and self.local_target_object.name in ( - node_tree.preview_collection.objects.keys() - ) - ): - node_tree.preview_collection.objects.unlink(self.local_target_object) - node_tree.non_preview_collection.objects.link(self.local_target_object) - - self.trigger_updates() - - #################### - # - UI - #################### - def draw_label_row(self, label_col_row: bpy.types.UILayout, text) -> None: - label_col_row.label(text=text) - label_col_row.prop(self, "show_preview", toggle=True, text="", icon="SEQ_PREVIEW") - label_col_row.prop(self, "show_definition", toggle=True, text="", icon="MOD_LENGTH") - - def draw_value(self, col: bpy.types.UILayout) -> None: - node_tree = self.node.id_data - - if self.show_definition: - col_row = col.row(align=True) - col_row.alignment = "EXPAND" - col_row.label(text="Target", icon="OBJECT_DATA") - col_row.prop( - self, - "target_object_pinned", - toggle=True, - icon="EVENT_A", - icon_only=True, - ) - #col_row.operator( - # "blender_maxwell.create_and_assign_target_bl_object", - # text="", - # icon="ADD", - #) - - col_row=col.row(align=True) - col_row.alignment = "EXPAND" - if not self.target_object_pinned: - col_row.prop(self, "local_target_object", text="") - - # Non-Preview Collection - col_row=col.row(align=True) - col_row.alignment = "EXPAND" - col_row.label(text="Enabled", icon="COLLECTION_COLOR_04") - col_row.prop( - self, - "preview_collection_pinned", - toggle=True, - icon="PINNED", - icon_only=True, - ) - - col_row=col.row(align=True) - col_row.alignment = "EXPAND" - if not self.preview_collection_pinned: - col_row.prop(self, "local_preview_collection", text="") - - # Non-Preview Collection - col_row=col.row(align=True) - col_row.alignment = "EXPAND" - col_row.label(text="Disabled", icon="COLLECTION_COLOR_01") - col_row.prop( - self, - "non_preview_collection_pinned", - toggle=True, - icon="PINNED", - icon_only=True, - ) - - col_row=col.row(align=True) - col_row.alignment = "EXPAND" - if not self.non_preview_collection_pinned: - col_row.prop(self, "local_non_preview_collection", text="") - - #################### - # - Default Value - #################### - @property - def default_value(self) -> bpy.types.Object: - node_tree = self.node.id_data - if not self.local_target_object and self.target_object_pinned: - mk_and_assign_target_bl_obj(self, self.node, node_tree) - return self.local_target_object - - return self.local_target_object - - @default_value.setter - def default_value(self, value: typ.Any) -> None: - pass - - #################### - # - Cleanup - #################### - def free(self) -> None: - if self.local_target_object: - bpy.data.meshes.remove(self.local_target_object.data, do_unlink=True) - -#################### -# - Socket Configuration -#################### -class BlenderPreviewTargetSocketDef(pyd.BaseModel): - socket_type: contracts.SocketType = contracts.SocketType.BlenderPreviewTarget - label: str - - show_preview: bool = True - - def init(self, bl_socket: BlenderPreviewTargetBLSocket) -> None: - pass - #bl_socket.show_preview = self.show_preview - -#################### -# - Blender Registration -#################### -BL_REGISTER = [ - BlenderMaxwellCreateAndAssignTargetBLObject, - BlenderPreviewTargetBLSocket, -] diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/text_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/text.py similarity index 88% rename from blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/text_socket.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/text.py index 7559b0e..53d1ef0 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/text_socket.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/text.py @@ -4,13 +4,13 @@ import bpy import pydantic as pyd from .. import base -from ... import contracts +from ... import contracts as ct #################### # - Blender Socket #################### class BlenderTextBLSocket(base.MaxwellSimSocket): - socket_type = contracts.SocketType.BlenderText + socket_type = ct.SocketType.BlenderText bl_label = "Blender Text" #################### @@ -44,7 +44,7 @@ class BlenderTextBLSocket(base.MaxwellSimSocket): # - Socket Configuration #################### class BlenderTextSocketDef(pyd.BaseModel): - socket_type: contracts.SocketType = contracts.SocketType.BlenderText + socket_type: ct.SocketType = ct.SocketType.BlenderText def init(self, bl_socket: BlenderTextBLSocket) -> None: pass diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/__init__.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/__init__.py index 196f9ac..66a37be 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/__init__.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/__init__.py @@ -1,45 +1,45 @@ -from . import bound_box_socket -from . import bound_face_socket -MaxwellBoundBoxSocketDef = bound_box_socket.MaxwellBoundBoxSocketDef -MaxwellBoundFaceSocketDef = bound_face_socket.MaxwellBoundFaceSocketDef +from . import bound_cond +from . import bound_conds +MaxwellBoundCondSocketDef = bound_cond.MaxwellBoundCondSocketDef +MaxwellBoundCondsSocketDef = bound_conds.MaxwellBoundCondsSocketDef -from . import medium_socket -from . import medium_non_linearity_socket -MaxwellMediumSocketDef = medium_socket.MaxwellMediumSocketDef -MaxwellMediumNonLinearitySocketDef = medium_non_linearity_socket.MaxwellMediumNonLinearitySocketDef +from . import medium +from . import medium_non_linearity +MaxwellMediumSocketDef = medium.MaxwellMediumSocketDef +MaxwellMediumNonLinearitySocketDef = medium_non_linearity.MaxwellMediumNonLinearitySocketDef -from . import source_socket -from . import temporal_shape_socket -MaxwellSourceSocketDef = source_socket.MaxwellSourceSocketDef -MaxwellTemporalShapeSocketDef = temporal_shape_socket.MaxwellTemporalShapeSocketDef +from . import source +from . import temporal_shape +MaxwellSourceSocketDef = source.MaxwellSourceSocketDef +MaxwellTemporalShapeSocketDef = temporal_shape.MaxwellTemporalShapeSocketDef -from . import structure_socket -MaxwellStructureSocketDef = structure_socket.MaxwellStructureSocketDef +from . import structure +MaxwellStructureSocketDef = structure.MaxwellStructureSocketDef -from . import monitor_socket -MaxwellMonitorSocketDef = monitor_socket.MaxwellMonitorSocketDef +from . import monitor +MaxwellMonitorSocketDef = monitor.MaxwellMonitorSocketDef -from . import fdtd_sim_socket -from . import sim_grid_socket -from . import sim_grid_axis_socket -from . import sim_domain_socket -MaxwellFDTDSimSocketDef = fdtd_sim_socket.MaxwellFDTDSimSocketDef -MaxwellSimGridSocketDef = sim_grid_socket.MaxwellSimGridSocketDef -MaxwellSimGridAxisSocketDef = sim_grid_axis_socket.MaxwellSimGridAxisSocketDef -MaxwellSimDomainSocketDef = sim_domain_socket.MaxwellSimDomainSocketDef +from . import fdtd_sim +from . import sim_grid +from . import sim_grid_axis +from . import sim_domain +MaxwellFDTDSimSocketDef = fdtd_sim.MaxwellFDTDSimSocketDef +MaxwellSimGridSocketDef = sim_grid.MaxwellSimGridSocketDef +MaxwellSimGridAxisSocketDef = sim_grid_axis.MaxwellSimGridAxisSocketDef +MaxwellSimDomainSocketDef = sim_domain.MaxwellSimDomainSocketDef BL_REGISTER = [ - *bound_box_socket.BL_REGISTER, - *bound_face_socket.BL_REGISTER, - *medium_socket.BL_REGISTER, - *medium_non_linearity_socket.BL_REGISTER, - *source_socket.BL_REGISTER, - *temporal_shape_socket.BL_REGISTER, - *structure_socket.BL_REGISTER, - *monitor_socket.BL_REGISTER, - *fdtd_sim_socket.BL_REGISTER, - *sim_grid_socket.BL_REGISTER, - *sim_grid_axis_socket.BL_REGISTER, - *sim_domain_socket.BL_REGISTER, + *bound_cond.BL_REGISTER, + *bound_conds.BL_REGISTER, + *medium.BL_REGISTER, + *medium_non_linearity.BL_REGISTER, + *source.BL_REGISTER, + *temporal_shape.BL_REGISTER, + *structure.BL_REGISTER, + *monitor.BL_REGISTER, + *fdtd_sim.BL_REGISTER, + *sim_grid.BL_REGISTER, + *sim_grid_axis.BL_REGISTER, + *sim_domain.BL_REGISTER, ] diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/bound_face_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/bound_cond.py similarity index 83% rename from blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/bound_face_socket.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/bound_cond.py index d7f201a..5f877ef 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/bound_face_socket.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/bound_cond.py @@ -8,8 +8,8 @@ import tidy3d as td from .. import base from ... import contracts as ct -class MaxwellBoundFaceBLSocket(base.MaxwellSimSocket): - socket_type = ct.SocketType.MaxwellBoundFace +class MaxwellBoundCondBLSocket(base.MaxwellSimSocket): + socket_type = ct.SocketType.MaxwellBoundCond bl_label = "Maxwell Bound Face" #################### @@ -53,17 +53,17 @@ class MaxwellBoundFaceBLSocket(base.MaxwellSimSocket): #################### # - Socket Configuration #################### -class MaxwellBoundFaceSocketDef(pyd.BaseModel): - socket_type: ct.SocketType = ct.SocketType.MaxwellBoundFace +class MaxwellBoundCondSocketDef(pyd.BaseModel): + socket_type: ct.SocketType = ct.SocketType.MaxwellBoundCond default_choice: typx.Literal["PML", "PEC", "PMC", "PERIODIC"] = "PML" - def init(self, bl_socket: MaxwellBoundFaceBLSocket) -> None: + def init(self, bl_socket: MaxwellBoundCondBLSocket) -> None: bl_socket.value = self.default_choice #################### # - Blender Registration #################### BL_REGISTER = [ - MaxwellBoundFaceBLSocket, + MaxwellBoundCondBLSocket, ] diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/bound_box_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/bound_conds.py similarity index 91% rename from blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/bound_box_socket.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/bound_conds.py index 8935d19..873830a 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/bound_box_socket.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/bound_conds.py @@ -20,8 +20,8 @@ BOUND_MAP = { "PERIODIC": td.Periodic(), } -class MaxwellBoundBoxBLSocket(base.MaxwellSimSocket): - socket_type = ct.SocketType.MaxwellBoundBox +class MaxwellBoundCondsBLSocket(base.MaxwellSimSocket): + socket_type = ct.SocketType.MaxwellBoundConds bl_label = "Maxwell Bound Box" #################### @@ -126,15 +126,15 @@ class MaxwellBoundBoxBLSocket(base.MaxwellSimSocket): #################### # - Socket Configuration #################### -class MaxwellBoundBoxSocketDef(pyd.BaseModel): - socket_type: ct.SocketType = ct.SocketType.MaxwellBoundBox +class MaxwellBoundCondsSocketDef(pyd.BaseModel): + socket_type: ct.SocketType = ct.SocketType.MaxwellBoundConds - def init(self, bl_socket: MaxwellBoundBoxBLSocket) -> None: + def init(self, bl_socket: MaxwellBoundCondsBLSocket) -> None: pass #################### # - Blender Registration #################### BL_REGISTER = [ - MaxwellBoundBoxBLSocket, + MaxwellBoundCondsBLSocket, ] diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/fdtd_sim_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/fdtd_sim.py similarity index 100% rename from blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/fdtd_sim_socket.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/fdtd_sim.py diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/medium_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/medium.py similarity index 100% rename from blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/medium_socket.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/medium.py diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/medium_non_linearity_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/medium_non_linearity.py similarity index 100% rename from blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/medium_non_linearity_socket.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/medium_non_linearity.py diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/monitor_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/monitor.py similarity index 100% rename from blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/monitor_socket.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/monitor.py diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/sim_domain_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/sim_domain.py similarity index 100% rename from blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/sim_domain_socket.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/sim_domain.py diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/sim_grid_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/sim_grid.py similarity index 100% rename from blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/sim_grid_socket.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/sim_grid.py diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/sim_grid_axis_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/sim_grid_axis.py similarity index 100% rename from blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/sim_grid_axis_socket.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/sim_grid_axis.py diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/source_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/source.py similarity index 100% rename from blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/source_socket.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/source.py diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/structure_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/structure.py similarity index 100% rename from blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/structure_socket.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/structure.py diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/temporal_shape_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/temporal_shape.py similarity index 100% rename from blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/temporal_shape_socket.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/temporal_shape.py diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/__init__.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/__init__.py index e07df32..8c763ad 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/__init__.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/__init__.py @@ -1,19 +1,19 @@ -from . import integer_number_socket -IntegerNumberSocketDef = integer_number_socket.IntegerNumberSocketDef +from . import integer_number +IntegerNumberSocketDef = integer_number.IntegerNumberSocketDef -from . import rational_number_socket -RationalNumberSocketDef = rational_number_socket.RationalNumberSocketDef +from . import rational_number +RationalNumberSocketDef = rational_number.RationalNumberSocketDef -from . import real_number_socket -RealNumberSocketDef = real_number_socket.RealNumberSocketDef +from . import real_number +RealNumberSocketDef = real_number.RealNumberSocketDef -from . import complex_number_socket -ComplexNumberSocketDef = complex_number_socket.ComplexNumberSocketDef +from . import complex_number +ComplexNumberSocketDef = complex_number.ComplexNumberSocketDef BL_REGISTER = [ - *integer_number_socket.BL_REGISTER, - *rational_number_socket.BL_REGISTER, - *real_number_socket.BL_REGISTER, - *complex_number_socket.BL_REGISTER, + *integer_number.BL_REGISTER, + *rational_number.BL_REGISTER, + *real_number.BL_REGISTER, + *complex_number.BL_REGISTER, ] diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/complex_number_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/complex_number.py similarity index 94% rename from blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/complex_number_socket.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/complex_number.py index 5b6e93b..9be8249 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/complex_number_socket.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/complex_number.py @@ -79,11 +79,6 @@ class ComplexNumberBLSocket(base.MaxwellSimSocket): - Polar: r,t -> re^(it) """ - # (Guard) Value Compatibility - if not self.is_compatible(value): - msg = f"Tried setting socket ({self}) to incompatible value ({value}) of type {type(value)}" - raise ValueError(msg) - self.raw_value = { "CARTESIAN": (sp.re(value), sp.im(value)), "POLAR": (sp.Abs(value), sp.arg(value)), @@ -115,9 +110,11 @@ class ComplexNumberBLSocket(base.MaxwellSimSocket): class ComplexNumberSocketDef(pyd.BaseModel): socket_type: ct.SocketType = ct.SocketType.ComplexNumber + default_value: SympyExpr = sp.S(0 + 0j) coord_sys: typ.Literal["CARTESIAN", "POLAR"] = "CARTESIAN" def init(self, bl_socket: ComplexNumberBLSocket) -> None: + bl_socket.value = self.default_value bl_socket.coord_sys = self.coord_sys #################### diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/integer_number_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/integer_number.py similarity index 100% rename from blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/integer_number_socket.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/integer_number.py diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/rational_number_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/rational_number.py similarity index 95% rename from blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/rational_number_socket.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/rational_number.py index 20d4723..d916fe6 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/rational_number_socket.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/rational_number.py @@ -58,8 +58,10 @@ class RationalNumberBLSocket(base.MaxwellSimSocket): class RationalNumberSocketDef(pyd.BaseModel): socket_type: ct.SocketType = ct.SocketType.RationalNumber + default_value: SympyExpr = sp.Rational(0, 1) + def init(self, bl_socket: RationalNumberBLSocket) -> None: - pass + bl_socket.value = self.default_value #################### # - Blender Registration diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/real_number_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/real_number.py similarity index 100% rename from blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/real_number_socket.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/real_number.py diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/__init__.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/__init__.py index e33a204..4adf964 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/__init__.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/__init__.py @@ -1,64 +1,64 @@ -from . import unit_system_socket -PhysicalUnitSystemSocketDef = unit_system_socket.PhysicalUnitSystemSocketDef +from . import unit_system +PhysicalUnitSystemSocketDef = unit_system.PhysicalUnitSystemSocketDef -from . import time_socket -PhysicalTimeSocketDef = time_socket.PhysicalTimeSocketDef +from . import time +PhysicalTimeSocketDef = time.PhysicalTimeSocketDef -from . import angle_socket -PhysicalAngleSocketDef = angle_socket.PhysicalAngleSocketDef +from . import angle +PhysicalAngleSocketDef = angle.PhysicalAngleSocketDef -from . import length_socket -from . import area_socket -from . import volume_socket -PhysicalLengthSocketDef = length_socket.PhysicalLengthSocketDef -PhysicalAreaSocketDef = area_socket.PhysicalAreaSocketDef -PhysicalVolumeSocketDef = volume_socket.PhysicalVolumeSocketDef +from . import length +from . import area +from . import volume +PhysicalLengthSocketDef = length.PhysicalLengthSocketDef +PhysicalAreaSocketDef = area.PhysicalAreaSocketDef +PhysicalVolumeSocketDef = volume.PhysicalVolumeSocketDef -from . import point_3d_socket -PhysicalPoint3DSocketDef = point_3d_socket.PhysicalPoint3DSocketDef +from . import point_3d +PhysicalPoint3DSocketDef = point_3d.PhysicalPoint3DSocketDef -from . import size_3d_socket -PhysicalSize3DSocketDef = size_3d_socket.PhysicalSize3DSocketDef +from . import size_3d +PhysicalSize3DSocketDef = size_3d.PhysicalSize3DSocketDef -from . import mass_socket -PhysicalMassSocketDef = mass_socket.PhysicalMassSocketDef +from . import mass +PhysicalMassSocketDef = mass.PhysicalMassSocketDef -from . import speed_socket -from . import accel_scalar_socket -from . import force_scalar_socket -PhysicalSpeedSocketDef = speed_socket.PhysicalSpeedSocketDef -PhysicalAccelScalarSocketDef = accel_scalar_socket.PhysicalAccelScalarSocketDef -PhysicalForceScalarSocketDef = force_scalar_socket.PhysicalForceScalarSocketDef +from . import speed +from . import accel_scalar +from . import force_scalar +PhysicalSpeedSocketDef = speed.PhysicalSpeedSocketDef +PhysicalAccelScalarSocketDef = accel_scalar.PhysicalAccelScalarSocketDef +PhysicalForceScalarSocketDef = force_scalar.PhysicalForceScalarSocketDef -from . import pol_socket -PhysicalPolSocketDef = pol_socket.PhysicalPolSocketDef +from . import pol +PhysicalPolSocketDef = pol.PhysicalPolSocketDef -from . import freq_socket -PhysicalFreqSocketDef = freq_socket.PhysicalFreqSocketDef +from . import freq +PhysicalFreqSocketDef = freq.PhysicalFreqSocketDef BL_REGISTER = [ - *unit_system_socket.BL_REGISTER, + *unit_system.BL_REGISTER, - *time_socket.BL_REGISTER, + *time.BL_REGISTER, - *angle_socket.BL_REGISTER, + *angle.BL_REGISTER, - *length_socket.BL_REGISTER, - *area_socket.BL_REGISTER, - *volume_socket.BL_REGISTER, + *length.BL_REGISTER, + *area.BL_REGISTER, + *volume.BL_REGISTER, - *point_3d_socket.BL_REGISTER, + *point_3d.BL_REGISTER, - *size_3d_socket.BL_REGISTER, + *size_3d.BL_REGISTER, - *mass_socket.BL_REGISTER, + *mass.BL_REGISTER, - *speed_socket.BL_REGISTER, - *accel_scalar_socket.BL_REGISTER, - *force_scalar_socket.BL_REGISTER, + *speed.BL_REGISTER, + *accel_scalar.BL_REGISTER, + *force_scalar.BL_REGISTER, - *pol_socket.BL_REGISTER, + *pol.BL_REGISTER, - *freq_socket.BL_REGISTER, + *freq.BL_REGISTER, ] diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/accel_scalar_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/accel_scalar.py similarity index 100% rename from blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/accel_scalar_socket.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/accel_scalar.py diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/angle_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/angle.py similarity index 100% rename from blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/angle_socket.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/angle.py diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/area_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/area.py similarity index 100% rename from blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/area_socket.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/area.py diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/force_scalar_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/force_scalar.py similarity index 100% rename from blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/force_scalar_socket.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/force_scalar.py diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/freq_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/freq.py similarity index 100% rename from blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/freq_socket.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/freq.py diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/length_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/length.py similarity index 100% rename from blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/length_socket.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/length.py diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/mass_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/mass.py similarity index 100% rename from blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/mass_socket.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/mass.py diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/point_3d_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/point_3d.py similarity index 100% rename from blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/point_3d_socket.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/point_3d.py diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/pol_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/pol.py similarity index 100% rename from blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/pol_socket.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/pol.py diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/size_3d_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/size_3d.py similarity index 100% rename from blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/size_3d_socket.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/size_3d.py diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/speed_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/speed.py similarity index 100% rename from blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/speed_socket.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/speed.py diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/time_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/time.py similarity index 100% rename from blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/time_socket.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/time.py diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/unit_system_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/unit_system.py similarity index 90% rename from blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/unit_system_socket.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/unit_system.py index dc985c0..3066c5b 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/unit_system_socket.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/unit_system.py @@ -152,19 +152,19 @@ class PhysicalUnitSystemBLSocket(base.MaxwellSimSocket): default=default_unit_key_for(ST.PhysicalForceScalar), update=(lambda self, context: self.sync_prop("unit_force_scalar", context)), ) - unit_accel_3d_vector: bpy.props.EnumProperty( + unit_accel_3d: bpy.props.EnumProperty( name="Accel3D Unit", description="Unit of 3D vector acceleration", - items=contract_units_to_items(ST.PhysicalAccel3DVector), - default=default_unit_key_for(ST.PhysicalAccel3DVector), - update=(lambda self, context: self.sync_prop("unit_accel_3d_vector", context)), + items=contract_units_to_items(ST.PhysicalAccel3D), + default=default_unit_key_for(ST.PhysicalAccel3D), + update=(lambda self, context: self.sync_prop("unit_accel_3d", context)), ) - unit_force_3d_vector: bpy.props.EnumProperty( + unit_force_3d: bpy.props.EnumProperty( name="Force3D Unit", description="Unit of 3D vector force", - items=contract_units_to_items(ST.PhysicalForce3DVector), - default=default_unit_key_for(ST.PhysicalForce3DVector), - update=(lambda self, context: self.sync_prop("unit_force_3d_vector", context)), + items=contract_units_to_items(ST.PhysicalForce3D), + default=default_unit_key_for(ST.PhysicalForce3D), + update=(lambda self, context: self.sync_prop("unit_force_3d", context)), ) unit_freq: bpy.props.EnumProperty( @@ -226,23 +226,18 @@ class PhysicalUnitSystemBLSocket(base.MaxwellSimSocket): col_row.label(text="Accel") col_row.prop(self, "unit_accel_scalar", text="") #col_row.prop(self, "unit_accel_2d_vector", text="") - col_row.prop(self, "unit_accel_3d_vector", text="") + col_row.prop(self, "unit_accel_3d", text="") col_row=col.row(align=True) col_row.label(text="Force") col_row.prop(self, "unit_force_scalar", text="") #col_row.prop(self, "unit_force_2d_vector", text="") - col_row.prop(self, "unit_force_3d_vector", text="") + col_row.prop(self, "unit_force_3d", text="") col_row=col.row(align=True) col_row.alignment = "EXPAND" col_row.label(text="Freq") col_row.prop(self, "unit_freq", text="") - - col_row=col.row(align=True) - col_row.alignment = "EXPAND" - col_row.label(text="Vac WL") - col_row.prop(self, "unit_vac_wl", text="") #################### # - Default Value @@ -270,11 +265,10 @@ class PhysicalUnitSystemBLSocket(base.MaxwellSimSocket): (ST.PhysicalSpeed, self.unit_speed), (ST.PhysicalAccelScalar, self.unit_accel_scalar), (ST.PhysicalForceScalar, self.unit_force_scalar), - (ST.PhysicalAccel3DVector, self.unit_accel_3d_vector), - (ST.PhysicalForce3DVector, self.unit_force_3d_vector), + (ST.PhysicalAccel3D, self.unit_accel_3d), + (ST.PhysicalForce3D, self.unit_force_3d), (ST.PhysicalFreq, self.unit_freq), - (ST.PhysicalVacWL, self.unit_vac_wl), ] } diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/volume_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/volume.py similarity index 100% rename from blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/volume_socket.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/volume.py diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/__init__.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/__init__.py index 1b718ad..f7ab146 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/__init__.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/__init__.py @@ -1,18 +1,18 @@ -from . import real_2d_vector_socket -from . import complex_2d_vector_socket -Real2DVectorSocketDef = real_2d_vector_socket.Real2DVectorSocketDef -Complex2DVectorSocketDef = complex_2d_vector_socket.Complex2DVectorSocketDef +from . import real_2d_vector +from . import complex_2d_vector +Real2DVectorSocketDef = real_2d_vector.Real2DVectorSocketDef +Complex2DVectorSocketDef = complex_2d_vector.Complex2DVectorSocketDef -from . import real_3d_vector_socket -from . import complex_3d_vector_socket -Real3DVectorSocketDef = real_3d_vector_socket.Real3DVectorSocketDef -Complex3DVectorSocketDef = complex_3d_vector_socket.Complex3DVectorSocketDef +from . import real_3d_vector +from . import complex_3d_vector +Real3DVectorSocketDef = real_3d_vector.Real3DVectorSocketDef +Complex3DVectorSocketDef = complex_3d_vector.Complex3DVectorSocketDef BL_REGISTER = [ - *real_2d_vector_socket.BL_REGISTER, - *complex_2d_vector_socket.BL_REGISTER, + *real_2d_vector.BL_REGISTER, + *complex_2d_vector.BL_REGISTER, - *real_3d_vector_socket.BL_REGISTER, - *complex_3d_vector_socket.BL_REGISTER, + *real_3d_vector.BL_REGISTER, + *complex_3d_vector.BL_REGISTER, ] diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/complex_2d_vector_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/complex_2d_vector.py similarity index 100% rename from blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/complex_2d_vector_socket.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/complex_2d_vector.py diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/complex_3d_vector_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/complex_3d_vector.py similarity index 100% rename from blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/complex_3d_vector_socket.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/complex_3d_vector.py diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/real_2d_vector_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/real_2d_vector.py similarity index 100% rename from blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/real_2d_vector_socket.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/real_2d_vector.py diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/real_3d_vector_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/real_3d_vector.py similarity index 100% rename from blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/real_3d_vector_socket.py rename to blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/real_3d_vector.py diff --git a/blender_maxwell/preferences.py b/blender_maxwell/preferences.py index d5647bb..2c07134 100644 --- a/blender_maxwell/preferences.py +++ b/blender_maxwell/preferences.py @@ -3,12 +3,12 @@ import bpy from .operators import types as operators_types class BlenderMaxwellAddonPreferences(bpy.types.AddonPreferences): - bl_idname = "blender_maxwell" - - def draw(self, context): - layout = self.layout - layout.operator(operators_types.BlenderMaxwellInstallDependencies, text="Install Dependencies") - layout.operator(operators_types.BlenderMaxwellUninstallDependencies, text="Uninstall Dependencies") + bl_idname = "blender_maxwell_preferences" + + def draw(self, context): + layout = self.layout + layout.operator(operators_types.BlenderMaxwellInstallDependencies, text="Install Dependencies") + layout.operator(operators_types.BlenderMaxwellUninstallDependencies, text="Uninstall Dependencies") #################### # - Blender Registration diff --git a/blender_maxwell/utils/analyze_geonodes.py b/blender_maxwell/utils/analyze_geonodes.py new file mode 100644 index 0000000..17ed1a8 --- /dev/null +++ b/blender_maxwell/utils/analyze_geonodes.py @@ -0,0 +1,27 @@ +import typing_extensions as typx + +import bpy + +INVALID_BL_SOCKET_TYPES = { + "NodeSocketGeometry", +} +def interface( + geo_nodes, + direc: typx.Literal["INPUT", "OUTPUT"], +): + """Returns 'valid' GeoNodes interface sockets, meaning that: + - The Blender socket type is not something invalid (ex. "Geometry"). + - The socket has a default value. + - The socket's direction (input/output) matches the requested direction. + """ + return { + interface_item_name: bl_interface_socket + for interface_item_name, bl_interface_socket in ( + geo_nodes.interface.items_tree.items() + ) + if all([ + bl_interface_socket.socket_type not in INVALID_BL_SOCKET_TYPES, + hasattr(bl_interface_socket, "default_value"), + bl_interface_socket.in_out == direc, + ]) + } diff --git a/blender_maxwell/utils/pydantic_sympy.py b/blender_maxwell/utils/pydantic_sympy.py index a71017f..1279ad0 100644 --- a/blender_maxwell/utils/pydantic_sympy.py +++ b/blender_maxwell/utils/pydantic_sympy.py @@ -33,8 +33,7 @@ class _SympyExpr: ) -> pyd_core_schema.CoreSchema: def validate_from_str(value: str) -> AllowedSympyExprs: if not isinstance(value, str): - msg = f"Value {value} is not a string" - raise ValueError(msg) + return value try: return sp.sympify(value) @@ -52,19 +51,14 @@ class _SympyExpr: return value - from_expr_schema = pyd_core_schema.chain_schema([ - pyd_core_schema.no_info_plain_validator_function(validate_from_expr), + sympy_expr_schema = pyd_core_schema.chain_schema([ pyd_core_schema.no_info_plain_validator_function(validate_from_str), + pyd_core_schema.no_info_plain_validator_function(validate_from_expr), + pyd_core_schema.is_instance_schema(AllowedSympyExprs), ]) return pyd_core_schema.json_or_python_schema( - json_schema=from_expr_schema, - python_schema=pyd_core_schema.union_schema( - [ - # check if it's an instance first before doing any further work - pyd_core_schema.is_instance_schema(AllowedSympyExprs), - from_expr_schema, - ] - ), + json_schema=sympy_expr_schema, + python_schema=sympy_expr_schema, serialization=pyd_core_schema.plain_serializer_function_ser_schema( lambda instance: str(instance) ),