Compare commits

...

3 Commits

14 changed files with 725 additions and 780 deletions

580
TODO.md
View File

@ -1,3 +1,9 @@
# Acute Tasks
- Implement Material Import for Maxim Data
- Move preview GN trees to the asset library.
# Nodes # Nodes
**LEGEND**: **LEGEND**:
- [-] Exists but doesn't quite work good enough. - [-] Exists but doesn't quite work good enough.
@ -6,430 +12,460 @@
- [?] Unsure whether we should do this. - [?] Unsure whether we should do this.
## Inputs ## Inputs
[x] Wave Constant - [x] Wave Constant
- [x] Implement export of frequency / wavelength array/range. - [x] Implement export of frequency / wavelength array/range.
[-] Unit System - [-] Unit System
- [ ] Implement presets, including "Tidy3D" and "Blender", shown in the label row. - [ ] Implement presets, including "Tidy3D" and "Blender", shown in the label row.
[ ] Constants / Scientific Constant - [ ] Constants / Scientific Constant
[x] Constants / Number Constant - [ ] Create `utils.sci_constants` to map `scipy` constants to `sympy` units.
[ ] Constants / Physical Constant - [ ] Utilize `utils.sci_constants` to make it easy for the user to select appropriate constants with two-layered dropdowns.
- [ ] Pol: Elliptical plot viz - [x] Constants / Number Constant
- [ ] Pol: Poincare sphere viz - [ ] Constants / Physical Constant
[x] Constants / Blender Constant - [ ] Pol: Elliptical viz as 2D plot.
- [ ] Pol: Poincare sphere viz as 3D GN.
- [x] Constants / Blender Constant
[x] Web / Tidy3D Web Importer - [-] Web / Tidy3D Web Importer
- [ ] Change to output only a `FilePath`, which can be plugged into a Tidy3D File Importer.
- [ ] Implement caching, such that the file will only download if the file doesn't already exist.
- [ ] Have a visual indicator for the current download status, with a manual re-download button.
[ ] File Import / JSON File Import - [ ] File Import / Material Import
- [ ] Dropdown to choose various supported JSON-sourced objects incl. - [ ] Dropdown to choose import format
[ ] File Import / Tidy3D File Import - [ ] File Import / Tidy3D File Import
- [ ] Implement HDF-based import of Tidy3D-exported object (which includes ex. mesh data and such) - [ ] HDF and JSON file support, with appropriate choice of loose output socket.
[ ] File Import / Array File Import - [ ] File Import / Array File Import
- [ ] Standardize 1D and 2D array loading/saving on numpy's savetxt with gzip enabled. - [ ] Standardize 1D and 2D array loading/saving on numpy's savetxt with gzip enabled.
- [ ] Implement datatype dropdown to guide format from disk, prefilled to detected.
- [ ] Implement unit system input to guide conversion from numpy data type. - [ ] Implement unit system input to guide conversion from numpy data type.
- [ ] Implement datatype dropdown to guide format from disk, prefilled to detected.
- [ ] Implement a LazyValue to provide a data path that avoids having to load massive arrays every time always. - [ ] Implement a LazyValue to provide a data path that avoids having to load massive arrays every time always.
## Outputs ## Outputs
[x] Viewer - [x] Viewer
- [ ] **BIG ONE**: Remove image preview when disabling plots. - [ ] Remove image preview when disabling plots.
- [ ] Declare Preview unit system on the viewer node. - [ ] BUG: CTRL+SHIFT+CLICK not on a node shows an error; should just do nothing.
- [ ] Either enforce singleton, or find a way to have several viewers at the same time. - [ ] Auto-enable 3D preview when creating.
- [ ] A setting that live-previews just a value. - [ ] Test/support multiple viewers at the same time.
- [ ] Pop-up multiline string print as alternative to console print. - [ ] Pop-up w/multiline string as alternative to console print.
- [x] Toggleable auto-plot, auto-3D-preview, auto-value-view, (?)auto-text-view.
[x] Web Export / Tidy3D Web Exporter - [x] Web Export / Tidy3D Web Exporter
- This is an extraordinarily nuanced node, and will need constant adjusting if it is to be robust.
- [ ] We need better ways of doing checks before uploading, like for monitor data size. Maybe a SimInfo node? - [ ] We need better ways of doing checks before uploading, like for monitor data size. Maybe a SimInfo node?
- [ ] Implement "new folder" feature w/popup operator.
- [ ] Implement "delete task" feature w/popup confirmation.
- [ ] We need to be able to "delete and re-upload" (or maybe just delete from the interface). - [ ] We need to be able to "delete and re-upload" (or maybe just delete from the interface).
- [x] Implement estimation of monitor storage
- [x] Implement cost estimation
- [?] Merge with the Tidy3D File Import (since both are working with HDFs; the web one only really does downloading too).
[x] File Export / JSON File Export - [x] File Export / JSON File Export
[ ] File Import / Tidy3D File Export - [ ] File Import / Tidy3D File Export
- [ ] Implement HDF-based export of Tidy3D-exported object (which includes ex. mesh data and such) - [ ] Implement HDF-based export of Tidy3D-exported object (which includes ex. mesh data and such)
[ ] File Export / Array File Export - [ ] Also JSON (but indicate somehow that ex. mesh data doesn't come along for the ride).
- [ ] File Export / Array File Export
- [ ] Implement datatype dropdown to guide format on disk. - [ ] Implement datatype dropdown to guide format on disk.
- [ ] Implement unit system input to guide conversion to numpy data type. - [ ] Implement unit system input to guide conversion to numpy data type.
- [ ] Standardize 1D and 2D array loading/saving on numpy's savetxt with gzip enabled. - [ ] Standardize 1D and 2D array loading/saving on numpy's savetxt with gzip enabled.
## Viz ## Viz
[x] Monitor Data Viz - [x] Monitor Data Viz
- [x] 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 (<https://docs.flexcompute.com/projects/tidy3d/en/latest/api/output_data.html>) - [x] 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 (<https://docs.flexcompute.com/projects/tidy3d/en/latest/api/output_data.html>)
- [ ] Project field values onto a plane object (managed) - [ ] Project field values onto a plane object (managed)
## Sources ## Sources
[x] Temporal Shapes / Gaussian Pulse Temporal Shape - [x] Temporal Shapes / Gaussian Pulse Temporal Shape
[x] Temporal Shapes / Continuous Wave Temporal Shape - [x] Temporal Shapes / Continuous Wave Temporal Shape
[ ] Temporal Shapes / Symbolic Temporal Shape - [ ] Temporal Shapes / Symbolic Temporal Shape
- [ ] Specify a Sympy function to generate appropriate array based on - [ ] Specify a Sympy function to generate appropriate array based on
[ ] Temporal Shapes / Array Temporal Shape - [ ] Temporal Shapes / Array Temporal Shape
[x] Point Dipole Source - [x] Point Dipole Source
- [ ] Consider a "real" mesh - the empty kind of gets stuck inside of the sim domain. - [ ] Use a viz mesh, not empty (empty doesn't play well with alpha hashing).
[-] Plane Wave Source - [-] Plane Wave Source
- [ ] **IMPORTANT**: Fix the math so that an actually valid construction emerges!!
- [x] Implement an oriented vector input with 3D preview. - [x] Implement an oriented vector input with 3D preview.
[ ] Uniform Current Source - [ ] **IMPORTANT**: Fix the math so that an actually valid construction emerges!!
[ ] TFSF Source - [ ] Uniform Current Source
- [ ] TFSF Source
[ ] Gaussian Beam Source - [ ] Gaussian Beam Source
[ ] Astigmatic Gaussian Beam Source - [ ] Astigmatic Gaussian Beam Source
[ ] Mode Source - [ ] Mode Source
[ ] Array Source / EH Array Source - [ ] Array Source / EH Array Source
[ ] Array Source / EH Equivilance Array Source - [ ] Array Source / EH Equiv Array Source
## Mediums ## Mediums
[x] Library Medium - [x] Library Medium
- [ ] Implement frequency range output - [ ] Implement frequency range output (listy)
[ ] PEC Medium - [ ] PEC Medium
[ ] Isotropic Medium - [ ] Isotropic Medium
[ ] Anisotropic Medium - [ ] Anisotropic Medium
[ ] Sellmeier Medium - [ ] Sellmeier Medium
[ ] Drude Medium - [ ] Drude Medium
[ ] Drude-Lorentz Medium - [ ] Drude-Lorentz Medium
[ ] Debye Medium - [ ] Debye Medium
[ ] Pole-Residue Medium - [ ] Pole-Residue Medium
[ ] Non-Linearity / `chi_3` Susceptibility Non-Linearity - [ ] Non-Linearity / `chi_3` Susceptibility Non-Linearity
[ ] Non-Linearity / Two-Photon Absorption Non-Linearity - [ ] Non-Linearity / Two-Photon Absorption Non-Linearity
[ ] Non-Linearity / Kerr Non-Linearity - [ ] Non-Linearity / Kerr Non-Linearity
[ ] Space/Time epsilon/mu Modulation - [ ] Space/Time epsilon/mu Modulation
## Structures ## Structures
[ ] BLObject Structure - [ ] BLObject Structure
[x] GeoNodes Structure - [x] GeoNodes Structure
- [x] Rewrite the `bl_socket_map.py` - [x] Rewrite the `bl_socket_map.py`
- [x] Use the modifier itself as memory, via the ManagedObj - [x] Use the modifier itself as memory, via the ManagedObj
- [x] Rewrite to use unit systems properly.
- [ ] Propertly map / implement Enum input sockets to the GN group.
- [ ] Implement a panel system, either based on native GN panels, or description parsing, or something like that.
- [?] 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. - [?] 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 Structure - [ ] Primitive Structures / Plane Structure
[x] Primitive Structures / Box Structure - [x] Primitive Structures / Box Structure
[x] Primitive Structures / Sphere Structure - [x] Primitive Structures / Sphere Structure
[ ] Primitive Structures / Cylinder Structure - [ ] Primitive Structures / Cylinder Structure
[ ] Primitive Structures / Ring Structure - [ ] Primitive Structures / Ring Structure
[ ] Primitive Structures / Capsule Structure - [ ] Primitive Structures / Capsule Structure
[ ] Primitive Structures / Cone Structure - [ ] Primitive Structures / Cone Structure
## Monitors ## Monitors
- **ALL**: "Steady-State" / "Time Domain" (only if relevant). - [x] E/H Field Monitor
- [x] Field Power Flux Monitor
[x] E/H Field Monitor - [ ] \epsilon Tensor Monitor
- [x] Monitor Domain as dropdown with Frequency or Time - [ ] Diffraction Monitor
- [x] Axis-aligned planar 2D (pixel) and coord-aligned box 3D (voxel).
[ ] Field Power Flux Monitor
- [ ] Monitor Domain as dropdown with Frequency or Time
- [ ] Axis-aligned planar 2D (pixel) and coord-aligned box 3D (voxel).
[ ] \epsilon Tensor Monitor
- [ ] Axis-aligned planar 2D (pixel) and coord-aligned box 3D (voxel).
[ ] Diffraction Monitor
- [ ] Axis-aligned planar 2D (pixel) - [ ] Axis-aligned planar 2D (pixel)
[ ] Projected E/H Field Monitor / Cartesian Projected E/H Field Monitor - [ ] Projected E/H Field Monitor / Cartesian Projected E/H Field Monitor
- [ ] Use to implement the metalens: <https://docs.flexcompute.com/projects/tidy3d/en/latest/notebooks/Metalens.html> - [ ] Use to implement the metalens: <https://docs.flexcompute.com/projects/tidy3d/en/latest/notebooks/Metalens.html>
[ ] Projected E/H Field Monitor / Angle Projected E/H Field Monitor - [ ] Projected E/H Field Monitor / Angle Projected E/H Field Monitor
[ ] Projected E/H Field Monitor / K-Space Projected E/H Field Monitor - [ ] Projected E/H Field Monitor / K-Space Projected E/H Field Monitor
- **TODO**: "Modal" solver monitoring (seems to be some kind of spatial+frequency feature, which an EM field can be decomposed into using a specially configured solver, which can be used to look for very particular kinds of effects by constraining investigations of a solver result to filter out everything that isn't these particular modes aka. features. Kind of a fourier-based redimensionalization, almost). - [ ] Modal Nodes
- Spatial+frequency feature monitoring. An EM field can be decomposed into using a specially configured solver, which can be used to look for very particular kinds of effects by constraining investigations of a solver result to filter out everything that isn't these particular modes aka. features. Kind of a fourier-based redimensionalization, almost).
## Simulations ## Simulations
[-] FDTDSim - [x] FDTDSim
[x] Sim Domain - [x] Sim Domain
- [ ] By-Medium batching of Structures when building the td.Simulation object, which can have significant performance implications. - [ ] By-Medium batching of Structures when building the td.Simulation object, which can have significant performance implications.
[x] Boundary Conds - [x] Boundary Conds
- [x] Rename from Bounds / BoundBox - [ ] Boundary Cond / PML Bound Face
[ ] Boundary Cond / PML Bound Face - [ ] Dropdown for "Normal" and "Stable"
- [ ] Implement dropdown for "Normal" and "Stable" - [ ] Boundary Cond / PEC Bound Face
[ ] Boundary Cond / PEC Bound Face - [ ] Boundary Cond / PMC Bound Face
[ ] Boundary Cond / PMC Bound Face - [ ] Boundary Cond / Bloch Bound Face
[ ] Boundary Cond / Bloch Bound Face - [ ] Boundary Cond / Periodic Bound Face
[ ] Boundary Cond / Periodic Bound Face - [ ] Boundary Cond / Absorbing Bound Face
[ ] Boundary Cond / Absorbing Bound Face
[ ] Sim Grid - [ ] Sim Grid
[ ] Sim Grid Axes / Auto Sim Grid Axis - [ ] Sim Grid Axes / Auto Sim Grid Axis
[ ] Sim Grid Axes / Manual Sim Grid Axis - [ ] Sim Grid Axes / Manual Sim Grid Axis
[ ] Sim Grid Axes / Uniform Sim Grid Axis - [ ] Sim Grid Axes / Uniform Sim Grid Axis
[ ] Sim Grid Axes / Array Sim Grid Axis - [ ] Sim Grid Axes / Array Sim Grid Axis
## Converters ## Converters
[ ] Math - [ ] Math
- [ ] Implement common operations w/secondary choice of socket type based on a custom internal data structure - [ ] Implement common operations w/secondary choice of socket type based on a custom internal data structure
- [ ] Implement angfreq/frequency/vacwl conversion. - [ ] Implement angfreq/frequency/vacwl conversion.
- [ ] Implement spectral math on SDs - [ ] Implement spectral math on SDs
- [ ] Implement easy derivation of ex. transmission and reflection. - [ ] Implement easy derivation of ex. transmission and reflection.
[ ] Separate - [ ] Separate
[x] Combine - [x] Combine
- [x] Implement concatenation of sim-critical socket types into their multi-type - [x] Implement concatenation of sim-critical socket types into their multi-type
# GeoNodes # GeoNodes
[ ] Tests / Monkey (suzanne deserves to be simulated, she may need manifolding up though :)) - [ ] Tests / Monkey (suzanne deserves to be simulated, she may need manifolding up though :))
[ ] Tests / Wood Pile - [ ] Tests / Wood Pile
[ ] Structures / Primitives / Plane - [ ] Structures / Primitives / Plane
[x] Structures / Primitives / Box - [x] Structures / Primitives / Box
[x] Structures / Primitives / Sphere - [x] Structures / Primitives / Sphere
[ ] Structures / Primitives / Cylinder - [ ] Structures / Primitives / Cylinder
[x] Structures / Primitives / Ring - [x] Structures / Primitives / Ring
[ ] Structures / Primitives / Capsule - [ ] Structures / Primitives / Capsule
[ ] Structures / Primitives / Cone - [ ] Structures / Primitives / Cone
[ ] Structures / Arrays / Square - [ ] Structures / Arrays / Square
[ ] Structures / Arrays / Square-Hole - [ ] Structures / Arrays / Square-Hole
[ ] Structures / Arrays / Cyl - [ ] Structures / Arrays / Cyl
[ ] Structures / Arrays / Cyl-Hole - [ ] Structures / Arrays / Cyl-Hole
[x] Structures / Arrays / Box - [x] Structures / Arrays / Box
[x] Structures / Arrays / Sphere - [x] Structures / Arrays / Sphere
[ ] Structures / Arrays / Cylinder - [ ] Structures / Arrays / Cylinder
[-] Structures / Arrays / Ring - [-] Structures / Arrays / Ring
[ ] Structures / Arrays / Capsule - [ ] Structures / Arrays / Capsule
[ ] Structures / Arrays / Cone - [ ] Structures / Arrays / Cone
[ ] Array / Square Array **NOTE: Ring and cylinder** - [ ] Array / Square Array **NOTE: Ring and cylinder**
[ ] Array / Hex Array **NOTE: Ring and cylinder** - [ ] Array / Hex Array **NOTE: Ring and cylinder**
[ ] Hole Array / Square Hole Array: Takes a primitive hole shape. - [ ] Hole Array / Square Hole Array: Takes a primitive hole shape.
[ ] Hole Array / Hex Hole Array: Takes a primitive hole shape. - [ ] Hole Array / Hex Hole Array: Takes a primitive hole shape.
[ ] Cavity Array / Hex Array w/ L-Cavity - [ ] Cavity Array / Hex Array w/ L-Cavity
[ ] Cavity Array / Hex Array w/ H-Cavity - [ ] Cavity Array / Hex Array w/ H-Cavity
[ ] Crystal Sphere Lattice / Sphere FCC Array - [ ] Crystal Sphere Lattice / Sphere FCC Array
[ ] Crystal Sphere Lattice / Sphere BCC Array - [ ] Crystal Sphere Lattice / Sphere BCC Array
# Benchmark / Example Sims # Benchmark / Example Sims
[ ] Research-Grade Experiment - [ ] Research-Grade Experiment
- Membrane 15nm thickness suspended in air - Membrane 15nm thickness suspended in air
- Square lattice of holes period 900nm (900nm between each hole, air inside holes) - Square lattice of holes period 900nm (900nm between each hole, air inside holes)
- Holes square radius 100nm - Holes square radius 100nm
- Square lattice - Square lattice
- Analysis of transmission - Analysis of transmission
- Guided mode resonance - Guided mode resonance
[ ] Tunable Chiral Metasurface <https://docs.flexcompute.com/projects/tidy3d/en/latest/notebooks/TunableChiralMetasurface.html> - [ ] Tunable Chiral Metasurface <https://docs.flexcompute.com/projects/tidy3d/en/latest/notebooks/TunableChiralMetasurface.html>
# Sockets # Sockets
## Basic ## Basic
[x] Any - [x] Any
[x] Bool - [x] Bool
[x] String - [x] String
- [ ] Rename from "Text" - [x] File Path
[x] File Path - [x] Color
[x] Color
## Number ## Number
[x] Integer - [x] Integer
[x] Rational - [x] Rational
- [ ] Implement constrained SympyExpr check for Rational. - [x] Real
[x] Real
- [ ] Implement min/max for ex. 0..1 factor support. - [ ] Implement min/max for ex. 0..1 factor support.
- [ ] Implement constrained SympyExpr check for Rational. - [x] Complex
[x] Complex
## Blender ## Blender
[x] Object - [x] Object
- [ ] Implement default SocketDef object name - [ ] Implement default object name in SocketDef
[x] Collection - [x] Collection
- [ ] Implement default SocketDef collection name - [ ] Implement default collection name in SocketDef
[x] Image - [x] Image
- [ ] Implement default SocketDef image name - [ ] Implement default image name in SocketDef
[x] GeoNodes - [x] GeoNodes
- [ ] Implement default SocketDef geonodes name - [ ] Implement default SocketDef geonodes name
[x] Text - [x] Text
- [ ] Implement default SocketDef object name - [ ] Implement default SocketDef object name
## Maxwell ## Maxwell
[x] Bound Conds - [x] Bound Conds
[ ] Bound Cond - [ ] Bound Cond
[x] Medium - [x] Medium
[ ] Medium Non-Linearity - [ ] Medium Non-Linearity
[x] Source - [x] Source
[ ] Temporal Shape - [ ] Temporal Shape
- [ ] Sane-default pulses for easy access. - [ ] Sane-default pulses for easy access.
[ ] Structure - [ ] Structure
[ ] Monitor - [ ] Monitor
[ ] FDTD Sim - [ ] FDTD Sim
[ ] Sim Domain - [ ] Sim Domain
- [?] Toggleable option to sync the simulation time duration to the scene end time (how to handle FPS vs time-step? Should we adjust the FPS such that there is one time step per frame, while keeping the definition of "second" aligned to a unit system?) - [?] Toggleable option to push-sync the simulation time duration to the scene end time (how to handle FPS vs time-step? Should we adjust the FPS such that there is one time step per frame, while keeping the definition of "second" aligned to the Blender unit system?)
[ ] Sim Grid - [ ] Sim Grid
[ ] Sim Grid Axis - [ ] Sim Grid Axis
[ ] Simulation Data - [ ] Simulation Data
## Tidy3D ## Tidy3D
[x] Cloud Task - [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. - [ ] 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 ## Physical
[x] Unit System - [x] Unit System
- [ ] Implement more comprehensible UI; honestly, probably with the new panels (<https://developer.blender.org/docs/release_notes/4.1/python_api/>) - [ ] Presets for Blender and Tidy3D
- [ ] Dropdowns in the socket UI
[x] Time - [x] Time
[x] Angle - [x] Angle
[ ] Solid Angle (steradian) - [ ] Remove superfluous units.
- [ ] Solid Angle (steradian)
[x] Frequency (hertz) - [x] Frequency (hertz)
[ ] Angular Frequency (`rad*hertz`) - [ ] Angular Frequency (`rad*hertz`)
### Cartesian ### Cartesian
[x] Length - [x] Length
[x] Area - [x] Area
[x] Volume - [x] Volume
[ ] Point 1D - [ ] Point 1D
[ ] Point 2D - [ ] Point 2D
[x] Point 3D - [x] Point 3D
[ ] Size 2D - [ ] Size 2D
[x] Size 3D - [x] Size 3D
[ ] Rotation 3D - [ ] Rotation 3D
- [ ] Implement Euler methods - [ ] Implement Euler methods
- [ ] Implement Quaternion methods - [ ] Implement Quaternion methods
### Mechanical ### Mechanical
[ ] Mass - [ ] Mass
[x] Speed - [x] Speed
[ ] Velocity 3D - [ ] Velocity 3D
[x] Acceleration Scalar - [x] Acceleration Scalar
[ ] Acceleration 3D - [ ] Acceleration 3D
[x] Force Scalar - [x] Force Scalar
[ ] Force 3D - [ ] Force 3D
[ ] Pressure - [ ] Pressure
### Energy ### Energy
[ ] Energy (joule) - [ ] Energy (joule)
[ ] Power (watt) - [ ] Power (watt)
[ ] Temperature - [ ] Temperature
### Electrodynamical ### Electrodynamical
[ ] Current (ampere) - [ ] Current (ampere)
[ ] Current Density 3D - [ ] Current Density 3D
[ ] Charge (coulomb) - [ ] Charge (coulomb)
[ ] Voltage (volts) - [ ] Voltage (volts)
[ ] Capacitance (farad) - [ ] Capacitance (farad)
[ ] Resistance (ohm) - [ ] Resistance (ohm)
[ ] Electric Conductance (siemens) - [ ] Electric Conductance (siemens)
[ ] Magnetic Flux (weber) - [ ] Magnetic Flux (weber)
[ ] Magnetic Flux Density (tesla) - [ ] Magnetic Flux Density (tesla)
[ ] Inductance (henry) - [ ] Inductance (henry)
[ ] Electric Field 3D (`volt*meter`) - [ ] Electric Field 3D (`volt*meter`)
[ ] Magnetic Field 3D (tesla) - [ ] Magnetic Field 3D (tesla)
### Luminal ### Luminal
[ ] Luminous Intensity (candela) - [ ] Luminous Intensity (candela)
[ ] Luminous Flux (lumen) - [ ] Luminous Flux (lumen)
[ ] Illuminance (lux) - [ ] Illuminance (lux)
### Optical ### Optical
[ ] Jones Polarization - [ ] Jones Polarization
[ ] Polarization (Stokes) - [ ] Polarization (Stokes)
# Style # Internal / Architecture
[x] Rethink the meaning of color and shapes in node sockets, including whether dynamic functionality is needed when it comes to socket shape (ex. it might be nice to know whether a socket is array-like or uses units).
# Architecture
## CRITICAL ## CRITICAL
With these things in place, we're in tip top shape: - [ ] License header UI for MaxwellSimTrees, to clarify the AGPL-compatible potentially user-selected license that trees must be distributed under.
[ ] Linkability / Appendability of library GeoNodes groups, including being able to semantically ask for a particular GeoNodes tree without 'magic strings' that are entirely end-user-file dependent, is completely critical. especially - [ ] Document the node tree cache semantics thoroughly; it's a VERY nuanced piece of logic, and its invariants may not survive Blender versions / the author's working memory
[ ] Simplify the boilerplate needed to add a particular 3D preview driven by the input sockets of a particular GeoNodes group. It's currently hard for all the wrong reasons, and greatly halts our velocity in developing useful 3D previews of any/everything. - [ ] Start standardizing nodes/sockets w/individualized SemVer
[ ] Finalize Viewer node unit systems. - Perhaps keep node / socket versions in a property, so that trying to load an incompatible major version hop can error w/indicator of where to find a compatible `blender_maxwell` version.
[ ] Introduce a simplified (maybe caching) method of translating sympy-enabled values, ex. 'Center', into values for external use (ex. in a Tidy3D object or in a Blender preview) based on
[ ] Abstract the actual unit system dict-like data structure out from the UnitSystem socket. ## Documentation
[ ] Ship the addon with libraries of GeoNodes groups (with NO dependency on the addon), which are linked (internal use) or appended (end-user-selected structures) when needed for previewing. - [ ] Make all modules available
- I don't know that library overrides are the correct approach when it comes to structures used by the end-user. It's extremely easy to make a change to a library structure (or have one made for us by a Blender update!) that completely wrecks all end-user simulations that use it, or override it. By appending, the structure becomes 'part of' the user's simulation, which also makes it quite a bit easier for the user to alter (even drastically) for their own needs. - [ ] Publish documentation site.
[ ] License header UI for MaxwellSimTrees, to clarify the AGPL-compatible potentially user-selected license that trees must be distributed under. - [ ] Initial user guides w/pictures.
- [ ] Comb through and finish `__doc__`s.
## Performance
- [ ] The GN value pushing currently does an expensive from-sympy conversion for all GN attributes, making it very slow.
- Generally, the issue is that we can't compare the pushed value to the existing value without a conversion.
- Also, sympy expressions can't be hashed (by default), and `str()` may be just as slow, so a simple `@lru_cache` is no good.
- **One approach** is a `HashableSympyExpr` class, which would use ex. atoms and values in `SympyExpr` to let us `@lru_cache` the sympy conversion.
- **Another approach** is to "just" make the `scale_to_unit` function faster (we should profile).
- Presumably, the scaling factors between units could be eagerly cached, then the numerical part of the expression could be used to avoid `convert_to` calls.
- Without the list format of `(np.array, spu.Quantity)`, `sp.Matrix` can't be performantly handled like this.
## Style
- [ ] Color of nodes should be a nice blue, eh?
## Registration and Contracts ## Registration and Contracts
[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.
[ ] Refactor the node category code; it's ugly.
- It's maybe not that easy. And it seems to work with surprising reliability. Leave it alone for now! - It's maybe not that easy. And it seems to work with surprising reliability. Leave it alone for now!
[?] Would be nice with some kind of indicator somewhere to help set good socket descriptions when using geonodes and wanting units. - [?] Would be nice with some kind of indicator somewhere to help set good socket descriptions when making geonodes.
## Managed Objects ## Managed Objects
[x] Implement modifier support on the managed BL object, with special attention paid to the needs of the GeoNodes socket. - [ ] Implement ManagedEmpty
- [x] Implement preview toggling too, ex. using the relevant node tree collections - [ ] Implement image-based empty connected to an image (which is managed by a different ManagedImage owned by the same node instance)
- 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 ManagedVol
[ ] Implement loading the xarray-defined voxels into OpenVDB, saving it, and loading it as a managed BL object with the volume setting. - [ ] 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 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). - [ ] 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 ## Utils or Services
[x] Dedicated module for managing the interaction with the tidy3d cloud, to help nuke all the random caches out of existance. - [ ] Document the `tdcloud` service thoroughly and open a GitHub discussion about `td.web` shortcomings.
## Node Base Class ## Node Base Class
[ ] Dedicated `draw_preview`-type draw functions for plot customizations. - [ ] Implement a new socket type for preview-only parameters
- [ ] For now, previewing isn't something I think should always be part of the node - perhaps we can use panels to keep settings, but also allow preview options to toggleably add a loose socket for driven input. - [ ] When used, the node base class should expose a toggle
[ ] Custom `@cache`/`@lru_cache`/`@cached_property` which caches by instance ID (possibly based on `beartype` or `pydantic`). - [ ] Instead of mangling props, we can instead reuse all of the socket-based code, while also enabling composability of previews.
[ ] 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 - [ ] Custom `@cache`/`@lru_cache`/`@cached_property` which caches by instance ID (possibly based on `beartype` or `pydantic`).
[ ] 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). - The problem to solve is performance vs. persistence.
[?] Mechanism for dynamic names (ex. "Library Medium" becoming "Au Medium") - [ ]
[-] Mechanism for selecting a blender object managed by a particular node. - [ ] Implement by-category sorting of loose sockets with 'move' method on `node.inputs`/`node.outputs`.
[ ] Mechanism for ex. specially coloring a node that is currently participating in the preview. - Currently order is not guaranteed
[ ] Custom callbacks when deleting a node (in `free()`), to ex. delete all previews with the viewer node. - [ ] 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
## Events
- [-] 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 ## Socket Base Class
[x] 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. - [ ] Second-generation listy, based on a `DataFlowKind.ValueListy`, `DataFlowKind.ValueRange`, etc. to encode the presence of special logic.
- [-] Implement a shape-selector, with a dropdown for dimensionality and an appropriate `IntegerVectorProperty` for each kind of shape (supporting also straight-up inf), which is declared to the node that supports array-likeness so it can decide how exactly to expose properties in the array-like context of things. - This is key to allow special handling, as "just give me a `list[]` of `sympy` objects" is an exceptionally non-performant and brittle thing.
- So far, the 'method of generation' seems pretty unique to each kind of socket. It's fully manual right now, but we could let the user select from standard UIs ex. 'generate with linspace', 'generate with logspace', etc. . - [ ] Implement capability system, which defaults to exactly matching the type.
[ ] Make `to_socket`s no-consent to new links from `from_socket`s of differing type (we'll see if this controls the typing story enough for now, and how much we'll need capabilities in the long run) - [ ] Make `to_socket`s no-consent to new links from `from_socket`s of incompatible Capability.
- [?] Alternatively, do exactly this, but have it be the default behavior of the capability system, to preserve the ability to extend it to more exotic capability needs. - [ ] Add Capabilities needed mainly in cases where we need `Any` currently.
- [?] Alternatively, reject non matching link types, and red-mark non matching capabilities?
## Many Nodes ## Many Nodes
[ ] Implement LazyValue stuff, including LazyParamValue on a new class of constant-like input nodes that really just emit ex. sympy variables. - [ ] Implement "Steady-State" / "Time Domain" on all relevant Monitor nodes
[?] Require a Unit System for nodes that construct Tidy3D objects - [?] Dynamic `bl_label` where appropriate (ex. "Library Medium" becoming "Au Medium")
[ ] Medium Features - [ ] Implement LazyValue, including LazyParamValue on a new class of constant-like input nodes that really just emit ex. sympy variables.
- [ ] Medium Features
- [ ] Accept spatial field. Else, spatial uniformity. - [ ] Accept spatial field. Else, spatial uniformity.
- [ ] Accept non-linearity. Else, linear. - [ ] Accept non-linearity. Else, linear.
- [ ] Accept space-time modulation. Else, static. - [ ] Accept space-time modulation. Else, static.
[ ] Modal Features - [ ] Modal Features
- [ ] ModeSpec, for use by ModeSource, ModeMonitor, ModeSolverMonitor. Data includes ModeSolverData, ModeData, ScalarModeFieldDataArray, ModeAmpsDataArray, ModeIndexDataArray, ModeSolver. - [ ] ModeSpec, for use by ModeSource, ModeMonitor, ModeSolverMonitor. Data includes ModeSolverData, ModeData, ScalarModeFieldDataArray, ModeAmpsDataArray, ModeIndexDataArray, ModeSolver.
## Many Sockets
- [ ] Implement constrained SympyExpr checks all over the place.
## Development Tooling ## Development Tooling
[x] Implement `rye` support - [ ] Pass a `mypy` check
[x] Setup neovim to be an ideal editor - [ ] Pass all `ruff` checks, including `__doc__` availability.
- [ ] Implement `pre-commit.
- [ ] Add profiling support, so we can properly analyze performance characteristics.
- Without a test harness, or profile-while-logging, there may be undue noise in our analysis.
- [ ] Simple `pytest` harnesses for unit testing of nodes, sockets.
- Start with the low-hanging-fruit stuff. Eventually, work towards wider code coverage w/headless Blender.
## Version Churn ## Version Churn
[ ] Implement real StrEnum sockets, since they appear in py3.11 - [ ] Migrate to StrEnum sockets (py3.11).
[x] Think about implementing new panels where appropriate (<https://developer.blender.org/docs/release_notes/4.1/python_api/>) - [ ] Implement drag-and-drop node-from-file via bl4.1 file handler API.
[ ] Think about using the new bl4.1 file handler API to enable drag and drop creation of appropriate nodes (for importing files without hassle). - [-] Start thinking about ways around `__annotations__` hacking.
[ ] Keep an eye on our manual `__annotations__` hacking; python 3.13 is apparently fucking with it. - [-] Prepare for for multi-input sockets (bl4.2)
[x] Plan for multi-input sockets <https://projects.blender.org/blender/blender/commit/14106150797a6ce35e006ffde18e78ea7ae67598> (for now, just use the "Combine" node and have seperate socket types for both). - PR has been merged: <https://projects.blender.org/blender/blender/commit/14106150797a6ce35e006ffde18e78ea7ae67598> (for now, just use the "Combine" node and have seperate socket types for both).
- There are big benefits with respect to previewability to using combine nodes; we shouldn't play the multi-input game for now. - The `Combine` node has its own benefits, including previewability of "only structures". Multi-input would mainly be a kind of shorthand in specific cases (like input to the `Combine` node?)
[ ] Keep an eye out for volume geonodes in 4.2 (July 16, 2024), which will better allow for more complicated volume processing (we might still want/need the jax based stuff after, but let's keep it minimal just in case) - [-] Prepare for volume geonodes (bl4.2; July 16, 2024)
- Will allow for actual volume processing in GeoNodes.
- We might still want/need the jax based stuff after; volume geonodes aren't finalized.
## Packaging ## Packaging
[x] Allow specifying custom dir for keeping pip dependencies, so we can unify prod and dev (currently we hard-code a dev dependency path). - [ ] Popup to install dependencies after UI is available (possibly with the help of the `draw()` function of the `InstallPyDeps` operator)
[x] Refactor top-level `__init__.py` to check dependencies first. If not everything is available, it should only register a minimal addon; specifically, a message telling the user that the addon requires additional dependencies (list which), and the button to install them. When the installation is done, re-check deps and register the rest of the addon. - [ ] Use a Modal and multiline-text-like construction to print `pip install` as we install dependencies, so that the user has an idea that something is happening.
[ ] Popup to install dependencies after UI is available (possibly with the help of the `draw()` function of the `InstallPyDeps` operator) - [ ] Test lockfile platform-agnosticism on Windows
[ ] Use a Modal and multiline-text-like construction to print `pip install` as we install dependencies, so that the user has an idea that something is happening.
[ ] Test on Windows
## Node Tree Cache Semantics
# TIDY3D BUGS # BUGS
- Directly running `SimulationTask.get()` is bugged - it doesn't return some fields, including `created_at`. Listing tasks by folder is not broken. We're trying to do our part by reporting bugs we find!
This is where we keep track of them for now.
## Blender Bugs
Reported:
- (SOLVED) <https://projects.blender.org/blender/blender/issues/119664>
Unreported:
- The `__mp_main__` bug.
# Tidy3D bugs
Unreported:
- Directly running `SimulationTask.get()` is missing fields - it doesn't return some fields, including `created_at`. Listing tasks by folder is not broken.

2
doc/.gitignore vendored
View File

@ -3,4 +3,4 @@ _site
_sidebar.yml _sidebar.yml
_site _site
objects.json objects.json
reference pydocs

View File

@ -1,11 +1,38 @@
project: project:
type: website type: website
# Website Configuration
website:
page-footer: "Copyright 2024, Sofus Albert Høgsbro Rose"
navbar:
left:
- file: index.qmd
text: Home
- file: pydocs/index.qmd
text: Reference
tools:
- icon: github
menu:
- text: Source Code
url: https://github.com/so-rose/blender_maxwell/
- text: Report a Bug
url: https://github.com/so-rose/blender_maxwell/issues/new/choose
# Auto-Generated Metadata
metadata-files: metadata-files:
- _sidebar.yml # Sidebar for /pydocs Paths
- pydocs/_sidebar.yml
####################
# - quartodoc - Autogenerated Python Docs
####################
quartodoc: quartodoc:
# Output
dir: pydocs
#out_index: _api_index.qmd
sidebar: pydocs/_sidebar.yml
# Python Package # Python Package
source_dir: ../src source_dir: ../src
package: blender_maxwell package: blender_maxwell
@ -13,17 +40,150 @@ quartodoc:
# Style # Style
style: pkgdown style: pkgdown
title: Package Reference title: "Blender Maxwell"
# Write Sidebar Data to Dedicated Metadata File # Options
sidebar: _sidebar.yml options:
include_private: true
include_empty: true
include_attributes: true
signature_name: "short"
sections: sections:
- title: Blender Maxwell API - title: "`scripts`"
desc: Root package for the Blender Maxwell addon desc: Build/packaging scripts for developing and publishing the addon.
package: scripts
contents: contents:
- register - name: info
- unregister children: embedded
- name: pack
children: embedded
- name: dev
children: embedded
- name: bl_delete_addon
children: embedded
- name: bl_install_addon
children: embedded
- title: "`bl_maxwell`"
desc: Root package for the addon.
contents:
- name: info
children: embedded
- name: preferences - name: preferences
children: embedded children: embedded
- name: registration
children: embedded
- subtitle: "`bl_maxwell.assets`"
desc: Blender assets bundled w/Blender Maxwell
contents:
- name: assets
children: embedded
- name: assets.import_geonodes
children: embedded
- subtitle: "`bl_maxwell.nodeps`"
desc: No-Dependency
contents:
- name: operators
children: embedded
- subtitle: "`bl_maxwell.utils`"
desc: Utilities wo/shared global state.
contents:
- name: utils.analyze_geonodes
children: embedded
- name: utils.blender_type_enum
children: embedded
- name: utils.extra_sympy_units
children: embedded
- name: utils.logger
children: embedded
- name: utils.pydantic_sympy
children: embedded
- subtitle: "`bl_maxwell.services`"
desc: Utilities w/shared global state.
contents:
- name: services.tdcloud
children: embedded
- subtitle: "`bl_maxwell.operators`"
desc: General Blender operators.
contents:
- name: operators.bl_append
children: embedded
- name: operators.connect_viewer
children: embedded
# Node Tree
- title: "`..maxwell_sim_nodes`"
desc: Maxwell Simulation Design/Viz Node Tree.
package: blender_maxwell.node_trees.maxwell_sim_nodes
contents:
- name: bl_socket_map
children: embedded
- name: categories
children: embedded
- name: node_tree
children: embedded
- subtitle: "`contracts`"
desc: Constants and interfaces for identifying resources.
package: blender_maxwell.node_trees.maxwell_sim_nodes.contracts
contents:
# General
- name: bl
children: embedded
- name: data_flows
children: embedded
- name: icons
children: embedded
- name: trees
children: embedded
# Managed Objects
- name: managed_obj_type
children: embedded
# Nodes
- name: node_types
children: embedded
- name: node_cats
children: embedded
- name: node_cat_labels
children: embedded
# Sockets
- name: socket_types
children: embedded
- name: socket_colors
children: embedded
- name: socket_from_bl_desc
children: embedded
- name: socket_from_bl_direct
children: embedded
- name: socket_shapes
children: embedded
- name: socket_units
children: embedded
- name: unit_systems
children: embedded
- subtitle: "`managed_objs`"
desc: Maxwell Simulation Design/Viz Node Tree
package: blender_maxwell.node_trees.maxwell_sim_nodes.managed_objs
contents:
- name: managed_bl_collection
children: embedded
- name: managed_bl_empty
children: embedded
- name: managed_bl_image
children: embedded
- name: managed_bl_mesh
children: embedded
- name: managed_bl_modifier
children: embedded

View File

@ -12,6 +12,7 @@ from ..utils import logger
log = logger.get(__name__) log = logger.get(__name__)
ImportMethod: typ.TypeAlias = typx.Literal['append', 'link']
BLOperatorStatus: typ.TypeAlias = set[ BLOperatorStatus: typ.TypeAlias = set[
typx.Literal['RUNNING_MODAL', 'CANCELLED', 'FINISHED', 'PASS_THROUGH', 'INTERFACE'] typx.Literal['RUNNING_MODAL', 'CANCELLED', 'FINISHED', 'PASS_THROUGH', 'INTERFACE']
] ]
@ -25,40 +26,143 @@ class GeoNodes(enum.StrEnum):
The value of this StrEnum is both the name of the .blend file containing the GeoNodes group, and of the GeoNodes group itself. The value of this StrEnum is both the name of the .blend file containing the GeoNodes group, and of the GeoNodes group itself.
""" """
# Node Previews
## Input
InputConstantPhysicalPol = '_input_constant_physical_pol'
## Source
SourcePointDipole = '_source_point_dipole'
SourcePlaneWave = '_source_plane_wave'
SourceUniformCurrent = '_source_uniform_current'
SourceTFSF = '_source_tfsf'
SourceGaussianBeam = '_source_gaussian_beam'
SourceAstigmaticGaussianBeam = '_source_astigmatic_gaussian_beam'
SourceMode = '_source_mode'
SourceEHArray = '_source_eh_array'
SourceEHEquivArray = '_source_eh_equiv_array'
## Structure
StructurePrimitivePlane = '_structure_primitive_plane'
StructurePrimitiveBox = '_structure_primitive_box'
StructurePrimitiveSphere = '_structure_primitive_sphere'
StructurePrimitiveCylinder = '_structure_primitive_cylinder'
StructurePrimitiveRing = '_structure_primitive_ring'
StructurePrimitiveCapsule = '_structure_primitive_capsule'
StructurePrimitiveCone = '_structure_primitive_cone'
## Monitor
MonitorEHField = '_monitor_eh_field'
MonitorFieldPowerFlux = '_monitor_field_power_flux'
MonitorEpsTensor = '_monitor_eps_tensor'
MonitorDiffraction = '_monitor_diffraction'
MonitorProjCartEHField = '_monitor_proj_eh_field'
MonitorProjAngEHField = '_monitor_proj_ang_eh_field'
MonitorProjKSpaceEHField = '_monitor_proj_k_space_eh_field'
## Simulation
SimulationSimDomain = '_simulation_sim_domain'
SimulationBoundConds = '_simulation_bound_conds'
SimulationBoundCondPML = '_simulation_bound_cond_pml'
SimulationBoundCondPEC = '_simulation_bound_cond_pec'
SimulationBoundCondPMC = '_simulation_bound_cond_pmc'
SimulationBoundCondBloch = '_simulation_bound_cond_bloch'
SimulationBoundCondPeriodic = '_simulation_bound_cond_periodic'
SimulationBoundCondAbsorbing = '_simulation_bound_cond_absorbing'
SimulationSimGrid = '_simulation_sim_grid'
SimulationSimGridAxisAuto = '_simulation_sim_grid_axis_auto'
SimulationSimGridAxisManual = '_simulation_sim_grid_axis_manual'
SimulationSimGridAxisUniform = '_simulation_sim_grid_axis_uniform'
SimulationSimGridAxisArray = '_simulation_sim_grid_axis_array'
# Structures
## Primitives
PrimitiveBox = 'box' PrimitiveBox = 'box'
PrimitiveRing = 'ring' PrimitiveRing = 'ring'
PrimitiveSphere = 'sphere' PrimitiveSphere = 'sphere'
# GeoNodes Path Mapping # GeoNodes Paths
GN_PRIMITIVES_PATH = info.PATH_ASSETS / 'geonodes' / 'primitives' ## Internal
GN_INTERNAL_PATH = info.PATH_ASSETS / 'internal' / 'primitives'
GN_INTERNAL_INPUTS_PATH = GN_INTERNAL_PATH / 'input'
GN_INTERNAL_SOURCES_PATH = GN_INTERNAL_PATH / 'source'
GN_INTERNAL_STRUCTURES_PATH = GN_INTERNAL_PATH / 'structure'
GN_INTERNAL_MONITORS_PATH = GN_INTERNAL_PATH / 'monitor'
GN_INTERNAL_SIMULATIONS_PATH = GN_INTERNAL_PATH / 'simulation'
## Structures
GN_STRUCTURES_PATH = info.PATH_ASSETS / 'structures'
GN_STRUCTURES_PRIMITIVES_PATH = GN_STRUCTURES_PATH / 'primitives'
GN_PARENT_PATHS: dict[GeoNodes, Path] = { GN_PARENT_PATHS: dict[GeoNodes, Path] = {
GeoNodes.PrimitiveBox: GN_PRIMITIVES_PATH, # Node Previews
GeoNodes.PrimitiveRing: GN_PRIMITIVES_PATH, ## Input
GeoNodes.PrimitiveSphere: GN_PRIMITIVES_PATH, GeoNodes.InputConstantPhysicalPol: GN_INTERNAL_INPUTS_PATH,
## Source
GeoNodes.SourcePointDipole: GN_INTERNAL_SOURCES_PATH,
GeoNodes.SourcePlaneWave: GN_INTERNAL_SOURCES_PATH,
GeoNodes.SourceUniformCurrent: GN_INTERNAL_SOURCES_PATH,
GeoNodes.SourceTFSF: GN_INTERNAL_SOURCES_PATH,
GeoNodes.SourceGaussianBeam: GN_INTERNAL_SOURCES_PATH,
GeoNodes.SourceAstigmaticGaussianBeam: GN_INTERNAL_SOURCES_PATH,
GeoNodes.SourceMode: GN_INTERNAL_SOURCES_PATH,
GeoNodes.SourceEHArray: GN_INTERNAL_SOURCES_PATH,
GeoNodes.SourceEHEquivArray: GN_INTERNAL_SOURCES_PATH,
## Structure
GeoNodes.StructurePrimitivePlane: GN_INTERNAL_STRUCTURES_PATH,
GeoNodes.StructurePrimitiveBox: GN_INTERNAL_STRUCTURES_PATH,
GeoNodes.StructurePrimitiveSphere: GN_INTERNAL_STRUCTURES_PATH,
GeoNodes.StructurePrimitiveCylinder: GN_INTERNAL_STRUCTURES_PATH,
GeoNodes.StructurePrimitiveRing: GN_INTERNAL_STRUCTURES_PATH,
GeoNodes.StructurePrimitiveCapsule: GN_INTERNAL_STRUCTURES_PATH,
GeoNodes.StructurePrimitiveCone: GN_INTERNAL_STRUCTURES_PATH,
## Monitor
GeoNodes.MonitorEHField: GN_INTERNAL_STRUCTURES_PATH,
GeoNodes.MonitorFieldPowerFlux: GN_INTERNAL_STRUCTURES_PATH,
GeoNodes.MonitorEpsTensor: GN_INTERNAL_STRUCTURES_PATH,
GeoNodes.MonitorDiffraction: GN_INTERNAL_STRUCTURES_PATH,
GeoNodes.MonitorProjCartEHField: GN_INTERNAL_STRUCTURES_PATH,
GeoNodes.MonitorProjAngEHField: GN_INTERNAL_STRUCTURES_PATH,
GeoNodes.MonitorProjKSpaceEHField: GN_INTERNAL_STRUCTURES_PATH,
## Simulation
GeoNodes.SimulationSimDomain: GN_INTERNAL_SIMULATIONS_PATH,
GeoNodes.SimulationBoundConds: GN_INTERNAL_SIMULATIONS_PATH,
GeoNodes.SimulationBoundCondPML: GN_INTERNAL_SIMULATIONS_PATH,
GeoNodes.SimulationBoundCondPEC: GN_INTERNAL_SIMULATIONS_PATH,
GeoNodes.SimulationBoundCondPMC: GN_INTERNAL_SIMULATIONS_PATH,
GeoNodes.SimulationBoundCondBloch: GN_INTERNAL_SIMULATIONS_PATH,
GeoNodes.SimulationBoundCondPeriodic: GN_INTERNAL_SIMULATIONS_PATH,
GeoNodes.SimulationBoundCondAbsorbing: GN_INTERNAL_SIMULATIONS_PATH,
GeoNodes.SimulationSimGrid: GN_INTERNAL_SIMULATIONS_PATH,
GeoNodes.SimulationSimGridAxisAuto: GN_INTERNAL_SIMULATIONS_PATH,
GeoNodes.SimulationSimGridAxisManual: GN_INTERNAL_SIMULATIONS_PATH,
GeoNodes.SimulationSimGridAxisUniform: GN_INTERNAL_SIMULATIONS_PATH,
GeoNodes.SimulationSimGridAxisArray: GN_INTERNAL_SIMULATIONS_PATH,
# Structures
GeoNodes.PrimitiveBox: GN_STRUCTURES_PRIMITIVES_PATH,
GeoNodes.PrimitiveRing: GN_STRUCTURES_PRIMITIVES_PATH,
GeoNodes.PrimitiveSphere: GN_STRUCTURES_PRIMITIVES_PATH,
} }
#################### ####################
# - Import GeoNodes (Link/Append) # - Import GeoNodes (Link/Append)
#################### ####################
ImportMethod: typ.TypeAlias = typx.Literal['append', 'link']
def import_geonodes( def import_geonodes(
geonodes: GeoNodes, geonodes: GeoNodes,
import_method: ImportMethod, import_method: ImportMethod,
force_import: bool = False,
) -> bpy.types.GeometryNodeGroup: ) -> bpy.types.GeometryNodeGroup:
"""Given a pre-defined GeoNodes group packaged with Blender Maxwell. """Given a (name of a) GeoNodes group packaged with Blender Maxwell, link/append it to the current file, and return the node group.
The procedure is as follows: Parameters:
geonodes: The (name of the) GeoNodes group, which ships with Blender Maxwell.
import_method: Whether to link or append the GeoNodes group.
When 'link', repeated calls will not link a new group; the existing group will simply be returned.
- Link it to the current .blend file. Returns:
- Retrieve the node group and return it. A GeoNodes group available in the current .blend file, which can ex. be attached to a 'GeoNodes Structure' node.
""" """
if geonodes in bpy.data.node_groups and not force_import: if (
import_method == 'link'
and geonodes in bpy.data.node_groups
):
return bpy.data.node_groups[geonodes] return bpy.data.node_groups[geonodes]
filename = geonodes filename = geonodes
@ -144,8 +248,6 @@ class AppendGeoNodes(bpy.types.Operator):
# - Properties # - Properties
#################### ####################
_asset: bpy.types.AssetRepresentation | None = None _asset: bpy.types.AssetRepresentation | None = None
_start_drag_x: bpy.props.IntProperty()
_start_drag_y: bpy.props.IntProperty()
#################### ####################
# - UI # - UI
@ -168,9 +270,7 @@ class AppendGeoNodes(bpy.types.Operator):
""" """
return context.asset is not None return context.asset is not None
def invoke(self, context, event): def invoke(self, context: bpy.types.Context, _):
self._start_drag_x = event.mouse_x
self._start_drag_y = event.mouse_y
return self.execute(context) return self.execute(context)
def execute(self, context: bpy.types.Context) -> BLOperatorStatus: def execute(self, context: bpy.types.Context) -> BLOperatorStatus:

View File

@ -43,10 +43,10 @@ def _size_from_bl_socket(
description: str, description: str,
bl_socket_type: BLSocketType, bl_socket_type: BLSocketType,
): ):
"""Parses the `size`, aka. number of elements, contained within the `default_value` of a Blender interface socket. """Parses the number of elements contained in 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. 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. When this is done, the third value is just never altered by the addon.
A hard-coded set of NodeSocket<Type> prefixes are used to determine which interface sockets are, in fact, 3D. A hard-coded set of NodeSocket<Type> 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. - For 3D sockets, a hard-coded list of Blender node socket types is used.
@ -204,6 +204,7 @@ def _writable_bl_socket_value(
unit_system: dict | None = None, unit_system: dict | None = None,
allow_unit_not_in_unit_system: bool = False, allow_unit_not_in_unit_system: bool = False,
) -> typ.Any: ) -> typ.Any:
log.debug('Writing BL Socket Value (%s)', str(value))
socket_type = _socket_type_from_bl_socket(description, bl_socket_type) socket_type = _socket_type_from_bl_socket(description, bl_socket_type)
# Retrieve Unit-System Unit # Retrieve Unit-System Unit

View File

@ -1,3 +1,5 @@
"""A managed Blender modifier, associated with some Blender object."""
import typing as typ import typing as typ
import bpy import bpy
@ -10,22 +12,35 @@ from .. import contracts as ct
log = logger.get(__name__) log = logger.get(__name__)
ModifierType: typ.TypeAlias = typx.Literal['NODES', 'ARRAY'] ModifierType: typ.TypeAlias = typx.Literal['NODES', 'ARRAY']
NodeTreeInterfaceID: typ.TypeAlias = str NodeTreeInterfaceID: typ.TypeAlias = str
UnitSystem: typ.TypeAlias = typ.Any
####################
# - Modifier Attributes
####################
class ModifierAttrsNODES(typ.TypedDict): class ModifierAttrsNODES(typ.TypedDict):
"""Describes values set on an GeoNodes modifier.
Attributes:
node_group: The GeoNodes group to use in the modifier.
unit_system: The unit system used by the GeoNodes output.
Generally, `ct.UNITS_BLENDER` is a good choice.
inputs: Values to associate with each GeoNodes interface socket.
Use `analyze_geonodes.interface(..., direc='INPUT')` to determine acceptable values.
"""
node_group: bpy.types.GeometryNodeTree node_group: bpy.types.GeometryNodeTree
unit_system: bpy.types.GeometryNodeTree unit_system: UnitSystem
inputs: dict[NodeTreeInterfaceID, typ.Any] inputs: dict[NodeTreeInterfaceID, typ.Any]
class ModifierAttrsARRAY(typ.TypedDict): class ModifierAttrsARRAY(typ.TypedDict):
pass """Describes values set on an Array modifier."""
ModifierAttrs: typ.TypeAlias = ModifierAttrsNODES | ModifierAttrsARRAY ModifierAttrs: typ.TypeAlias = ModifierAttrsNODES | ModifierAttrsARRAY
MODIFIER_NAMES = { MODIFIER_NAMES = {
'NODES': 'BLMaxwell_GeoNodes', 'NODES': 'BLMaxwell_GeoNodes',
'ARRAY': 'BLMaxwell_Array', 'ARRAY': 'BLMaxwell_Array',
@ -37,6 +52,7 @@ MODIFIER_NAMES = {
#################### ####################
def read_modifier(bl_modifier: bpy.types.Modifier) -> ModifierAttrs: def read_modifier(bl_modifier: bpy.types.Modifier) -> ModifierAttrs:
if bl_modifier.type == 'NODES': if bl_modifier.type == 'NODES':
## TODO: Also get GeoNodes modifier values, if the nodegroup is not-None.
return { return {
'node_group': bl_modifier.node_group, 'node_group': bl_modifier.node_group,
} }
@ -50,9 +66,18 @@ def read_modifier(bl_modifier: bpy.types.Modifier) -> ModifierAttrs:
# - Write Modifier Information # - Write Modifier Information
#################### ####################
def write_modifier_geonodes( def write_modifier_geonodes(
bl_modifier: bpy.types.Modifier, bl_modifier: bpy.types.NodesModifier,
modifier_attrs: ModifierAttrsNODES, modifier_attrs: ModifierAttrsNODES,
) -> bool: ) -> bool:
"""Writes attributes to the GeoNodes modifier, changing only what's needed.
Parameters:
bl_modifier: The GeoNodes modifier to write to.
modifier_attrs: The attributes to write to
Returns:
True if the modifier was altered.
"""
modifier_altered = False modifier_altered = False
# Alter GeoNodes Group # Alter GeoNodes Group
if bl_modifier.node_group != modifier_attrs['node_group']: if bl_modifier.node_group != modifier_attrs['node_group']:
@ -163,7 +188,28 @@ class ManagedBLModifier(ct.schemas.ManagedObj):
# - Deallocation # - Deallocation
#################### ####################
def free(self): def free(self):
pass """Not needed - when the object is removed, its modifiers are also removed."""
def free_from_bl_object(
self,
bl_object: bpy.types.Object,
) -> None:
"""Remove the managed BL modifier from the passed Blender object.
Parameters:
bl_object: The Blender object to remove the modifier from.
"""
if (bl_modifier := bl_object.modifiers.get(self.name)) is not None:
log.info(
'Removing (recreating) BLModifier "%s" on BLObject "%s" (existing modifier_type is "%s")',
bl_modifier.name,
bl_object.name,
bl_modifier.type,
)
bl_modifier = bl_object.modifiers.remove(bl_modifier)
else:
msg = f'Tried to free bl_modifier "{self.name}", but bl_object "{bl_object.name}" has no modifier of that name'
raise ValueError(msg)
#################### ####################
# - Modifiers # - Modifiers
@ -190,7 +236,7 @@ class ManagedBLModifier(ct.schemas.ManagedObj):
bl_modifier.type, bl_modifier.type,
modifier_type, modifier_type,
) )
self.free() self.free_from_bl_object(bl_object)
modifier_was_removed = True modifier_was_removed = True
# Create Modifier # Create Modifier

View File

@ -1,400 +0,0 @@
import contextlib
import bmesh
import bpy
import numpy as np
import typing_extensions as typx
from ....utils import logger
from .. import contracts as ct
from .managed_bl_collection import managed_collection, preview_collection
log = logger.get(__name__)
ModifierType = typx.Literal['NODES', 'ARRAY']
MODIFIER_NAMES = {
'NODES': 'BLMaxwell_GeoNodes',
'ARRAY': 'BLMaxwell_Array',
}
####################
# - BLObject
####################
class ManagedBLObject(ct.schemas.ManagedObj):
managed_obj_type = ct.ManagedObjType.ManagedBLObject
_bl_object_name: str | None = None
####################
# - BL Object Name
####################
@property
def name(self):
return self._bl_object_name
@name.setter
def name(self, value: str) -> None:
log.info(
'Changing BLObject w/Name "%s" to Name "%s"', self._bl_object_name, value
)
if not bpy.data.objects.get(value):
log.info(
'Desired BLObject Name "%s" Not Taken',
value,
)
if self._bl_object_name is None:
log.info(
'Set New BLObject Name to "%s"',
value,
)
elif bl_object := bpy.data.objects.get(self._bl_object_name):
log.info(
'Changed BLObject Name to "%s"',
value,
)
bl_object.name = value
else:
msg = f'ManagedBLObject with name "{self._bl_object_name}" was deleted'
raise RuntimeError(msg)
# Set Internal Name
self._bl_object_name = value
else:
log.info(
'Desired BLObject Name "%s" is Taken. Using Blender Rename',
value,
)
# Set Name Anyway, but Respect Blender's Renaming
## When a name already exists, Blender adds .### to prevent overlap.
## `set_name` is allowed to change the name; nodes account for this.
bl_object.name = value
self._bl_object_name = bl_object.name
log.info(
'Changed BLObject Name to "%s"',
bl_object.name,
)
####################
# - Allocation
####################
def __init__(self, name: str):
self.name = name
####################
# - Deallocation
####################
def free(self):
if (bl_object := bpy.data.objects.get(self.name)) is None:
return
# Delete the Underlying Datablock
## This automatically deletes the object too
log.info('Removing "%s" BLObject', bl_object.type)
if bl_object.type in {'MESH', 'EMPTY'}:
bpy.data.meshes.remove(bl_object.data)
elif bl_object.type == 'VOLUME':
bpy.data.volumes.remove(bl_object.data)
else:
msg = f'BLObject "{bl_object.name}" has invalid kind "{bl_object.type}"'
raise RuntimeError(msg)
####################
# - Actions
####################
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 it's already included, do nothing.
"""
bl_object = self.bl_object(kind)
if bl_object.name not in preview_collection().objects:
log.info('Moving "%s" to Preview Collection', bl_object.name)
preview_collection().objects.link(bl_object)
# Display Parameters
if kind == 'EMPTY' and empty_display_type is not None:
log.info(
'Setting Empty Display Type "%s" for "%s"',
empty_display_type,
bl_object.name,
)
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().objects:
log.info('Removing "%s" from Preview Collection', bl_object.name)
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 (bl_object := bpy.data.objects.get(self.name)) is not None:
bpy.ops.object.select_all(action='DESELECT')
bl_object.select_set(True)
msg = 'Managed BLObject does not exist'
raise ValueError(msg)
####################
# - BLObject Management
####################
def bl_object(
self,
kind: typx.Literal['MESH', 'EMPTY', 'VOLUME'],
):
"""Returns the managed blender object.
If the requested object data kind is different, then delete the old
object and recreate.
"""
# Remove Object (if mismatch)
if (bl_object := bpy.data.objects.get(self.name)) and bl_object.type != kind:
log.info(
'Removing (recreating) "%s" (existing kind is "%s", but "%s" is requested)',
bl_object.name,
bl_object.type,
kind,
)
self.free()
# Create Object w/Appropriate Data Block
if not (bl_object := bpy.data.objects.get(self.name)):
log.info(
'Creating "%s" with kind "%s"',
self.name,
kind,
)
if kind == 'MESH':
bl_data = bpy.data.meshes.new(self.name)
elif kind == 'EMPTY':
bl_data = None
elif kind == 'VOLUME':
raise NotImplementedError
else:
msg = f'Created BLObject w/invalid kind "{bl_object.type}" for "{self.name}"'
raise ValueError(msg)
bl_object = bpy.data.objects.new(self.name, bl_data)
log.debug(
'Linking "%s" to Base Collection',
bl_object.name,
)
managed_collection().objects.link(bl_object)
return bl_object
####################
# - Mesh Data Properties
####################
@property
def mesh_data(self) -> bpy.types.Mesh:
"""Directly loads the Blender mesh data.
Raises:
ValueError: If the object has no mesh data.
"""
if (bl_object := bpy.data.objects.get(self.name)) and bl_object.type == 'MESH':
return bl_object.data
msg = f'Requested mesh data from {self.name} of type {bl_object.type}'
raise ValueError(msg)
@contextlib.contextmanager
def mesh_as_bmesh(
self,
evaluate: bool = True,
triangulate: bool = False,
) -> bpy.types.Mesh:
if (bl_object := bpy.data.objects.get(self.name)) and bl_object.type == 'MESH':
bmesh_mesh = None
try:
bmesh_mesh = bmesh.new()
if evaluate:
bmesh_mesh.from_object(
bl_object,
bpy.context.evaluated_depsgraph_get(),
)
else:
bmesh_mesh.from_object(bl_object)
if triangulate:
bmesh.ops.triangulate(bmesh_mesh, faces=bmesh_mesh.faces)
yield bmesh_mesh
finally:
if bmesh_mesh:
bmesh_mesh.free()
else:
msg = f'Requested BMesh from "{self.name}" of type "{bl_object.type}"'
raise ValueError(msg)
@property
def mesh_as_arrays(self) -> dict:
## TODO: Cached
# Ensure Updated Geometry
log.debug('Updating View Layer')
bpy.context.view_layer.update()
# Compute Evaluted + Triangulated Mesh
log.debug('Casting BMesh of "%s" to Temporary Mesh', self.name)
_mesh = bpy.data.meshes.new(name='TemporaryMesh')
with self.mesh_as_bmesh(evaluate=True, triangulate=True) as bmesh_mesh:
bmesh_mesh.to_mesh(_mesh)
# Optimized Vertex Copy
## See <https://blog.michelanders.nl/2016/02/copying-vertices-to-numpy-arrays-in_4.html>
log.debug('Copying Vertices from "%s"', self.name)
verts = np.zeros(3 * len(_mesh.vertices), dtype=np.float64)
_mesh.vertices.foreach_get('co', verts)
verts.shape = (-1, 3)
# Optimized Triangle Copy
## To understand, read it, **carefully**.
log.debug('Copying Faces from "%s"', self.name)
faces = np.zeros(3 * len(_mesh.polygons), dtype=np.uint64)
_mesh.polygons.foreach_get('vertices', faces)
faces.shape = (-1, 3)
# Remove Temporary Mesh
log.debug('Removing Temporary Mesh')
bpy.data.meshes.remove(_mesh)
return {
'verts': verts,
'faces': faces,
}
####################
# - Modifiers
####################
def bl_modifier(
self,
modifier_type: ModifierType,
):
"""Creates a new modifier for the current `bl_object`.
- Modifier Type Names: <https://docs.blender.org/api/current/bpy_types_enum_items/object_modifier_type_items.html#rna-enum-object-modifier-type-items>
"""
if not (bl_object := bpy.data.objects.get(self.name)):
msg = f'Tried to add modifier to "{self.name}", but it has no bl_object'
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()

View File

@ -71,9 +71,9 @@ class ViewerNode(base.MaxwellSimNode):
update=lambda self, context: self.sync_prop('auto_3d_preview', context), update=lambda self, context: self.sync_prop('auto_3d_preview', context),
) )
cache__data_was_unlinked: bpy.props.BoolProperty( cache__data_socket_linked: bpy.props.BoolProperty(
name='Data Was Unlinked', name='Data Is Linked',
description="Whether the Data input was unlinked last time it was checked.", description='Whether the Data input was linked last time it was checked.',
default=True, default=True,
) )
@ -113,9 +113,12 @@ class ViewerNode(base.MaxwellSimNode):
#################### ####################
def print_data_to_console(self): def print_data_to_console(self):
import sys import sys
for module_name, module in sys.modules.copy().items(): for module_name, module in sys.modules.copy().items():
if module_name == '__mp_main__': if module_name == '__mp_main__':
print('Anything, even repr(), with this module just crashes:', module_name) print(
'Anything, even repr(), with this module just crashes:', module_name
)
print(module) ## Crash print(module) ## Crash
if not self.inputs['Data'].is_linked: if not self.inputs['Data'].is_linked:
@ -141,27 +144,31 @@ class ViewerNode(base.MaxwellSimNode):
if self.inputs['Data'].is_linked and props['auto_plot']: if self.inputs['Data'].is_linked and props['auto_plot']:
self.trigger_action('show_plot') self.trigger_action('show_plot')
####################
# - Event Methods: 3D Preview
####################
@events.on_value_changed( @events.on_value_changed(
socket_name='Data', prop_name='auto_3d_preview',
props={'auto_3d_preview'}, props={'auto_3d_preview'},
) )
def on_changed_3d_data(self, props): def on_changed_3d_preview(self, props):
# Data Not Attached
if not self.inputs['Data'].is_linked:
self.cache__data_was_unlinked = True
# Data Just Attached
elif self.cache__data_was_unlinked:
node_tree = self.id_data
# Unpreview Everything # Unpreview Everything
node_tree = self.id_data
node_tree.unpreview_all() node_tree.unpreview_all()
# Enable Previews in Tree # Trigger Preview Action
if props['auto_3d_preview']: if self.inputs['Data'].is_linked and props['auto_3d_preview']:
log.info('Enabling 3D Previews from "%s"', self.name) log.info('Enabling 3D Previews from "%s"', self.name)
self.trigger_action('show_preview') self.trigger_action('show_preview')
self.cache__data_was_unlinked = False
@events.on_value_changed(
socket_name='Data',
)
def on_changed_3d_data(self):
# Just Linked / Just Unlinked: Preview/Unpreview
if self.inputs['Data'].is_linked ^ self.cache__data_socket_linked:
self.on_changed_3d_preview()
self.cache__data_socket_linked = self.inputs['Data'].is_linked
#################### ####################

View File

@ -89,15 +89,10 @@ class GeoNodesStructureNode(base.MaxwellSimNode):
if (geonodes := input_sockets['GeoNodes']) is None: if (geonodes := input_sockets['GeoNodes']) is None:
if ( if (
managed_objs['modifier'].name managed_objs['modifier'].name
in managed_objs['mesh'].bl_object().modifiers in managed_objs['mesh'].bl_object().modifiers.keys().copy()
): ):
log.info( managed_objs['modifier'].free_from_bl_object(
'Removing Modifier "%s" from BLObject "%s"', managed_objs['mesh'].bl_object()
managed_objs['modifier'].name,
managed_objs['mesh'].name,
)
managed_objs['mesh'].bl_object().modifiers.remove(
managed_objs['modifier'].name
) )
# Reset Loose Input Sockets # Reset Loose Input Sockets