Compare commits
2 Commits
59a236f33a
...
0fbf201d08
Author | SHA1 | Date |
---|---|---|
Sofus Albert Høgsbro Rose | 0fbf201d08 | |
Sofus Albert Høgsbro Rose | be4eec2242 |
|
@ -1,4 +1,5 @@
|
|||
dev
|
||||
build
|
||||
*.blend[0-9]
|
||||
|
||||
.cached-dependencies
|
||||
|
|
|
@ -1 +1 @@
|
|||
3.10.13
|
||||
3.11.8
|
||||
|
|
|
@ -6,16 +6,25 @@ authors = [
|
|||
{ name = "Sofus Albert Høgsbro Rose", email = "blender-maxwell@sofusrose.com" }
|
||||
]
|
||||
dependencies = [
|
||||
"tidy3d>=2.6.1",
|
||||
"pydantic>=2.6.4",
|
||||
"sympy>=1.12",
|
||||
"scipy>=1.12.0",
|
||||
"trimesh>=4.2.0",
|
||||
"networkx>=3.2.1",
|
||||
"rtree>=1.2.0",
|
||||
"tidy3d~=2.6.1",
|
||||
"pydantic~=2.6.4",
|
||||
"sympy~=1.12",
|
||||
"scipy~=1.12.0",
|
||||
"trimesh~=4.2.0",
|
||||
"networkx~=3.2.1",
|
||||
"rtree~=1.2.0",
|
||||
|
||||
# Pin Blender 4.1.0-Compatible Versions
|
||||
## The dependency resolver will report if anything is wonky.
|
||||
"urllib3==1.26.8",
|
||||
"requests==2.27.1",
|
||||
"numpy==1.24.3",
|
||||
"idna==3.3",
|
||||
"charset-normalizer==2.0.10",
|
||||
"certifi==2021.10.8",
|
||||
]
|
||||
readme = "README.md"
|
||||
requires-python = "~= 3.10"
|
||||
requires-python = "~= 3.11"
|
||||
license = { text = "AGPL-3.0-or-later" }
|
||||
|
||||
####################
|
||||
|
@ -26,13 +35,18 @@ managed = true
|
|||
virtual = true
|
||||
dev-dependencies = [
|
||||
"ruff>=0.3.2",
|
||||
"fake-bpy-module-4-0>=20231118", ## TODO: Update to Blender 4.1.0
|
||||
]
|
||||
|
||||
[tool.rye.scripts]
|
||||
dev = "python ./scripts/run.py"
|
||||
|
||||
|
||||
####################
|
||||
# - Tooling: Ruff
|
||||
####################
|
||||
[tool.ruff]
|
||||
target-version = "py312"
|
||||
target-version = "py311"
|
||||
line-length = 79
|
||||
|
||||
[tool.ruff.lint]
|
||||
|
@ -77,14 +91,15 @@ select = [
|
|||
"PT", # flake8-pytest-style ## pytest-Specific Checks
|
||||
]
|
||||
ignore = [
|
||||
"B008", # FastAPI uses this for Depends(), Security(), etc. .
|
||||
"E701", # class foo(Parent): pass or if simple: return are perfectly elegant
|
||||
"COM812", # Conflicts w/Formatter
|
||||
"ISC001", # Conflicts w/Formatter
|
||||
"Q000", # Conflicts w/Formatter
|
||||
"Q001", # Conflicts w/Formatter
|
||||
"Q002", # Conflicts w/Formatter
|
||||
"Q003", # Conflicts w/Formatter
|
||||
"B008", # FastAPI uses this for Depends(), Security(), etc. .
|
||||
"E701", # class foo(Parent): pass or if simple: return are perfectly elegant
|
||||
"ERA001", # 'Commented-out code' seems to be just about anything to ruff
|
||||
]
|
||||
|
||||
####################
|
||||
|
|
|
@ -14,9 +14,9 @@ boto3==1.23.1
|
|||
botocore==1.26.10
|
||||
# via boto3
|
||||
# via s3transfer
|
||||
certifi==2024.2.2
|
||||
certifi==2021.10.8
|
||||
# via requests
|
||||
charset-normalizer==3.3.2
|
||||
charset-normalizer==2.0.10
|
||||
# via requests
|
||||
click==8.0.3
|
||||
# via dask
|
||||
|
@ -31,6 +31,7 @@ cycler==0.12.1
|
|||
# via matplotlib
|
||||
dask==2023.10.1
|
||||
# via tidy3d
|
||||
fake-bpy-module-4-0==20231118
|
||||
fonttools==4.49.0
|
||||
# via matplotlib
|
||||
fsspec==2024.2.0
|
||||
|
@ -40,7 +41,7 @@ h5netcdf==1.0.2
|
|||
h5py==3.10.0
|
||||
# via h5netcdf
|
||||
# via tidy3d
|
||||
idna==3.6
|
||||
idna==3.3
|
||||
# via requests
|
||||
importlib-metadata==6.11.0
|
||||
# via dask
|
||||
|
@ -57,11 +58,10 @@ matplotlib==3.8.3
|
|||
mpmath==1.3.0
|
||||
# via sympy
|
||||
networkx==3.2.1
|
||||
numpy==1.26.4
|
||||
numpy==1.24.3
|
||||
# via contourpy
|
||||
# via h5py
|
||||
# via matplotlib
|
||||
# via pandas
|
||||
# via scipy
|
||||
# via shapely
|
||||
# via trimesh
|
||||
|
@ -99,10 +99,10 @@ pyyaml==6.0.1
|
|||
# via dask
|
||||
# via responses
|
||||
# via tidy3d
|
||||
requests==2.31.0
|
||||
requests==2.27.1
|
||||
# via responses
|
||||
# via tidy3d
|
||||
responses==0.25.0
|
||||
responses==0.23.1
|
||||
# via tidy3d
|
||||
rich==12.5.1
|
||||
# via tidy3d
|
||||
|
@ -124,12 +124,14 @@ toolz==0.12.1
|
|||
# via dask
|
||||
# via partd
|
||||
trimesh==4.2.0
|
||||
types-pyyaml==6.0.12.20240311
|
||||
# via responses
|
||||
typing-extensions==4.10.0
|
||||
# via pydantic
|
||||
# via pydantic-core
|
||||
tzdata==2024.1
|
||||
# via pandas
|
||||
urllib3==1.26.18
|
||||
urllib3==1.26.8
|
||||
# via botocore
|
||||
# via requests
|
||||
# via responses
|
||||
|
|
|
@ -14,9 +14,9 @@ boto3==1.23.1
|
|||
botocore==1.26.10
|
||||
# via boto3
|
||||
# via s3transfer
|
||||
certifi==2024.2.2
|
||||
certifi==2021.10.8
|
||||
# via requests
|
||||
charset-normalizer==3.3.2
|
||||
charset-normalizer==2.0.10
|
||||
# via requests
|
||||
click==8.0.3
|
||||
# via dask
|
||||
|
@ -40,7 +40,7 @@ h5netcdf==1.0.2
|
|||
h5py==3.10.0
|
||||
# via h5netcdf
|
||||
# via tidy3d
|
||||
idna==3.6
|
||||
idna==3.3
|
||||
# via requests
|
||||
importlib-metadata==6.11.0
|
||||
# via dask
|
||||
|
@ -57,11 +57,10 @@ matplotlib==3.8.3
|
|||
mpmath==1.3.0
|
||||
# via sympy
|
||||
networkx==3.2.1
|
||||
numpy==1.26.4
|
||||
numpy==1.24.3
|
||||
# via contourpy
|
||||
# via h5py
|
||||
# via matplotlib
|
||||
# via pandas
|
||||
# via scipy
|
||||
# via shapely
|
||||
# via trimesh
|
||||
|
@ -99,10 +98,10 @@ pyyaml==6.0.1
|
|||
# via dask
|
||||
# via responses
|
||||
# via tidy3d
|
||||
requests==2.31.0
|
||||
requests==2.27.1
|
||||
# via responses
|
||||
# via tidy3d
|
||||
responses==0.25.0
|
||||
responses==0.23.1
|
||||
# via tidy3d
|
||||
rich==12.5.1
|
||||
# via tidy3d
|
||||
|
@ -123,12 +122,14 @@ toolz==0.12.1
|
|||
# via dask
|
||||
# via partd
|
||||
trimesh==4.2.0
|
||||
types-pyyaml==6.0.12.20240311
|
||||
# via responses
|
||||
typing-extensions==4.10.0
|
||||
# via pydantic
|
||||
# via pydantic-core
|
||||
tzdata==2024.1
|
||||
# via pandas
|
||||
urllib3==1.26.18
|
||||
urllib3==1.26.8
|
||||
# via botocore
|
||||
# via requests
|
||||
# via responses
|
||||
|
|
108
run.py
108
run.py
|
@ -1,108 +0,0 @@
|
|||
import zipfile
|
||||
import contextlib
|
||||
import shutil
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import bpy
|
||||
import addon_utils
|
||||
|
||||
PATH_ROOT = Path(__file__).resolve().parent
|
||||
|
||||
####################
|
||||
# - Defined Constants
|
||||
####################
|
||||
ADDON_NAME = "blender_maxwell"
|
||||
PATH_BLEND = PATH_ROOT / "demo.blend"
|
||||
PATH_ADDON_DEPS = PATH_ROOT / ".cached-dependencies"
|
||||
|
||||
####################
|
||||
# - Computed Constants
|
||||
####################
|
||||
PATH_ADDON = PATH_ROOT / ADDON_NAME
|
||||
PATH_ADDON_ZIP = PATH_ROOT / (ADDON_NAME + ".zip")
|
||||
|
||||
####################
|
||||
# - Utilities
|
||||
####################
|
||||
@contextlib.contextmanager
|
||||
def zipped_directory(path_dir: Path, path_zip: Path):
|
||||
"""Context manager that exposes a zipped version of a directory,
|
||||
then deletes the .zip file afterwards.
|
||||
"""
|
||||
# Delete Existing ZIP file (if exists)
|
||||
if path_zip.is_file(): path_zip.unlink()
|
||||
|
||||
# Create a (new) ZIP file of the addon directory
|
||||
with zipfile.ZipFile(path_zip, 'w', zipfile.ZIP_DEFLATED) as f_zip:
|
||||
for file_to_zip in path_dir.rglob('*'):
|
||||
f_zip.write(file_to_zip, file_to_zip.relative_to(path_dir.parent))
|
||||
|
||||
# Delete the ZIP
|
||||
try:
|
||||
yield path_zip
|
||||
finally:
|
||||
path_zip.unlink()
|
||||
|
||||
####################
|
||||
# - main()
|
||||
####################
|
||||
if __name__ == "__main__":
|
||||
# Check and uninstall the addon if it's enabled
|
||||
is_loaded_by_default, is_loaded_now = addon_utils.check(ADDON_NAME)
|
||||
if is_loaded_now:
|
||||
# Disable the Addon
|
||||
addon_utils.disable(ADDON_NAME, default_set=True, handle_error=None)
|
||||
|
||||
# Completey Delete the Addon
|
||||
for mod in addon_utils.modules():
|
||||
if mod.__name__ == ADDON_NAME:
|
||||
# Delete Addon from Blender Python Tree
|
||||
shutil.rmtree(Path(mod.__file__).parent)
|
||||
|
||||
# Reset All Addons
|
||||
addon_utils.reset_all()
|
||||
|
||||
# Save User Preferences & Break
|
||||
bpy.ops.wm.save_userpref()
|
||||
break
|
||||
|
||||
# Quit Blender (hard-flush Python environment)
|
||||
## - Python environments are not made to be partially flushed.
|
||||
## - This is the only truly reliable way to avoid all bugs.
|
||||
## - See https://github.com/JacquesLucke/blender_vscode
|
||||
bpy.ops.wm.quit_blender()
|
||||
try:
|
||||
raise RuntimeError
|
||||
except:
|
||||
sys.exit(42)
|
||||
|
||||
with zipped_directory(PATH_ADDON, PATH_ADDON_ZIP) as path_zipped:
|
||||
# Install the ZIPped Addon
|
||||
bpy.ops.preferences.addon_install(filepath=str(path_zipped))
|
||||
|
||||
# Enable the Addon
|
||||
addon_utils.enable(
|
||||
ADDON_NAME,
|
||||
default_set=True,
|
||||
persistent=True,
|
||||
handle_error=None,
|
||||
)
|
||||
|
||||
# Save User Preferences
|
||||
bpy.ops.wm.save_userpref()
|
||||
|
||||
# Load the .blend
|
||||
bpy.ops.wm.open_mainfile(filepath=str(PATH_BLEND))
|
||||
|
||||
# Ensure Addon-Specific Dependency Cache is Importable
|
||||
## - In distribution, the addon keeps this folder in the Blender script tree.
|
||||
## - For testing, we need to hack sys.path here.
|
||||
## - This avoids having to install all deps with every reload.
|
||||
if str(PATH_ADDON_DEPS) not in sys.path:
|
||||
sys.path.insert(0, str(PATH_ADDON_DEPS))
|
||||
|
||||
# Modify any specific settings, if needed
|
||||
# Example: bpy.context.preferences.addons[addon_name].preferences.your_setting = "your_value"
|
||||
|
||||
|
11
run.sh
11
run.sh
|
@ -1,11 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
blender --python run.py
|
||||
if [ $? -eq 42 ]; then
|
||||
echo
|
||||
echo
|
||||
echo
|
||||
echo
|
||||
echo
|
||||
blender --python run.py
|
||||
fi
|
|
@ -0,0 +1,155 @@
|
|||
"""Blender startup script ensuring correct addon installation.
|
||||
|
||||
See <https://github.com/dfelinto/blender/blob/master/release/scripts/modules/addon_utils.py>
|
||||
"""
|
||||
|
||||
import shutil
|
||||
import sys
|
||||
import traceback
|
||||
from pathlib import Path
|
||||
|
||||
import bpy
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||||
import info
|
||||
import pack
|
||||
|
||||
## TODO: Preferences item that allows using BLMaxwell 'starter.blend' as Blender's default starter blendfile.
|
||||
|
||||
|
||||
####################
|
||||
# - Addon Functions
|
||||
####################
|
||||
def delete_addon_if_loaded(addon_name: str) -> None:
|
||||
"""Strongly inspired by Blender's addon_utils.py."""
|
||||
should_restart_blender = False
|
||||
|
||||
# Check if Python Module is Loaded
|
||||
mod = sys.modules.get(addon_name)
|
||||
# if (mod := sys.modules.get(addon_name)) is None:
|
||||
# ## It could still be loaded-by-default; then, it's in the prefs list
|
||||
# is_loaded_now = False
|
||||
# loads_by_default = addon_name in bpy.context.preferences.addons
|
||||
# else:
|
||||
# ## BL sets __addon_enabled__ on module of enabled addons.
|
||||
# ## BL sets __addon_persistent__ on module of load-by-default addons.
|
||||
# is_loaded_now = getattr(mod, '__addon_enabled__', False)
|
||||
# loads_by_default = getattr(mod, '__addon_persistent__', False)
|
||||
|
||||
# Unregister Modules and Mark Disabled & Non-Persistent
|
||||
## This effectively disables it
|
||||
if mod is not None:
|
||||
mod.__addon_enabled__ = False
|
||||
mod.__addon_persistent__ = False
|
||||
try:
|
||||
mod.unregister()
|
||||
except BaseException:
|
||||
traceback.print_exc()
|
||||
should_restart_blender = True
|
||||
|
||||
# Remove Addon
|
||||
## Remove Addon from Preferences
|
||||
## - Unsure why addon_utils has a while, but let's trust the process...
|
||||
while addon_name in bpy.context.preferences.addons:
|
||||
addon = bpy.context.preferences.addons.get(addon_name)
|
||||
if addon:
|
||||
bpy.context.preferences.addons.remove(addon)
|
||||
|
||||
## Physically Excise Addon Code
|
||||
for addons_path in bpy.utils.script_paths(subdir='addons'):
|
||||
addon_path = Path(addons_path) / addon_name
|
||||
if addon_path.exists():
|
||||
shutil.rmtree(addon_path)
|
||||
should_restart_blender = True
|
||||
|
||||
## Save User Preferences
|
||||
bpy.ops.wm.save_userpref()
|
||||
|
||||
# Quit (Restart) Blender - hard-flush Python environment
|
||||
## - Python environments are not made to be partially flushed.
|
||||
## - This is the only truly reliable way to avoid all bugs.
|
||||
## - See <https://github.com/JacquesLucke/blender_vscode>
|
||||
## - By passing STATUS_UNINSTALLED_ADDON, we report that it's clean now.
|
||||
if should_restart_blender:
|
||||
bpy.ops.wm.quit_blender()
|
||||
sys.exit(info.STATUS_UNINSTALLED_ADDON)
|
||||
|
||||
|
||||
def install_addon(addon_name: str, addon_zip: Path) -> None:
|
||||
"""Strongly inspired by Blender's addon_utils.py."""
|
||||
# Check if Addon is Installable
|
||||
if any(
|
||||
[
|
||||
(mod := sys.modules.get(addon_name)) is not None,
|
||||
addon_name in bpy.context.preferences.addons,
|
||||
any(
|
||||
(Path(addon_path) / addon_name).exists()
|
||||
for addon_path in bpy.utils.script_paths(subdir='addons')
|
||||
),
|
||||
]
|
||||
):
|
||||
## TODO: Check if addon file path exists?
|
||||
in_pref_addons = addon_name in bpy.context.preferences.addons
|
||||
existing_files_found = {
|
||||
addon_path: (Path(addon_path) / addon_name).exists()
|
||||
for addon_path in bpy.utils.script_paths(subdir='addons')
|
||||
if (Path(addon_path) / addon_name).exists()
|
||||
}
|
||||
msg = f"Addon (module = '{mod}') is not installable (in preferences.addons: {in_pref_addons}) (existing files found: {existing_files_found})"
|
||||
raise ValueError(msg)
|
||||
|
||||
# Install Addon
|
||||
bpy.ops.preferences.addon_install(filepath=str(addon_zip))
|
||||
if not any(
|
||||
(Path(addon_path) / addon_name).exists()
|
||||
for addon_path in bpy.utils.script_paths(subdir='addons')
|
||||
):
|
||||
msg = f"Couldn't install addon {addon_name}"
|
||||
raise RuntimeError(msg)
|
||||
|
||||
# Enable Addon
|
||||
bpy.ops.preferences.addon_enable(module=addon_name)
|
||||
if addon_name not in bpy.context.preferences.addons:
|
||||
msg = f"Couldn't enable addon {addon_name}"
|
||||
raise RuntimeError(msg)
|
||||
|
||||
# Set Dev Path for Addon Dependencies
|
||||
addon_prefs = bpy.context.preferences.addons[addon_name].preferences
|
||||
addon_prefs.use_default_path_addon_pydeps = False
|
||||
addon_prefs.path_addon_pydeps = info.PATH_ADDON_DEV_DEPS
|
||||
|
||||
# Save User Preferences
|
||||
bpy.ops.wm.save_userpref()
|
||||
|
||||
|
||||
####################
|
||||
# - Entrypoint
|
||||
####################
|
||||
if __name__ == '__main__':
|
||||
# Delete Addon (maybe; possibly restart)
|
||||
delete_addon_if_loaded(info.ADDON_NAME)
|
||||
|
||||
# Signal that Live-Printing can Start
|
||||
print(info.SIGNAL_START_CLEAN_BLENDER) # noqa: T201
|
||||
|
||||
# Install and Enable Addon
|
||||
install_failed = False
|
||||
with pack.zipped_addon(
|
||||
info.PATH_ADDON_PKG,
|
||||
info.PATH_ADDON_ZIP,
|
||||
info.PATH_ROOT / 'pyproject.toml',
|
||||
info.PATH_ROOT / 'requirements.lock',
|
||||
) as path_zipped:
|
||||
try:
|
||||
install_addon(info.ADDON_NAME, path_zipped)
|
||||
except Exception as exe:
|
||||
traceback.print_exc()
|
||||
install_failed = True
|
||||
|
||||
# Load Development .blend
|
||||
## TODO: We need a better (also final-deployed-compatible) solution for what happens when a user opened a .blend file without installing dependencies!
|
||||
if not install_failed:
|
||||
bpy.ops.wm.open_mainfile(filepath=str(info.PATH_ADDON_DEV_BLEND))
|
||||
else:
|
||||
bpy.ops.wm.quit_blender()
|
||||
sys.exit(info.STATUS_NOINSTALL_ADDON)
|
|
@ -0,0 +1,51 @@
|
|||
import tomllib
|
||||
from pathlib import Path
|
||||
|
||||
PATH_ROOT = Path(__file__).resolve().parent.parent
|
||||
PATH_RUN = PATH_ROOT / 'scripts' / 'run.py'
|
||||
PATH_BL_RUN = PATH_ROOT / 'scripts' / 'bl_run.py'
|
||||
|
||||
PATH_BUILD = PATH_ROOT / 'build'
|
||||
PATH_BUILD.mkdir(exist_ok=True)
|
||||
|
||||
PATH_DEV = PATH_ROOT / 'dev'
|
||||
PATH_DEV.mkdir(exist_ok=True)
|
||||
|
||||
####################
|
||||
# - BL_RUN stdout Signals
|
||||
####################
|
||||
SIGNAL_START_CLEAN_BLENDER = 'SIGNAL__blender_is_clean'
|
||||
|
||||
####################
|
||||
# - BL_RUN Exit Codes
|
||||
####################
|
||||
STATUS_UNINSTALLED_ADDON = 42
|
||||
STATUS_NOINSTALL_ADDON = 68
|
||||
|
||||
####################
|
||||
# - Addon Information
|
||||
####################
|
||||
with (PATH_ROOT / 'pyproject.toml').open('rb') as f:
|
||||
PROJ_SPEC = tomllib.load(f)
|
||||
|
||||
ADDON_NAME = PROJ_SPEC['project']['name']
|
||||
ADDON_VERSION = PROJ_SPEC['project']['version']
|
||||
|
||||
####################
|
||||
# - Packaging Information
|
||||
####################
|
||||
PATH_ADDON_PKG = PATH_ROOT / 'src' / ADDON_NAME
|
||||
PATH_ADDON_ZIP = (
|
||||
PATH_ROOT / 'build' / (ADDON_NAME + '__' + ADDON_VERSION + '.zip')
|
||||
)
|
||||
|
||||
PATH_ADDON_BLEND_STARTER = PATH_ADDON_PKG / 'blenders' / 'starter.blend'
|
||||
|
||||
# Install the ZIPped Addon
|
||||
####################
|
||||
# - Development Information
|
||||
####################
|
||||
PATH_ADDON_DEV_BLEND = PATH_DEV / 'demo.blend'
|
||||
|
||||
PATH_ADDON_DEV_DEPS = PATH_DEV / '.cached-dev-dependencies'
|
||||
PATH_ADDON_DEV_DEPS.mkdir(exist_ok=True)
|
|
@ -0,0 +1,93 @@
|
|||
import contextlib
|
||||
import tempfile
|
||||
import typing as typ
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
import info
|
||||
|
||||
_PROJ_VERSION_STR = str(
|
||||
tuple(int(el) for el in info.PROJ_SPEC['project']['version'].split('.'))
|
||||
)
|
||||
_PROJ_DESC_STR = info.PROJ_SPEC['project']['description']
|
||||
|
||||
BL_INFO_REPLACEMENTS = {
|
||||
"'version': (0, 0, 0),": f"'version': {_PROJ_VERSION_STR},",
|
||||
"'description': 'Placeholder',": f"'description': '{_PROJ_DESC_STR}',",
|
||||
}
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def zipped_addon(
|
||||
path_addon_pkg: Path,
|
||||
path_addon_zip: Path,
|
||||
path_pyproject_toml: Path,
|
||||
path_requirements_lock: Path,
|
||||
replace_if_exists: bool = False,
|
||||
) -> typ.Iterator[Path]:
|
||||
"""Context manager exposing a folder as a (temporary) zip file.
|
||||
The .zip file is deleted afterwards.
|
||||
"""
|
||||
# Delete Existing ZIP (maybe)
|
||||
if path_addon_zip.is_file():
|
||||
if replace_if_exists:
|
||||
msg = 'File already exists where ZIP would be made'
|
||||
raise ValueError(msg)
|
||||
path_addon_zip.unlink()
|
||||
|
||||
# Create New ZIP file of the addon directory
|
||||
with zipfile.ZipFile(path_addon_zip, 'w', zipfile.ZIP_DEFLATED) as f_zip:
|
||||
# Install Addon Files @ /*
|
||||
for file_to_zip in path_addon_pkg.rglob('*'):
|
||||
# Dynamically Alter 'bl_info' in __init__.py
|
||||
## This is the only way to propagate ex. version information
|
||||
if str(file_to_zip.relative_to(path_addon_pkg)) == '__init__.py':
|
||||
with (
|
||||
file_to_zip.open('r') as f_init,
|
||||
tempfile.NamedTemporaryFile(mode='w') as f_tmp,
|
||||
):
|
||||
initpy = f_init.read()
|
||||
for (
|
||||
to_replace,
|
||||
replacement,
|
||||
) in BL_INFO_REPLACEMENTS.items():
|
||||
initpy = initpy.replace(to_replace, replacement)
|
||||
f_tmp.write(initpy)
|
||||
|
||||
# Write to ZIP
|
||||
f_zip.writestr(
|
||||
str(file_to_zip.relative_to(path_addon_pkg.parent)),
|
||||
initpy,
|
||||
)
|
||||
|
||||
# Write File to Zip
|
||||
else:
|
||||
f_zip.write(
|
||||
file_to_zip, file_to_zip.relative_to(path_addon_pkg.parent)
|
||||
)
|
||||
|
||||
# Install pyproject.toml @ /pyproject.toml of Addon
|
||||
f_zip.write(
|
||||
path_pyproject_toml,
|
||||
str(
|
||||
(Path(path_addon_pkg.name) / Path(path_pyproject_toml.name))
|
||||
.with_suffix('')
|
||||
.with_suffix('.toml')
|
||||
),
|
||||
)
|
||||
|
||||
# Install requirements.lock @ /requirements.txt of Addon
|
||||
f_zip.write(
|
||||
path_requirements_lock,
|
||||
str(
|
||||
(Path(path_addon_pkg.name) / Path(path_requirements_lock.name))
|
||||
.with_suffix('')
|
||||
.with_suffix('.txt')
|
||||
),
|
||||
)
|
||||
|
||||
# Delete the ZIP
|
||||
try:
|
||||
yield path_addon_zip
|
||||
finally:
|
||||
path_addon_zip.unlink()
|
|
@ -0,0 +1,54 @@
|
|||
import os
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
import info
|
||||
|
||||
|
||||
####################
|
||||
# - Blender Runner
|
||||
####################
|
||||
def run_blender(py_script: Path, print_live: bool = False):
|
||||
process = subprocess.Popen(
|
||||
['blender', '--python', str(py_script)],
|
||||
env=os.environ | {'PYTHONUNBUFFERED': '1'},
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
text=True,
|
||||
)
|
||||
output = []
|
||||
printing_live = print_live
|
||||
|
||||
# Process Real-Time Output
|
||||
for line in iter(process.stdout.readline, b''):
|
||||
if not line:
|
||||
break
|
||||
|
||||
if printing_live:
|
||||
print(line, end='') # noqa: T201
|
||||
elif (
|
||||
info.SIGNAL_START_CLEAN_BLENDER in line
|
||||
# or 'Traceback (most recent call last)' in line
|
||||
):
|
||||
printing_live = True
|
||||
print(''.join(output)) # noqa: T201
|
||||
else:
|
||||
output.append(line)
|
||||
|
||||
# Wait for the process to finish and get the exit code
|
||||
process.wait()
|
||||
return process.returncode, output
|
||||
|
||||
|
||||
####################
|
||||
# - Run Blender w/Clean Addon Reinstall
|
||||
####################
|
||||
if __name__ == '__main__':
|
||||
return_code, output = run_blender(info.PATH_BL_RUN, print_live=False)
|
||||
if return_code == info.STATUS_UNINSTALLED_ADDON:
|
||||
return_code, output = run_blender(info.PATH_BL_RUN, print_live=True)
|
||||
if return_code == info.STATUS_NOINSTALL_ADDON:
|
||||
msg = f"Couldn't install addon {info.ADDON_NAME}"
|
||||
raise ValueError(msg)
|
||||
elif return_code != 0:
|
||||
print(''.join(output)) # noqa: T201
|
|
@ -1,84 +1,97 @@
|
|||
import tomllib
|
||||
from pathlib import Path
|
||||
|
||||
import bpy
|
||||
|
||||
from . import operators_nodeps, preferences, registration
|
||||
from .utils import pydeps
|
||||
from .utils import logger as _logger
|
||||
|
||||
log = _logger.get()
|
||||
PATH_ADDON_ROOT = Path(__file__).resolve().parent
|
||||
with (PATH_ADDON_ROOT / 'pyproject.toml').open('rb') as f:
|
||||
PROJ_SPEC = tomllib.load(f)
|
||||
|
||||
####################
|
||||
# - Addon Information
|
||||
####################
|
||||
# The following parameters are replaced when packing the addon ZIP
|
||||
## - description
|
||||
## - version
|
||||
bl_info = {
|
||||
"name": "Maxwell Simulation and Visualization",
|
||||
"blender": (4, 0, 2),
|
||||
"category": "Node",
|
||||
"description": "Custom node trees for defining and visualizing Maxwell simulation.",
|
||||
"author": "Sofus Albert Høgsbro Rose",
|
||||
"version": (0, 1),
|
||||
"wiki_url": "https://git.sofus.io/dtu-courses/bsc_thesis",
|
||||
"tracker_url": "https://git.sofus.io/dtu-courses/bsc_thesis/issues",
|
||||
'name': 'Maxwell PDE Sim and Viz',
|
||||
'blender': (4, 1, 0),
|
||||
'category': 'Node',
|
||||
'description': 'Placeholder',
|
||||
'author': 'Sofus Albert Høgsbro Rose',
|
||||
'version': (0, 0, 0),
|
||||
'wiki_url': 'https://git.sofus.io/dtu-courses/bsc_thesis',
|
||||
'tracker_url': 'https://git.sofus.io/dtu-courses/bsc_thesis/issues',
|
||||
}
|
||||
## bl_info MUST readable via. ast.parse
|
||||
## See scripts/pack.py::BL_INFO_REPLACEMENTS for active replacements
|
||||
## The mechanism is a 'dumb' - output of 'ruff fmt' MUST be basis for replacing
|
||||
|
||||
|
||||
def ADDON_PREFS():
|
||||
return bpy.context.preferences.addons[
|
||||
PROJ_SPEC['project']['name']
|
||||
].preferences
|
||||
|
||||
|
||||
####################
|
||||
# - sys.path Library Inclusion
|
||||
# - Load and Register Addon
|
||||
####################
|
||||
import sys
|
||||
sys.path.insert(0, "/home/sofus/src/college/bsc_ge/thesis/code/.cached-dependencies")
|
||||
## ^^ Placeholder
|
||||
BL_REGISTER__BEFORE_DEPS = [
|
||||
*operators_nodeps.BL_REGISTER,
|
||||
*preferences.BL_REGISTER,
|
||||
]
|
||||
|
||||
####################
|
||||
# - Module Import
|
||||
####################
|
||||
if "bpy" not in locals():
|
||||
import bpy
|
||||
import nodeitems_utils
|
||||
try:
|
||||
from . import node_trees
|
||||
|
||||
def BL_REGISTER__AFTER_DEPS(path_deps: Path):
|
||||
with pydeps.importable_addon_deps(path_deps):
|
||||
from . import node_trees, operators
|
||||
return [
|
||||
*operators.BL_REGISTER,
|
||||
*node_trees.BL_REGISTER,
|
||||
]
|
||||
|
||||
|
||||
def BL_KEYMAP_ITEM_DEFS(path_deps: Path):
|
||||
with pydeps.importable_addon_deps(path_deps):
|
||||
from . import operators
|
||||
from . import preferences
|
||||
except ImportError:
|
||||
import sys
|
||||
sys.path.insert(0, "/home/sofus/src/college/bsc_ge/thesis/code/blender-maxwell")
|
||||
import node_trees
|
||||
import operators
|
||||
import preferences
|
||||
else:
|
||||
import importlib
|
||||
|
||||
importlib.reload(node_trees)
|
||||
return [
|
||||
*operators.BL_KMI_REGISTER,
|
||||
]
|
||||
|
||||
|
||||
####################
|
||||
# - Registration
|
||||
####################
|
||||
BL_REGISTER = [
|
||||
*node_trees.BL_REGISTER,
|
||||
*operators.BL_REGISTER,
|
||||
*preferences.BL_REGISTER,
|
||||
]
|
||||
BL_KMI_REGISTER = [
|
||||
*operators.BL_KMI_REGISTER,
|
||||
]
|
||||
BL_NODE_CATEGORIES = [
|
||||
*node_trees.BL_NODE_CATEGORIES,
|
||||
]
|
||||
|
||||
km = bpy.context.window_manager.keyconfigs.addon.keymaps.new(
|
||||
name='Node Editor',
|
||||
space_type="NODE_EDITOR",
|
||||
)
|
||||
REGISTERED_KEYMAPS = []
|
||||
def register():
|
||||
global REGISTERED_KEYMAPS
|
||||
|
||||
for cls in BL_REGISTER:
|
||||
bpy.utils.register_class(cls)
|
||||
|
||||
for kmi_def in BL_KMI_REGISTER:
|
||||
kmi = km.keymap_items.new(
|
||||
*kmi_def["_"],
|
||||
ctrl=kmi_def["ctrl"],
|
||||
shift=kmi_def["shift"],
|
||||
alt=kmi_def["alt"],
|
||||
)
|
||||
REGISTERED_KEYMAPS.append(kmi)
|
||||
|
||||
def unregister():
|
||||
for cls in reversed(BL_REGISTER):
|
||||
bpy.utils.unregister_class(cls)
|
||||
|
||||
for kmi in REGISTERED_KEYMAPS:
|
||||
km.keymap_items.remove(kmi)
|
||||
# Register Barebones Addon for Dependency Installation
|
||||
registration.register_classes(BL_REGISTER__BEFORE_DEPS)
|
||||
|
||||
if __name__ == "__main__":
|
||||
register()
|
||||
# Retrieve PyDeps Path from Addon Preferences
|
||||
addon_prefs = ADDON_PREFS()
|
||||
path_pydeps = addon_prefs.path_addon_pydeps
|
||||
|
||||
# If Dependencies are Satisfied, Register Everything
|
||||
if pydeps.check_pydeps(path_pydeps):
|
||||
registration.register_classes(BL_REGISTER__AFTER_DEPS())
|
||||
registration.register_keymap_items(BL_KEYMAP_ITEM_DEFS())
|
||||
else:
|
||||
# Delay Registration
|
||||
registration.delay_registration(
|
||||
registration.EVENT__DEPS_SATISFIED,
|
||||
classes_cb=BL_REGISTER__AFTER_DEPS,
|
||||
keymap_item_defs_cb=BL_KEYMAP_ITEM_DEFS,
|
||||
)
|
||||
|
||||
# TODO: A popup before the addon fully loads or something like that?
|
||||
## TODO: Communicate that deps must be installed and all that?
|
||||
|
||||
|
||||
def unregister():
|
||||
registration.unregister_classes()
|
||||
registration.unregister_keymap_items()
|
||||
|
|
Binary file not shown.
|
@ -1,4 +1,5 @@
|
|||
import sympy as sp
|
||||
|
||||
sp.printing.str.StrPrinter._default_settings['abbrev'] = True
|
||||
## In this tree, all Sympy unit printing must be abbreviated.
|
||||
## By configuring this in __init__.py, we guarantee it for all subimports.
|
||||
|
|
|
@ -26,47 +26,47 @@ Unit = typ.Any ## Type of a valid unit
|
|||
SOCKET_DEFS = {
|
||||
socket_type: getattr(
|
||||
sck,
|
||||
socket_type.value.removesuffix("SocketType") + "SocketDef",
|
||||
socket_type.value.removesuffix('SocketType') + 'SocketDef',
|
||||
)
|
||||
for socket_type in ST
|
||||
if hasattr(
|
||||
sck,
|
||||
socket_type.value.removesuffix("SocketType") + "SocketDef"
|
||||
)
|
||||
if hasattr(sck, socket_type.value.removesuffix('SocketType') + 'SocketDef')
|
||||
}
|
||||
## TODO: Bit of a hack. Is it robust enough?
|
||||
|
||||
for socket_type in ST:
|
||||
if not hasattr(
|
||||
sck,
|
||||
socket_type.value.removesuffix("SocketType") + "SocketDef",
|
||||
socket_type.value.removesuffix('SocketType') + 'SocketDef',
|
||||
):
|
||||
print("Missing SocketDef for", socket_type.value)
|
||||
print('Missing SocketDef for', socket_type.value)
|
||||
|
||||
|
||||
####################
|
||||
# - BL Socket Size Parser
|
||||
####################
|
||||
BL_SOCKET_3D_TYPE_PREFIXES = {
|
||||
"NodeSocketVector",
|
||||
"NodeSocketRotation",
|
||||
'NodeSocketVector',
|
||||
'NodeSocketRotation',
|
||||
}
|
||||
BL_SOCKET_4D_TYPE_PREFIXES = {
|
||||
"NodeSocketColor",
|
||||
'NodeSocketColor',
|
||||
}
|
||||
|
||||
|
||||
def size_from_bl_interface_socket(
|
||||
bl_interface_socket: bpy.types.NodeTreeInterfaceSocket
|
||||
bl_interface_socket: bpy.types.NodeTreeInterfaceSocket,
|
||||
) -> typx.Literal[1, 2, 3, 4]:
|
||||
"""Parses the `size`, aka. number of elements, contained within the `default_value` of a Blender interface socket.
|
||||
|
||||
|
||||
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<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.
|
||||
- Else, it is a 1D socket type.
|
||||
"""
|
||||
if bl_interface_socket.description.startswith("2D"): return 2
|
||||
if bl_interface_socket.description.startswith('2D'):
|
||||
return 2
|
||||
if any(
|
||||
bl_interface_socket.socket_type.startswith(bl_socket_3d_type_prefix)
|
||||
for bl_socket_3d_type_prefix in BL_SOCKET_3D_TYPE_PREFIXES
|
||||
|
@ -77,7 +77,7 @@ def size_from_bl_interface_socket(
|
|||
for bl_socket_4d_type_prefix in BL_SOCKET_4D_TYPE_PREFIXES
|
||||
):
|
||||
return 4
|
||||
|
||||
|
||||
return 1
|
||||
|
||||
|
||||
|
@ -88,15 +88,15 @@ 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(
|
||||
|
@ -105,44 +105,55 @@ def parse_bl_interface_socket(
|
|||
) 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"
|
||||
if (_tokens := bl_interface_socket.description.split(' '))[0] != '2D'
|
||||
else _tokens[1:]
|
||||
) ## Don't include the "2D" token, if defined.
|
||||
) ## 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
|
||||
|
||||
return (
|
||||
direct_socket_type,
|
||||
None,
|
||||
) ## Description doesn't map to anything
|
||||
|
||||
# Determine Socket Unit (to use instead of "unit system")
|
||||
## This is entirely OPTIONAL
|
||||
socket_unit = None
|
||||
if socket_type in ct.SOCKET_UNITS:
|
||||
## Case: Unit is User-Defined
|
||||
if len(tokens) > 1 and "(" in tokens[1] and ")" in tokens[1]:
|
||||
if len(tokens) > 1 and '(' in tokens[1] and ')' in tokens[1]:
|
||||
# Compute (<unit_str>) as Unit Token
|
||||
unit_token = tokens[1].removeprefix("(").removesuffix(")")
|
||||
|
||||
unit_token = tokens[1].removeprefix('(').removesuffix(')')
|
||||
|
||||
# Compare Unit Token to Valid Sympy-Printed Units
|
||||
socket_unit = _socket_unit if (_socket_unit := [
|
||||
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"]
|
||||
]
|
||||
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)
|
||||
|
||||
|
||||
|
@ -152,11 +163,8 @@ def parse_bl_interface_socket(
|
|||
def socket_def_from_bl_interface_socket(
|
||||
bl_interface_socket: bpy.types.NodeTreeInterfaceSocket,
|
||||
):
|
||||
"""Computes an appropriate (no-arg) SocketDef from the given `bl_interface_socket`, by parsing it.
|
||||
"""
|
||||
return SOCKET_DEFS[
|
||||
parse_bl_interface_socket(bl_interface_socket)[0]
|
||||
]
|
||||
"""Computes an appropriate (no-arg) SocketDef from the given `bl_interface_socket`, by parsing it."""
|
||||
return SOCKET_DEFS[parse_bl_interface_socket(bl_interface_socket)[0]]
|
||||
|
||||
|
||||
####################
|
||||
|
@ -169,7 +177,7 @@ def value_from_bl(
|
|||
"""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 = {
|
||||
|
@ -179,24 +187,25 @@ def value_from_bl(
|
|||
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):
|
||||
|
@ -208,44 +217,43 @@ def make_scalar_bl_compat(scalar: typ.Any) -> typ.Any:
|
|||
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]
|
||||
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
|
||||
]),
|
||||
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
|
||||
|
|
|
@ -6,34 +6,35 @@ from . import contracts as ct
|
|||
from .nodes import BL_NODES
|
||||
|
||||
DYNAMIC_SUBMENU_REGISTRATIONS = []
|
||||
|
||||
|
||||
def mk_node_categories(
|
||||
tree,
|
||||
syllable_prefix = [],
|
||||
#root = True,
|
||||
syllable_prefix=[],
|
||||
# root = True,
|
||||
):
|
||||
global DYNAMIC_SUBMENU_REGISTRATIONS
|
||||
items = []
|
||||
|
||||
|
||||
# Add Node Items
|
||||
base_category = ct.NodeCategory["_".join(syllable_prefix)]
|
||||
base_category = ct.NodeCategory['_'.join(syllable_prefix)]
|
||||
for node_type, node_category in BL_NODES.items():
|
||||
if node_category == base_category:
|
||||
items.append(nodeitems_utils.NodeItem(node_type.value))
|
||||
|
||||
|
||||
# Add Node Sub-Menus
|
||||
for syllable, sub_tree in tree.items():
|
||||
current_syllable_path = syllable_prefix + [syllable]
|
||||
current_category = ct.NodeCategory[
|
||||
"_".join(current_syllable_path)
|
||||
]
|
||||
|
||||
current_category = ct.NodeCategory['_'.join(current_syllable_path)]
|
||||
|
||||
# Build Items for Sub-Categories
|
||||
subitems = mk_node_categories(
|
||||
sub_tree,
|
||||
current_syllable_path,
|
||||
)
|
||||
if len(subitems) == 0: continue
|
||||
|
||||
if len(subitems) == 0:
|
||||
continue
|
||||
|
||||
# Define Dynamic Node Submenu
|
||||
def draw_factory(items):
|
||||
def draw(self, context):
|
||||
|
@ -44,7 +45,7 @@ def mk_node_categories(
|
|||
):
|
||||
nodeitem = nodeitem_or_submenu
|
||||
op_add_node_cfg = self.layout.operator(
|
||||
"node.add_node",
|
||||
'node.add_node',
|
||||
text=nodeitem.label,
|
||||
)
|
||||
op_add_node_cfg.type = nodeitem.nodetype
|
||||
|
@ -52,34 +53,39 @@ def mk_node_categories(
|
|||
elif isinstance(nodeitem_or_submenu, str):
|
||||
submenu_id = nodeitem_or_submenu
|
||||
self.layout.menu(submenu_id)
|
||||
|
||||
return draw
|
||||
|
||||
menu_class = type(str(current_category.value), (bpy.types.Menu,), {
|
||||
'bl_idname': current_category.value,
|
||||
'bl_label': ct.NODE_CAT_LABELS[current_category],
|
||||
'draw': draw_factory(tuple(subitems)),
|
||||
})
|
||||
|
||||
|
||||
menu_class = type(
|
||||
str(current_category.value),
|
||||
(bpy.types.Menu,),
|
||||
{
|
||||
'bl_idname': current_category.value,
|
||||
'bl_label': ct.NODE_CAT_LABELS[current_category],
|
||||
'draw': draw_factory(tuple(subitems)),
|
||||
},
|
||||
)
|
||||
|
||||
# Report to Items and Registration List
|
||||
items.append(current_category.value)
|
||||
DYNAMIC_SUBMENU_REGISTRATIONS.append(menu_class)
|
||||
|
||||
return items
|
||||
|
||||
return items
|
||||
|
||||
|
||||
####################
|
||||
# - Blender Registration
|
||||
####################
|
||||
BL_NODE_CATEGORIES = mk_node_categories(
|
||||
ct.NodeCategory.get_tree()["MAXWELLSIM"],
|
||||
syllable_prefix = ["MAXWELLSIM"],
|
||||
ct.NodeCategory.get_tree()['MAXWELLSIM'],
|
||||
syllable_prefix=['MAXWELLSIM'],
|
||||
)
|
||||
## TODO: refactor, this has a big code smell
|
||||
BL_REGISTER = [
|
||||
*DYNAMIC_SUBMENU_REGISTRATIONS
|
||||
] ## Must be run after, right now.
|
||||
|
||||
|
||||
## TEST - TODO this is a big code smell
|
||||
def menu_draw(self, context):
|
||||
if context.space_data.tree_type == ct.TreeType.MaxwellSim.value:
|
||||
|
@ -87,5 +93,6 @@ def menu_draw(self, context):
|
|||
if isinstance(nodeitem_or_submenu, str):
|
||||
submenu_id = nodeitem_or_submenu
|
||||
self.layout.menu(submenu_id)
|
||||
|
||||
|
||||
|
||||
bpy.types.NODE_MT_add.append(menu_draw)
|
||||
|
|
|
@ -7,20 +7,32 @@ import bpy
|
|||
####################
|
||||
# - Pure BL Types
|
||||
####################
|
||||
BLEnumID = pytypes_ext.Annotated[str, pyd.StringConstraints(
|
||||
pattern=r'^[A-Z_]+$',
|
||||
)]
|
||||
SocketName = pytypes_ext.Annotated[str, pyd.StringConstraints(
|
||||
pattern=r'^[a-zA-Z0-9_]+$',
|
||||
)]
|
||||
PresetName = pytypes_ext.Annotated[str, pyd.StringConstraints(
|
||||
pattern=r'^[a-zA-Z0-9_]+$',
|
||||
)]
|
||||
BLEnumID = pytypes_ext.Annotated[
|
||||
str,
|
||||
pyd.StringConstraints(
|
||||
pattern=r'^[A-Z_]+$',
|
||||
),
|
||||
]
|
||||
SocketName = pytypes_ext.Annotated[
|
||||
str,
|
||||
pyd.StringConstraints(
|
||||
pattern=r'^[a-zA-Z0-9_]+$',
|
||||
),
|
||||
]
|
||||
PresetName = pytypes_ext.Annotated[
|
||||
str,
|
||||
pyd.StringConstraints(
|
||||
pattern=r'^[a-zA-Z0-9_]+$',
|
||||
),
|
||||
]
|
||||
BLColorRGBA = tuple[float, float, float, float]
|
||||
|
||||
####################
|
||||
# - Shared-With-BL Types
|
||||
####################
|
||||
ManagedObjName = pytypes_ext.Annotated[str, pyd.StringConstraints(
|
||||
pattern=r'^[a-z_]+$',
|
||||
)]
|
||||
ManagedObjName = pytypes_ext.Annotated[
|
||||
str,
|
||||
pyd.StringConstraints(
|
||||
pattern=r'^[a-z_]+$',
|
||||
),
|
||||
]
|
||||
|
|
|
@ -2,11 +2,12 @@ import enum
|
|||
|
||||
from ....utils.blender_type_enum import BlenderTypeEnum
|
||||
|
||||
|
||||
class DataFlowKind(BlenderTypeEnum):
|
||||
"""Defines a shape/kind of data that may flow through a node tree.
|
||||
|
||||
|
||||
Since a node socket may define one of each, we can support several related kinds of data flow through the same node-graph infrastructure.
|
||||
|
||||
|
||||
Attributes:
|
||||
Value: A value usable without new data.
|
||||
- Basic types aka. float, int, list, string, etc. .
|
||||
|
@ -14,43 +15,43 @@ class DataFlowKind(BlenderTypeEnum):
|
|||
- A usable constructed object, ex. a `tidy3d.Box`.
|
||||
- Expressions (`sp.Expr`) that don't have unknown variables.
|
||||
- Lazy sequences aka. generators, with all data bound.
|
||||
|
||||
|
||||
LazyValue: An object which, when given new data, can make many values.
|
||||
- An `sp.Expr`, which might need `simplify`ing, `jax` JIT'ing, unit cancellations, variable substitutions, etc. before use.
|
||||
- Lazy objects, for which all parameters aren't yet known.
|
||||
- A computational graph aka. `aesara`, which may even need to be handled before
|
||||
|
||||
- A computational graph aka. `aesara`, which may even need to be handled before
|
||||
|
||||
Capabilities: A `ValueCapability` object providing compatibility.
|
||||
|
||||
|
||||
# Value Data Flow
|
||||
Simply passing values is the simplest and easiest use case.
|
||||
|
||||
|
||||
This doesn't mean it's "dumb" - ex. a `sp.Expr` might, before use, have `simplify`, rewriting, unit cancellation, etc. run.
|
||||
All of this is okay, as long as there is no *introduction of new data* ex. variable substitutions.
|
||||
|
||||
|
||||
|
||||
|
||||
# Lazy Value Data Flow
|
||||
By passing (essentially) functions, one supports:
|
||||
- **Lightness**: While lazy values can be made expensive to construct, they will generally not be nearly as heavy to handle when trying to work with ex. operations on voxel arrays.
|
||||
- **Performance**: Parameterizing ex. `sp.Expr` with variables allows one to build very optimized functions, which can make ex. node graph updates very fast if the only operation run is the `jax` JIT'ed function (aka. GPU accelerated) generated from the final full expression.
|
||||
- **Numerical Stability**: Libraries like `aesara` build a computational graph, which can be automatically rewritten to avoid many obvious conditioning / cancellation errors.
|
||||
- **Lazy Output**: The goal of a node-graph may not be the definition of a single value, but rather, a parameterized expression for generating *many values* with known properties. This is especially interesting for use cases where one wishes to build an optimization step using nodes.
|
||||
|
||||
|
||||
|
||||
|
||||
# Capability Passing
|
||||
By being able to pass "capabilities" next to other kinds of values, nodes can quickly determine whether a given link is valid without having to actually compute it.
|
||||
|
||||
|
||||
|
||||
|
||||
# Lazy Parameter Value
|
||||
When using parameterized LazyValues, one may wish to independently pass parameter values through the graph, so they can be inserted into the final (cached) high-performance expression without.
|
||||
|
||||
|
||||
The advantage of using a different data flow would be changing this kind of value would ONLY invalidate lazy parameter value caches, which would allow an incredibly fast path of getting the value into the lazy expression for high-performance computation.
|
||||
|
||||
|
||||
Implementation TBD - though, ostensibly, one would have a "parameter" node which both would only provide a LazyValue (aka. a symbolic variable), but would also be able to provide a LazyParamValue, which would be a particular value of some kind (probably via the `value` of some other node socket).
|
||||
"""
|
||||
|
||||
|
||||
Value = enum.auto()
|
||||
LazyValue = enum.auto()
|
||||
Capabilities = enum.auto()
|
||||
|
||||
|
||||
LazyParamValue = enum.auto()
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from ....utils.blender_type_enum import BlenderTypeEnum
|
||||
|
||||
|
||||
class Icon(BlenderTypeEnum):
|
||||
SimNodeEditor = "MOD_SIMPLEDEFORM"
|
||||
SimNodeEditor = 'MOD_SIMPLEDEFORM'
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import enum
|
||||
|
||||
from ....utils.blender_type_enum import (
|
||||
BlenderTypeEnum
|
||||
)
|
||||
from ....utils.blender_type_enum import BlenderTypeEnum
|
||||
|
||||
|
||||
class ManagedObjType(BlenderTypeEnum):
|
||||
ManagedBLObject = enum.auto()
|
||||
|
|
|
@ -2,48 +2,39 @@ from .node_cats import NodeCategory as NC
|
|||
|
||||
NODE_CAT_LABELS = {
|
||||
# Inputs/
|
||||
NC.MAXWELLSIM_INPUTS: "Inputs",
|
||||
NC.MAXWELLSIM_INPUTS_IMPORTERS: "Importers",
|
||||
NC.MAXWELLSIM_INPUTS_SCENE: "Scene",
|
||||
NC.MAXWELLSIM_INPUTS_PARAMETERS: "Parameters",
|
||||
NC.MAXWELLSIM_INPUTS_CONSTANTS: "Constants",
|
||||
NC.MAXWELLSIM_INPUTS_LISTS: "Lists",
|
||||
|
||||
NC.MAXWELLSIM_INPUTS: 'Inputs',
|
||||
NC.MAXWELLSIM_INPUTS_IMPORTERS: 'Importers',
|
||||
NC.MAXWELLSIM_INPUTS_SCENE: 'Scene',
|
||||
NC.MAXWELLSIM_INPUTS_PARAMETERS: 'Parameters',
|
||||
NC.MAXWELLSIM_INPUTS_CONSTANTS: 'Constants',
|
||||
NC.MAXWELLSIM_INPUTS_LISTS: 'Lists',
|
||||
# Outputs/
|
||||
NC.MAXWELLSIM_OUTPUTS: "Outputs",
|
||||
NC.MAXWELLSIM_OUTPUTS_VIEWERS: "Viewers",
|
||||
NC.MAXWELLSIM_OUTPUTS_EXPORTERS: "Exporters",
|
||||
NC.MAXWELLSIM_OUTPUTS_PLOTTERS: "Plotters",
|
||||
|
||||
NC.MAXWELLSIM_OUTPUTS: 'Outputs',
|
||||
NC.MAXWELLSIM_OUTPUTS_VIEWERS: 'Viewers',
|
||||
NC.MAXWELLSIM_OUTPUTS_EXPORTERS: 'Exporters',
|
||||
NC.MAXWELLSIM_OUTPUTS_PLOTTERS: 'Plotters',
|
||||
# Sources/
|
||||
NC.MAXWELLSIM_SOURCES: "Sources",
|
||||
NC.MAXWELLSIM_SOURCES_TEMPORALSHAPES: "Temporal Shapes",
|
||||
|
||||
NC.MAXWELLSIM_SOURCES: 'Sources',
|
||||
NC.MAXWELLSIM_SOURCES_TEMPORALSHAPES: 'Temporal Shapes',
|
||||
# Mediums/
|
||||
NC.MAXWELLSIM_MEDIUMS: "Mediums",
|
||||
NC.MAXWELLSIM_MEDIUMS_NONLINEARITIES: "Non-Linearities",
|
||||
|
||||
NC.MAXWELLSIM_MEDIUMS: 'Mediums',
|
||||
NC.MAXWELLSIM_MEDIUMS_NONLINEARITIES: 'Non-Linearities',
|
||||
# Structures/
|
||||
NC.MAXWELLSIM_STRUCTURES: "Structures",
|
||||
NC.MAXWELLSIM_STRUCTURES_PRIMITIVES: "Primitives",
|
||||
|
||||
NC.MAXWELLSIM_STRUCTURES: 'Structures',
|
||||
NC.MAXWELLSIM_STRUCTURES_PRIMITIVES: 'Primitives',
|
||||
# Bounds/
|
||||
NC.MAXWELLSIM_BOUNDS: "Bounds",
|
||||
NC.MAXWELLSIM_BOUNDS_BOUNDCONDS: "Bound Conds",
|
||||
|
||||
NC.MAXWELLSIM_BOUNDS: 'Bounds',
|
||||
NC.MAXWELLSIM_BOUNDS_BOUNDCONDS: 'Bound Conds',
|
||||
# Monitors/
|
||||
NC.MAXWELLSIM_MONITORS: "Monitors",
|
||||
NC.MAXWELLSIM_MONITORS_NEARFIELDPROJECTIONS: "Near-Field Projections",
|
||||
|
||||
NC.MAXWELLSIM_MONITORS: 'Monitors',
|
||||
NC.MAXWELLSIM_MONITORS_NEARFIELDPROJECTIONS: 'Near-Field Projections',
|
||||
# Simulations/
|
||||
NC.MAXWELLSIM_SIMS: "Simulations",
|
||||
NC.MAXWELLSIM_SIMGRIDAXES: "Sim Grid Axes",
|
||||
|
||||
NC.MAXWELLSIM_SIMS: 'Simulations',
|
||||
NC.MAXWELLSIM_SIMGRIDAXES: 'Sim Grid Axes',
|
||||
# Utilities/
|
||||
NC.MAXWELLSIM_UTILITIES: "Utilities",
|
||||
NC.MAXWELLSIM_UTILITIES_CONVERTERS: "Converters",
|
||||
NC.MAXWELLSIM_UTILITIES_OPERATIONS: "Operations",
|
||||
|
||||
NC.MAXWELLSIM_UTILITIES: 'Utilities',
|
||||
NC.MAXWELLSIM_UTILITIES_CONVERTERS: 'Converters',
|
||||
NC.MAXWELLSIM_UTILITIES_OPERATIONS: 'Operations',
|
||||
# Viz/
|
||||
NC.MAXWELLSIM_VIZ: "Viz",
|
||||
NC.MAXWELLSIM_VIZ: 'Viz',
|
||||
}
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
import enum
|
||||
|
||||
from ....utils.blender_type_enum import (
|
||||
BlenderTypeEnum, wrap_values_in_MT
|
||||
)
|
||||
from ....utils.blender_type_enum import BlenderTypeEnum, wrap_values_in_MT
|
||||
|
||||
|
||||
@wrap_values_in_MT
|
||||
class NodeCategory(BlenderTypeEnum):
|
||||
MAXWELLSIM = enum.auto()
|
||||
|
||||
|
||||
# Inputs/
|
||||
MAXWELLSIM_INPUTS = enum.auto()
|
||||
MAXWELLSIM_INPUTS_IMPORTERS = enum.auto()
|
||||
|
@ -15,63 +14,63 @@ class NodeCategory(BlenderTypeEnum):
|
|||
MAXWELLSIM_INPUTS_PARAMETERS = enum.auto()
|
||||
MAXWELLSIM_INPUTS_CONSTANTS = enum.auto()
|
||||
MAXWELLSIM_INPUTS_LISTS = enum.auto()
|
||||
|
||||
|
||||
# Outputs/
|
||||
MAXWELLSIM_OUTPUTS = enum.auto()
|
||||
MAXWELLSIM_OUTPUTS_VIEWERS = enum.auto()
|
||||
MAXWELLSIM_OUTPUTS_EXPORTERS = enum.auto()
|
||||
MAXWELLSIM_OUTPUTS_PLOTTERS = enum.auto()
|
||||
|
||||
|
||||
# Sources/
|
||||
MAXWELLSIM_SOURCES = enum.auto()
|
||||
MAXWELLSIM_SOURCES_TEMPORALSHAPES = enum.auto()
|
||||
|
||||
|
||||
# Mediums/
|
||||
MAXWELLSIM_MEDIUMS = enum.auto()
|
||||
MAXWELLSIM_MEDIUMS_NONLINEARITIES = enum.auto()
|
||||
|
||||
|
||||
# Structures/
|
||||
MAXWELLSIM_STRUCTURES = enum.auto()
|
||||
MAXWELLSIM_STRUCTURES_PRIMITIVES = enum.auto()
|
||||
|
||||
|
||||
# Bounds/
|
||||
MAXWELLSIM_BOUNDS = enum.auto()
|
||||
MAXWELLSIM_BOUNDS_BOUNDCONDS = enum.auto()
|
||||
|
||||
|
||||
# Monitors/
|
||||
MAXWELLSIM_MONITORS = enum.auto()
|
||||
MAXWELLSIM_MONITORS_NEARFIELDPROJECTIONS = enum.auto()
|
||||
|
||||
|
||||
# Simulations/
|
||||
MAXWELLSIM_SIMS = enum.auto()
|
||||
MAXWELLSIM_SIMGRIDAXES = enum.auto()
|
||||
|
||||
|
||||
# Utilities/
|
||||
MAXWELLSIM_UTILITIES = enum.auto()
|
||||
MAXWELLSIM_UTILITIES_CONVERTERS = enum.auto()
|
||||
MAXWELLSIM_UTILITIES_OPERATIONS = enum.auto()
|
||||
|
||||
|
||||
# Viz/
|
||||
MAXWELLSIM_VIZ = enum.auto()
|
||||
|
||||
|
||||
@classmethod
|
||||
def get_tree(cls):
|
||||
## TODO: Refactor
|
||||
syllable_categories = [
|
||||
str(node_category.value).split("_")
|
||||
str(node_category.value).split('_')
|
||||
for node_category in cls
|
||||
if node_category.value != "MAXWELLSIM"
|
||||
if node_category.value != 'MAXWELLSIM'
|
||||
]
|
||||
|
||||
|
||||
category_tree = {}
|
||||
for syllable_category in syllable_categories:
|
||||
# Set Current Subtree to Root
|
||||
current_category_subtree = category_tree
|
||||
|
||||
|
||||
for i, syllable in enumerate(syllable_category):
|
||||
# Create New Category Subtree and/or Step to Subtree
|
||||
if syllable not in current_category_subtree:
|
||||
current_category_subtree[syllable] = {}
|
||||
current_category_subtree = current_category_subtree[syllable]
|
||||
|
||||
|
||||
return category_tree
|
||||
|
|
|
@ -1,58 +1,58 @@
|
|||
import enum
|
||||
|
||||
from ....utils.blender_type_enum import (
|
||||
BlenderTypeEnum, append_cls_name_to_values
|
||||
BlenderTypeEnum,
|
||||
append_cls_name_to_values,
|
||||
)
|
||||
|
||||
|
||||
@append_cls_name_to_values
|
||||
class NodeType(BlenderTypeEnum):
|
||||
KitchenSink = enum.auto()
|
||||
|
||||
|
||||
# Inputs
|
||||
UnitSystem = enum.auto()
|
||||
|
||||
|
||||
## Inputs / Scene
|
||||
Time = enum.auto()
|
||||
|
||||
|
||||
## Inputs / Importers
|
||||
Tidy3DWebImporter = enum.auto()
|
||||
|
||||
|
||||
## Inputs / Parameters
|
||||
NumberParameter = enum.auto()
|
||||
PhysicalParameter = enum.auto()
|
||||
|
||||
|
||||
## Inputs / Constants
|
||||
WaveConstant = enum.auto()
|
||||
ScientificConstant = enum.auto()
|
||||
NumberConstant = enum.auto()
|
||||
PhysicalConstant = enum.auto()
|
||||
BlenderConstant = enum.auto()
|
||||
|
||||
|
||||
## Inputs / Lists
|
||||
RealList = enum.auto()
|
||||
ComplexList = enum.auto()
|
||||
|
||||
## Inputs /
|
||||
|
||||
## Inputs /
|
||||
InputFile = enum.auto()
|
||||
|
||||
|
||||
|
||||
# Outputs
|
||||
## Outputs / Viewers
|
||||
Viewer = enum.auto()
|
||||
ValueViewer = enum.auto()
|
||||
ConsoleViewer = enum.auto()
|
||||
|
||||
|
||||
## Outputs / Exporters
|
||||
JSONFileExporter = enum.auto()
|
||||
Tidy3DWebExporter = enum.auto()
|
||||
|
||||
|
||||
|
||||
# Sources
|
||||
## Sources / Temporal Shapes
|
||||
GaussianPulseTemporalShape = enum.auto()
|
||||
ContinuousWaveTemporalShape = enum.auto()
|
||||
ListTemporalShape = enum.auto()
|
||||
|
||||
|
||||
## Sources /
|
||||
PointDipoleSource = enum.auto()
|
||||
UniformCurrentSource = enum.auto()
|
||||
|
@ -61,92 +61,86 @@ class NodeType(BlenderTypeEnum):
|
|||
GaussianBeamSource = enum.auto()
|
||||
AstigmaticGaussianBeamSource = enum.auto()
|
||||
TFSFSource = enum.auto()
|
||||
|
||||
|
||||
EHEquivalenceSource = enum.auto()
|
||||
EHSource = enum.auto()
|
||||
|
||||
|
||||
# Mediums
|
||||
LibraryMedium = enum.auto()
|
||||
|
||||
|
||||
PECMedium = enum.auto()
|
||||
IsotropicMedium = enum.auto()
|
||||
AnisotropicMedium = enum.auto()
|
||||
|
||||
|
||||
TripleSellmeierMedium = enum.auto()
|
||||
SellmeierMedium = enum.auto()
|
||||
PoleResidueMedium = enum.auto()
|
||||
DrudeMedium = enum.auto()
|
||||
DrudeLorentzMedium = enum.auto()
|
||||
DebyeMedium = enum.auto()
|
||||
|
||||
|
||||
## Mediums / Non-Linearities
|
||||
AddNonLinearity = enum.auto()
|
||||
ChiThreeSusceptibilityNonLinearity = enum.auto()
|
||||
TwoPhotonAbsorptionNonLinearity = enum.auto()
|
||||
KerrNonLinearity = enum.auto()
|
||||
|
||||
|
||||
# Structures
|
||||
ObjectStructure = enum.auto()
|
||||
GeoNodesStructure = enum.auto()
|
||||
ScriptedStructure = enum.auto()
|
||||
|
||||
|
||||
## Structures / Primitives
|
||||
BoxStructure = enum.auto()
|
||||
SphereStructure = enum.auto()
|
||||
CylinderStructure = enum.auto()
|
||||
|
||||
|
||||
|
||||
# Bounds
|
||||
BoundConds = enum.auto()
|
||||
|
||||
|
||||
## Bounds / Bound Faces
|
||||
PMLBoundCond = enum.auto()
|
||||
PECBoundCond = enum.auto()
|
||||
PMCBoundCond = enum.auto()
|
||||
|
||||
|
||||
BlochBoundCond = enum.auto()
|
||||
PeriodicBoundCond = enum.auto()
|
||||
AbsorbingBoundCond = enum.auto()
|
||||
|
||||
|
||||
|
||||
# Monitors
|
||||
EHFieldMonitor = enum.auto()
|
||||
FieldPowerFluxMonitor = enum.auto()
|
||||
EpsilonTensorMonitor = enum.auto()
|
||||
DiffractionMonitor = enum.auto()
|
||||
|
||||
|
||||
## Monitors / Near-Field Projections
|
||||
CartesianNearFieldProjectionMonitor = enum.auto()
|
||||
ObservationAngleNearFieldProjectionMonitor = enum.auto()
|
||||
KSpaceNearFieldProjectionMonitor = enum.auto()
|
||||
|
||||
|
||||
|
||||
# Sims
|
||||
SimDomain = enum.auto()
|
||||
SimGrid = enum.auto()
|
||||
|
||||
|
||||
## Sims / Sim Grid Axis
|
||||
AutomaticSimGridAxis = enum.auto()
|
||||
ManualSimGridAxis = enum.auto()
|
||||
UniformSimGridAxis = enum.auto()
|
||||
ArraySimGridAxis = enum.auto()
|
||||
|
||||
|
||||
## Sim /
|
||||
FDTDSim = enum.auto()
|
||||
|
||||
|
||||
|
||||
# Utilities
|
||||
Combine = enum.auto()
|
||||
Separate = enum.auto()
|
||||
Math = enum.auto()
|
||||
|
||||
|
||||
## Utilities / Converters
|
||||
WaveConverter = enum.auto()
|
||||
|
||||
|
||||
## Utilities / Operations
|
||||
ArrayOperation = enum.auto()
|
||||
|
||||
|
||||
|
||||
|
||||
# Viz
|
||||
FDTDSimDataViz = enum.auto()
|
||||
|
|
|
@ -1,33 +1,26 @@
|
|||
import typing as typ
|
||||
import typing as typx
|
||||
|
||||
import pydantic as pyd
|
||||
|
||||
import bpy
|
||||
|
||||
from ..bl import ManagedObjName, SocketName
|
||||
from ..bl import ManagedObjName
|
||||
from ..managed_obj_type import ManagedObjType
|
||||
|
||||
|
||||
class ManagedObj(typ.Protocol):
|
||||
managed_obj_type: ManagedObjType
|
||||
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: ManagedObjName,
|
||||
):
|
||||
...
|
||||
|
||||
): ...
|
||||
|
||||
@property
|
||||
def name(self) -> str: ...
|
||||
@name.setter
|
||||
def name(self, value: str): ...
|
||||
|
||||
def free(self):
|
||||
...
|
||||
|
||||
|
||||
def free(self): ...
|
||||
|
||||
def bl_select(self):
|
||||
"""If this is a managed Blender object, and the operation "select this in Blender" makes sense, then do so.
|
||||
|
||||
Else, do nothing.
|
||||
"""
|
||||
pass
|
||||
|
|
|
@ -6,6 +6,7 @@ import pydantic as pyd
|
|||
from ..bl import PresetName, SocketName, BLEnumID
|
||||
from .managed_obj import ManagedObj
|
||||
|
||||
|
||||
class ManagedObjDef(pyd.BaseModel):
|
||||
mk: typ.Callable[[str], ManagedObj]
|
||||
name_prefix: str = ""
|
||||
name_prefix: str = ''
|
||||
|
|
|
@ -4,6 +4,7 @@ import pydantic as pyd
|
|||
|
||||
from ..bl import PresetName, SocketName, BLEnumID
|
||||
|
||||
|
||||
class PresetDef(pyd.BaseModel):
|
||||
label: PresetName
|
||||
description: str
|
||||
|
|
|
@ -4,9 +4,9 @@ import bpy
|
|||
|
||||
from ..socket_types import SocketType
|
||||
|
||||
|
||||
@typ.runtime_checkable
|
||||
class SocketDef(typ.Protocol):
|
||||
socket_type: SocketType
|
||||
|
||||
def init(self, bl_socket: bpy.types.NodeSocket) -> None:
|
||||
...
|
||||
|
||||
def init(self, bl_socket: bpy.types.NodeSocket) -> None: ...
|
||||
|
|
|
@ -10,13 +10,11 @@ SOCKET_COLORS = {
|
|||
ST.Bool: (0.7, 0.7, 0.7, 1.0), # Medium Light Grey
|
||||
ST.String: (0.7, 0.7, 0.7, 1.0), # Medium Light Grey
|
||||
ST.FilePath: (0.6, 0.6, 0.6, 1.0), # Medium Grey
|
||||
|
||||
# Number
|
||||
ST.IntegerNumber: (0.5, 0.5, 1.0, 1.0), # Light Blue
|
||||
ST.RationalNumber: (0.4, 0.4, 0.9, 1.0), # Medium Light Blue
|
||||
ST.RealNumber: (0.3, 0.3, 0.8, 1.0), # Medium Blue
|
||||
ST.ComplexNumber: (0.2, 0.2, 0.7, 1.0), # Dark Blue
|
||||
|
||||
# Vector
|
||||
ST.Integer2DVector: (0.5, 1.0, 0.5, 1.0), # Light Green
|
||||
ST.Real2DVector: (0.5, 1.0, 0.5, 1.0), # Light Green
|
||||
|
@ -24,7 +22,6 @@ SOCKET_COLORS = {
|
|||
ST.Integer3DVector: (0.3, 0.8, 0.3, 1.0), # Medium Green
|
||||
ST.Real3DVector: (0.3, 0.8, 0.3, 1.0), # Medium Green
|
||||
ST.Complex3DVector: (0.2, 0.7, 0.2, 1.0), # Dark Green
|
||||
|
||||
# Physical
|
||||
ST.PhysicalUnitSystem: (1.0, 0.5, 0.5, 1.0), # Light Red
|
||||
ST.PhysicalTime: (1.0, 0.5, 0.5, 1.0), # Light Red
|
||||
|
@ -44,14 +41,12 @@ SOCKET_COLORS = {
|
|||
ST.PhysicalForce3D: (0.6, 0.45, 0.25, 1.0), # Medium Dark Orange
|
||||
ST.PhysicalPol: (0.5, 0.4, 0.2, 1.0), # Dark Orange
|
||||
ST.PhysicalFreq: (1.0, 0.7, 0.5, 1.0), # Light Peach
|
||||
|
||||
# Blender
|
||||
ST.BlenderObject: (0.7, 0.5, 1.0, 1.0), # Light Purple
|
||||
ST.BlenderCollection: (0.6, 0.45, 0.9, 1.0), # Medium Light Purple
|
||||
ST.BlenderImage: (0.5, 0.4, 0.8, 1.0), # Medium Purple
|
||||
ST.BlenderGeoNodes: (0.3, 0.3, 0.6, 1.0), # Dark Purple
|
||||
ST.BlenderText: (0.5, 0.5, 0.75, 1.0), # Light Lavender
|
||||
|
||||
# Maxwell
|
||||
ST.MaxwellSource: (1.0, 1.0, 0.5, 1.0), # Light Yellow
|
||||
ST.MaxwellTemporalShape: (0.9, 0.9, 0.45, 1.0), # Medium Light Yellow
|
||||
|
@ -66,8 +61,6 @@ SOCKET_COLORS = {
|
|||
ST.MaxwellSimGrid: (0.5, 0.4, 0.3, 1.0), # Dark Gold
|
||||
ST.MaxwellSimGridAxis: (0.4, 0.3, 0.25, 1.0), # Darkest Gold
|
||||
ST.MaxwellSimDomain: (0.4, 0.3, 0.25, 1.0), # Darkest Gold
|
||||
|
||||
# Tidy3D
|
||||
ST.Tidy3DCloudTask: (0.4, 0.3, 0.25, 1.0), # Darkest Gold
|
||||
}
|
||||
|
||||
|
|
|
@ -1,78 +1,62 @@
|
|||
from .socket_types import SocketType as ST
|
||||
|
||||
BL_SOCKET_DESCR_ANNOT_STRING = ":: "
|
||||
BL_SOCKET_DESCR_ANNOT_STRING = ':: '
|
||||
BL_SOCKET_DESCR_TYPE_MAP = {
|
||||
("Time", "NodeSocketFloat", 1): ST.PhysicalTime,
|
||||
|
||||
("Angle", "NodeSocketFloat", 1): ST.PhysicalAngle,
|
||||
("SolidAngle", "NodeSocketFloat", 1): ST.PhysicalSolidAngle,
|
||||
|
||||
("Rotation", "NodeSocketVector", 2): ST.PhysicalRot2D,
|
||||
("Rotation", "NodeSocketVector", 3): ST.PhysicalRot3D,
|
||||
|
||||
("Freq", "NodeSocketFloat", 1): ST.PhysicalFreq,
|
||||
("AngFreq", "NodeSocketFloat", 1): ST.PhysicalAngFreq,
|
||||
|
||||
('Time', 'NodeSocketFloat', 1): ST.PhysicalTime,
|
||||
('Angle', 'NodeSocketFloat', 1): ST.PhysicalAngle,
|
||||
('SolidAngle', 'NodeSocketFloat', 1): ST.PhysicalSolidAngle,
|
||||
('Rotation', 'NodeSocketVector', 2): ST.PhysicalRot2D,
|
||||
('Rotation', 'NodeSocketVector', 3): ST.PhysicalRot3D,
|
||||
('Freq', 'NodeSocketFloat', 1): ST.PhysicalFreq,
|
||||
('AngFreq', 'NodeSocketFloat', 1): ST.PhysicalAngFreq,
|
||||
## Cartesian
|
||||
("Length", "NodeSocketFloat", 1): ST.PhysicalLength,
|
||||
("Area", "NodeSocketFloat", 1): ST.PhysicalArea,
|
||||
("Volume", "NodeSocketFloat", 1): ST.PhysicalVolume,
|
||||
|
||||
("Disp", "NodeSocketVector", 2): ST.PhysicalDisp2D,
|
||||
("Disp", "NodeSocketVector", 3): ST.PhysicalDisp3D,
|
||||
|
||||
("Point", "NodeSocketFloat", 1): ST.PhysicalPoint1D,
|
||||
("Point", "NodeSocketVector", 2): ST.PhysicalPoint2D,
|
||||
("Point", "NodeSocketVector", 3): ST.PhysicalPoint3D,
|
||||
|
||||
("Size", "NodeSocketVector", 2): ST.PhysicalSize2D,
|
||||
("Size", "NodeSocketVector", 3): ST.PhysicalSize3D,
|
||||
|
||||
('Length', 'NodeSocketFloat', 1): ST.PhysicalLength,
|
||||
('Area', 'NodeSocketFloat', 1): ST.PhysicalArea,
|
||||
('Volume', 'NodeSocketFloat', 1): ST.PhysicalVolume,
|
||||
('Disp', 'NodeSocketVector', 2): ST.PhysicalDisp2D,
|
||||
('Disp', 'NodeSocketVector', 3): ST.PhysicalDisp3D,
|
||||
('Point', 'NodeSocketFloat', 1): ST.PhysicalPoint1D,
|
||||
('Point', 'NodeSocketVector', 2): ST.PhysicalPoint2D,
|
||||
('Point', 'NodeSocketVector', 3): ST.PhysicalPoint3D,
|
||||
('Size', 'NodeSocketVector', 2): ST.PhysicalSize2D,
|
||||
('Size', 'NodeSocketVector', 3): ST.PhysicalSize3D,
|
||||
## Mechanical
|
||||
("Mass", "NodeSocketFloat", 1): ST.PhysicalMass,
|
||||
|
||||
("Speed", "NodeSocketFloat", 1): ST.PhysicalSpeed,
|
||||
("Vel", "NodeSocketVector", 2): ST.PhysicalVel2D,
|
||||
("Vel", "NodeSocketVector", 3): ST.PhysicalVel3D,
|
||||
("Accel", "NodeSocketFloat", 1): ST.PhysicalAccelScalar,
|
||||
("Accel", "NodeSocketVector", 2): ST.PhysicalAccel2D,
|
||||
("Accel", "NodeSocketVector", 3): ST.PhysicalAccel3D,
|
||||
("Force", "NodeSocketFloat", 1): ST.PhysicalForceScalar,
|
||||
("Force", "NodeSocketVector", 2): ST.PhysicalForce2D,
|
||||
("Force", "NodeSocketVector", 3): ST.PhysicalForce3D,
|
||||
("Pressure", "NodeSocketFloat", 1): ST.PhysicalPressure,
|
||||
|
||||
('Mass', 'NodeSocketFloat', 1): ST.PhysicalMass,
|
||||
('Speed', 'NodeSocketFloat', 1): ST.PhysicalSpeed,
|
||||
('Vel', 'NodeSocketVector', 2): ST.PhysicalVel2D,
|
||||
('Vel', 'NodeSocketVector', 3): ST.PhysicalVel3D,
|
||||
('Accel', 'NodeSocketFloat', 1): ST.PhysicalAccelScalar,
|
||||
('Accel', 'NodeSocketVector', 2): ST.PhysicalAccel2D,
|
||||
('Accel', 'NodeSocketVector', 3): ST.PhysicalAccel3D,
|
||||
('Force', 'NodeSocketFloat', 1): ST.PhysicalForceScalar,
|
||||
('Force', 'NodeSocketVector', 2): ST.PhysicalForce2D,
|
||||
('Force', 'NodeSocketVector', 3): ST.PhysicalForce3D,
|
||||
('Pressure', 'NodeSocketFloat', 1): ST.PhysicalPressure,
|
||||
## Energetic
|
||||
("Energy", "NodeSocketFloat", 1): ST.PhysicalEnergy,
|
||||
("Power", "NodeSocketFloat", 1): ST.PhysicalPower,
|
||||
("Temp", "NodeSocketFloat", 1): ST.PhysicalTemp,
|
||||
|
||||
('Energy', 'NodeSocketFloat', 1): ST.PhysicalEnergy,
|
||||
('Power', 'NodeSocketFloat', 1): ST.PhysicalPower,
|
||||
('Temp', 'NodeSocketFloat', 1): ST.PhysicalTemp,
|
||||
## ELectrodynamical
|
||||
("Curr", "NodeSocketFloat", 1): ST.PhysicalCurr,
|
||||
("CurrDens", "NodeSocketVector", 2): ST.PhysicalCurrDens2D,
|
||||
("CurrDens", "NodeSocketVector", 3): ST.PhysicalCurrDens3D,
|
||||
|
||||
("Charge", "NodeSocketFloat", 1): ST.PhysicalCharge,
|
||||
("Voltage", "NodeSocketFloat", 1): ST.PhysicalVoltage,
|
||||
("Capacitance", "NodeSocketFloat", 1): ST.PhysicalCapacitance,
|
||||
("Resistance", "NodeSocketFloat", 1): ST.PhysicalResistance,
|
||||
("Conductance", "NodeSocketFloat", 1): ST.PhysicalConductance,
|
||||
|
||||
("MagFlux", "NodeSocketFloat", 1): ST.PhysicalMagFlux,
|
||||
("MagFluxDens", "NodeSocketFloat", 1): ST.PhysicalMagFluxDens,
|
||||
("Inductance", "NodeSocketFloat", 1): ST.PhysicalInductance,
|
||||
|
||||
("EField", "NodeSocketFloat", 2): ST.PhysicalEField3D,
|
||||
("EField", "NodeSocketFloat", 3): ST.PhysicalEField2D,
|
||||
("HField", "NodeSocketFloat", 2): ST.PhysicalHField3D,
|
||||
("HField", "NodeSocketFloat", 3): ST.PhysicalHField2D,
|
||||
|
||||
('Curr', 'NodeSocketFloat', 1): ST.PhysicalCurr,
|
||||
('CurrDens', 'NodeSocketVector', 2): ST.PhysicalCurrDens2D,
|
||||
('CurrDens', 'NodeSocketVector', 3): ST.PhysicalCurrDens3D,
|
||||
('Charge', 'NodeSocketFloat', 1): ST.PhysicalCharge,
|
||||
('Voltage', 'NodeSocketFloat', 1): ST.PhysicalVoltage,
|
||||
('Capacitance', 'NodeSocketFloat', 1): ST.PhysicalCapacitance,
|
||||
('Resistance', 'NodeSocketFloat', 1): ST.PhysicalResistance,
|
||||
('Conductance', 'NodeSocketFloat', 1): ST.PhysicalConductance,
|
||||
('MagFlux', 'NodeSocketFloat', 1): ST.PhysicalMagFlux,
|
||||
('MagFluxDens', 'NodeSocketFloat', 1): ST.PhysicalMagFluxDens,
|
||||
('Inductance', 'NodeSocketFloat', 1): ST.PhysicalInductance,
|
||||
('EField', 'NodeSocketFloat', 2): ST.PhysicalEField3D,
|
||||
('EField', 'NodeSocketFloat', 3): ST.PhysicalEField2D,
|
||||
('HField', 'NodeSocketFloat', 2): ST.PhysicalHField3D,
|
||||
('HField', 'NodeSocketFloat', 3): ST.PhysicalHField2D,
|
||||
## Luminal
|
||||
("LumIntensity", "NodeSocketFloat", 1): ST.PhysicalLumIntensity,
|
||||
("LumFlux", "NodeSocketFloat", 1): ST.PhysicalLumFlux,
|
||||
("Illuminance", "NodeSocketFloat", 1): ST.PhysicalIlluminance,
|
||||
|
||||
('LumIntensity', 'NodeSocketFloat', 1): ST.PhysicalLumIntensity,
|
||||
('LumFlux', 'NodeSocketFloat', 1): ST.PhysicalLumFlux,
|
||||
('Illuminance', 'NodeSocketFloat', 1): ST.PhysicalIlluminance,
|
||||
## Optical
|
||||
("PolJones", "NodeSocketFloat", 2): ST.PhysicalPolJones,
|
||||
("Pol", "NodeSocketFloat", 4): ST.PhysicalPol,
|
||||
('PolJones', 'NodeSocketFloat', 2): ST.PhysicalPolJones,
|
||||
('Pol', 'NodeSocketFloat', 4): ST.PhysicalPol,
|
||||
}
|
||||
|
|
|
@ -1,36 +1,33 @@
|
|||
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,
|
||||
('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,
|
||||
}
|
||||
|
|
|
@ -2,67 +2,61 @@ from .socket_types import SocketType as ST
|
|||
|
||||
SOCKET_SHAPES = {
|
||||
# Basic
|
||||
ST.Any: "CIRCLE",
|
||||
ST.Bool: "CIRCLE",
|
||||
ST.String: "CIRCLE",
|
||||
ST.FilePath: "CIRCLE",
|
||||
|
||||
ST.Any: 'CIRCLE',
|
||||
ST.Bool: 'CIRCLE',
|
||||
ST.String: 'CIRCLE',
|
||||
ST.FilePath: 'CIRCLE',
|
||||
# Number
|
||||
ST.IntegerNumber: "CIRCLE",
|
||||
ST.RationalNumber: "CIRCLE",
|
||||
ST.RealNumber: "CIRCLE",
|
||||
ST.ComplexNumber: "CIRCLE",
|
||||
|
||||
ST.IntegerNumber: 'CIRCLE',
|
||||
ST.RationalNumber: 'CIRCLE',
|
||||
ST.RealNumber: 'CIRCLE',
|
||||
ST.ComplexNumber: 'CIRCLE',
|
||||
# Vector
|
||||
ST.Integer2DVector: "CIRCLE",
|
||||
ST.Real2DVector: "CIRCLE",
|
||||
ST.Complex2DVector: "CIRCLE",
|
||||
ST.Integer3DVector: "CIRCLE",
|
||||
ST.Real3DVector: "CIRCLE",
|
||||
ST.Complex3DVector: "CIRCLE",
|
||||
|
||||
ST.Integer2DVector: 'CIRCLE',
|
||||
ST.Real2DVector: 'CIRCLE',
|
||||
ST.Complex2DVector: 'CIRCLE',
|
||||
ST.Integer3DVector: 'CIRCLE',
|
||||
ST.Real3DVector: 'CIRCLE',
|
||||
ST.Complex3DVector: 'CIRCLE',
|
||||
# Physical
|
||||
ST.PhysicalUnitSystem: "CIRCLE",
|
||||
ST.PhysicalTime: "CIRCLE",
|
||||
ST.PhysicalAngle: "CIRCLE",
|
||||
ST.PhysicalLength: "CIRCLE",
|
||||
ST.PhysicalArea: "CIRCLE",
|
||||
ST.PhysicalVolume: "CIRCLE",
|
||||
ST.PhysicalPoint2D: "CIRCLE",
|
||||
ST.PhysicalPoint3D: "CIRCLE",
|
||||
ST.PhysicalSize2D: "CIRCLE",
|
||||
ST.PhysicalSize3D: "CIRCLE",
|
||||
ST.PhysicalMass: "CIRCLE",
|
||||
ST.PhysicalSpeed: "CIRCLE",
|
||||
ST.PhysicalAccelScalar: "CIRCLE",
|
||||
ST.PhysicalForceScalar: "CIRCLE",
|
||||
ST.PhysicalAccel3D: "CIRCLE",
|
||||
ST.PhysicalForce3D: "CIRCLE",
|
||||
ST.PhysicalPol: "CIRCLE",
|
||||
ST.PhysicalFreq: "CIRCLE",
|
||||
|
||||
ST.PhysicalUnitSystem: 'CIRCLE',
|
||||
ST.PhysicalTime: 'CIRCLE',
|
||||
ST.PhysicalAngle: 'CIRCLE',
|
||||
ST.PhysicalLength: 'CIRCLE',
|
||||
ST.PhysicalArea: 'CIRCLE',
|
||||
ST.PhysicalVolume: 'CIRCLE',
|
||||
ST.PhysicalPoint2D: 'CIRCLE',
|
||||
ST.PhysicalPoint3D: 'CIRCLE',
|
||||
ST.PhysicalSize2D: 'CIRCLE',
|
||||
ST.PhysicalSize3D: 'CIRCLE',
|
||||
ST.PhysicalMass: 'CIRCLE',
|
||||
ST.PhysicalSpeed: 'CIRCLE',
|
||||
ST.PhysicalAccelScalar: 'CIRCLE',
|
||||
ST.PhysicalForceScalar: 'CIRCLE',
|
||||
ST.PhysicalAccel3D: 'CIRCLE',
|
||||
ST.PhysicalForce3D: 'CIRCLE',
|
||||
ST.PhysicalPol: 'CIRCLE',
|
||||
ST.PhysicalFreq: 'CIRCLE',
|
||||
# Blender
|
||||
ST.BlenderObject: "DIAMOND",
|
||||
ST.BlenderCollection: "DIAMOND",
|
||||
ST.BlenderImage: "DIAMOND",
|
||||
ST.BlenderGeoNodes: "DIAMOND",
|
||||
ST.BlenderText: "DIAMOND",
|
||||
|
||||
ST.BlenderObject: 'DIAMOND',
|
||||
ST.BlenderCollection: 'DIAMOND',
|
||||
ST.BlenderImage: 'DIAMOND',
|
||||
ST.BlenderGeoNodes: 'DIAMOND',
|
||||
ST.BlenderText: 'DIAMOND',
|
||||
# Maxwell
|
||||
ST.MaxwellSource: "CIRCLE",
|
||||
ST.MaxwellTemporalShape: "CIRCLE",
|
||||
ST.MaxwellMedium: "CIRCLE",
|
||||
ST.MaxwellMediumNonLinearity: "CIRCLE",
|
||||
ST.MaxwellStructure: "CIRCLE",
|
||||
ST.MaxwellBoundConds: "CIRCLE",
|
||||
ST.MaxwellBoundCond: "CIRCLE",
|
||||
ST.MaxwellMonitor: "CIRCLE",
|
||||
ST.MaxwellFDTDSim: "CIRCLE",
|
||||
ST.MaxwellFDTDSimData: "CIRCLE",
|
||||
ST.MaxwellSimGrid: "CIRCLE",
|
||||
ST.MaxwellSimGridAxis: "CIRCLE",
|
||||
ST.MaxwellSimDomain: "CIRCLE",
|
||||
|
||||
ST.MaxwellSource: 'CIRCLE',
|
||||
ST.MaxwellTemporalShape: 'CIRCLE',
|
||||
ST.MaxwellMedium: 'CIRCLE',
|
||||
ST.MaxwellMediumNonLinearity: 'CIRCLE',
|
||||
ST.MaxwellStructure: 'CIRCLE',
|
||||
ST.MaxwellBoundConds: 'CIRCLE',
|
||||
ST.MaxwellBoundCond: 'CIRCLE',
|
||||
ST.MaxwellMonitor: 'CIRCLE',
|
||||
ST.MaxwellFDTDSim: 'CIRCLE',
|
||||
ST.MaxwellFDTDSimData: 'CIRCLE',
|
||||
ST.MaxwellSimGrid: 'CIRCLE',
|
||||
ST.MaxwellSimGridAxis: 'CIRCLE',
|
||||
ST.MaxwellSimDomain: 'CIRCLE',
|
||||
# Tidy3D
|
||||
ST.Tidy3DCloudTask: "DIAMOND",
|
||||
ST.Tidy3DCloudTask: 'DIAMOND',
|
||||
}
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
import enum
|
||||
|
||||
from ....utils.blender_type_enum import (
|
||||
BlenderTypeEnum, append_cls_name_to_values, wrap_values_in_MT
|
||||
BlenderTypeEnum,
|
||||
append_cls_name_to_values,
|
||||
wrap_values_in_MT,
|
||||
)
|
||||
|
||||
|
||||
@append_cls_name_to_values
|
||||
class SocketType(BlenderTypeEnum):
|
||||
# Base
|
||||
|
@ -12,43 +15,43 @@ class SocketType(BlenderTypeEnum):
|
|||
String = enum.auto()
|
||||
FilePath = enum.auto()
|
||||
Color = enum.auto()
|
||||
|
||||
|
||||
# Number
|
||||
IntegerNumber = enum.auto()
|
||||
RationalNumber = enum.auto()
|
||||
RealNumber = enum.auto()
|
||||
ComplexNumber = enum.auto()
|
||||
|
||||
|
||||
# Vector
|
||||
Integer2DVector = enum.auto()
|
||||
Real2DVector = enum.auto()
|
||||
Real2DVectorDir = enum.auto()
|
||||
Complex2DVector = enum.auto()
|
||||
|
||||
|
||||
Integer3DVector = enum.auto()
|
||||
Real3DVector = enum.auto()
|
||||
Real3DVectorDir = enum.auto()
|
||||
Complex3DVector = enum.auto()
|
||||
|
||||
|
||||
# Blender
|
||||
BlenderObject = enum.auto()
|
||||
BlenderCollection = enum.auto()
|
||||
|
||||
|
||||
BlenderImage = enum.auto()
|
||||
|
||||
|
||||
BlenderGeoNodes = enum.auto()
|
||||
BlenderText = enum.auto()
|
||||
|
||||
|
||||
# Maxwell
|
||||
MaxwellBoundConds = enum.auto()
|
||||
MaxwellBoundCond = enum.auto()
|
||||
|
||||
|
||||
MaxwellMedium = enum.auto()
|
||||
MaxwellMediumNonLinearity = enum.auto()
|
||||
|
||||
|
||||
MaxwellSource = enum.auto()
|
||||
MaxwellTemporalShape = enum.auto()
|
||||
|
||||
|
||||
MaxwellStructure = enum.auto()
|
||||
MaxwellMonitor = enum.auto()
|
||||
|
||||
|
@ -57,42 +60,42 @@ class SocketType(BlenderTypeEnum):
|
|||
MaxwellSimDomain = enum.auto()
|
||||
MaxwellSimGrid = enum.auto()
|
||||
MaxwellSimGridAxis = 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()
|
||||
|
@ -103,37 +106,37 @@ class SocketType(BlenderTypeEnum):
|
|||
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()
|
||||
|
|
|
@ -5,262 +5,255 @@ from .socket_types import SocketType as ST
|
|||
|
||||
SOCKET_UNITS = {
|
||||
ST.PhysicalTime: {
|
||||
"default": "PS",
|
||||
"values": {
|
||||
"FS": spux.femtosecond,
|
||||
"PS": spu.picosecond,
|
||||
"NS": spu.nanosecond,
|
||||
"MS": spu.microsecond,
|
||||
"MLSEC": spu.millisecond,
|
||||
"SEC": spu.second,
|
||||
"MIN": spu.minute,
|
||||
"HOUR": spu.hour,
|
||||
"DAY": spu.day,
|
||||
'default': 'PS',
|
||||
'values': {
|
||||
'FS': spux.femtosecond,
|
||||
'PS': spu.picosecond,
|
||||
'NS': spu.nanosecond,
|
||||
'MS': spu.microsecond,
|
||||
'MLSEC': spu.millisecond,
|
||||
'SEC': spu.second,
|
||||
'MIN': spu.minute,
|
||||
'HOUR': spu.hour,
|
||||
'DAY': spu.day,
|
||||
},
|
||||
},
|
||||
|
||||
ST.PhysicalAngle: {
|
||||
"default": "RADIAN",
|
||||
"values": {
|
||||
"RADIAN": spu.radian,
|
||||
"DEGREE": spu.degree,
|
||||
"STERAD": spu.steradian,
|
||||
"ANGMIL": spu.angular_mil,
|
||||
'default': 'RADIAN',
|
||||
'values': {
|
||||
'RADIAN': spu.radian,
|
||||
'DEGREE': spu.degree,
|
||||
'STERAD': spu.steradian,
|
||||
'ANGMIL': spu.angular_mil,
|
||||
},
|
||||
},
|
||||
|
||||
ST.PhysicalLength: {
|
||||
"default": "UM",
|
||||
"values": {
|
||||
"PM": spu.picometer,
|
||||
"A": spu.angstrom,
|
||||
"NM": spu.nanometer,
|
||||
"UM": spu.micrometer,
|
||||
"MM": spu.millimeter,
|
||||
"CM": spu.centimeter,
|
||||
"M": spu.meter,
|
||||
"INCH": spu.inch,
|
||||
"FOOT": spu.foot,
|
||||
"YARD": spu.yard,
|
||||
"MILE": spu.mile,
|
||||
'default': 'UM',
|
||||
'values': {
|
||||
'PM': spu.picometer,
|
||||
'A': spu.angstrom,
|
||||
'NM': spu.nanometer,
|
||||
'UM': spu.micrometer,
|
||||
'MM': spu.millimeter,
|
||||
'CM': spu.centimeter,
|
||||
'M': spu.meter,
|
||||
'INCH': spu.inch,
|
||||
'FOOT': spu.foot,
|
||||
'YARD': spu.yard,
|
||||
'MILE': spu.mile,
|
||||
},
|
||||
},
|
||||
ST.PhysicalArea: {
|
||||
"default": "UM_SQ",
|
||||
"values": {
|
||||
"PM_SQ": spu.picometer**2,
|
||||
"A_SQ": spu.angstrom**2,
|
||||
"NM_SQ": spu.nanometer**2,
|
||||
"UM_SQ": spu.micrometer**2,
|
||||
"MM_SQ": spu.millimeter**2,
|
||||
"CM_SQ": spu.centimeter**2,
|
||||
"M_SQ": spu.meter**2,
|
||||
"INCH_SQ": spu.inch**2,
|
||||
"FOOT_SQ": spu.foot**2,
|
||||
"YARD_SQ": spu.yard**2,
|
||||
"MILE_SQ": spu.mile**2,
|
||||
'default': 'UM_SQ',
|
||||
'values': {
|
||||
'PM_SQ': spu.picometer**2,
|
||||
'A_SQ': spu.angstrom**2,
|
||||
'NM_SQ': spu.nanometer**2,
|
||||
'UM_SQ': spu.micrometer**2,
|
||||
'MM_SQ': spu.millimeter**2,
|
||||
'CM_SQ': spu.centimeter**2,
|
||||
'M_SQ': spu.meter**2,
|
||||
'INCH_SQ': spu.inch**2,
|
||||
'FOOT_SQ': spu.foot**2,
|
||||
'YARD_SQ': spu.yard**2,
|
||||
'MILE_SQ': spu.mile**2,
|
||||
},
|
||||
},
|
||||
ST.PhysicalVolume: {
|
||||
"default": "UM_CB",
|
||||
"values": {
|
||||
"PM_CB": spu.picometer**3,
|
||||
"A_CB": spu.angstrom**3,
|
||||
"NM_CB": spu.nanometer**3,
|
||||
"UM_CB": spu.micrometer**3,
|
||||
"MM_CB": spu.millimeter**3,
|
||||
"CM_CB": spu.centimeter**3,
|
||||
"M_CB": spu.meter**3,
|
||||
"ML": spu.milliliter,
|
||||
"L": spu.liter,
|
||||
"INCH_CB": spu.inch**3,
|
||||
"FOOT_CB": spu.foot**3,
|
||||
"YARD_CB": spu.yard**3,
|
||||
"MILE_CB": spu.mile**3,
|
||||
'default': 'UM_CB',
|
||||
'values': {
|
||||
'PM_CB': spu.picometer**3,
|
||||
'A_CB': spu.angstrom**3,
|
||||
'NM_CB': spu.nanometer**3,
|
||||
'UM_CB': spu.micrometer**3,
|
||||
'MM_CB': spu.millimeter**3,
|
||||
'CM_CB': spu.centimeter**3,
|
||||
'M_CB': spu.meter**3,
|
||||
'ML': spu.milliliter,
|
||||
'L': spu.liter,
|
||||
'INCH_CB': spu.inch**3,
|
||||
'FOOT_CB': spu.foot**3,
|
||||
'YARD_CB': spu.yard**3,
|
||||
'MILE_CB': spu.mile**3,
|
||||
},
|
||||
},
|
||||
|
||||
ST.PhysicalPoint2D: {
|
||||
"default": "UM",
|
||||
"values": {
|
||||
"PM": spu.picometer,
|
||||
"A": spu.angstrom,
|
||||
"NM": spu.nanometer,
|
||||
"UM": spu.micrometer,
|
||||
"MM": spu.millimeter,
|
||||
"CM": spu.centimeter,
|
||||
"M": spu.meter,
|
||||
"INCH": spu.inch,
|
||||
"FOOT": spu.foot,
|
||||
"YARD": spu.yard,
|
||||
"MILE": spu.mile,
|
||||
'default': 'UM',
|
||||
'values': {
|
||||
'PM': spu.picometer,
|
||||
'A': spu.angstrom,
|
||||
'NM': spu.nanometer,
|
||||
'UM': spu.micrometer,
|
||||
'MM': spu.millimeter,
|
||||
'CM': spu.centimeter,
|
||||
'M': spu.meter,
|
||||
'INCH': spu.inch,
|
||||
'FOOT': spu.foot,
|
||||
'YARD': spu.yard,
|
||||
'MILE': spu.mile,
|
||||
},
|
||||
},
|
||||
ST.PhysicalPoint3D: {
|
||||
"default": "UM",
|
||||
"values": {
|
||||
"PM": spu.picometer,
|
||||
"A": spu.angstrom,
|
||||
"NM": spu.nanometer,
|
||||
"UM": spu.micrometer,
|
||||
"MM": spu.millimeter,
|
||||
"CM": spu.centimeter,
|
||||
"M": spu.meter,
|
||||
"INCH": spu.inch,
|
||||
"FOOT": spu.foot,
|
||||
"YARD": spu.yard,
|
||||
"MILE": spu.mile,
|
||||
'default': 'UM',
|
||||
'values': {
|
||||
'PM': spu.picometer,
|
||||
'A': spu.angstrom,
|
||||
'NM': spu.nanometer,
|
||||
'UM': spu.micrometer,
|
||||
'MM': spu.millimeter,
|
||||
'CM': spu.centimeter,
|
||||
'M': spu.meter,
|
||||
'INCH': spu.inch,
|
||||
'FOOT': spu.foot,
|
||||
'YARD': spu.yard,
|
||||
'MILE': spu.mile,
|
||||
},
|
||||
},
|
||||
|
||||
ST.PhysicalSize2D: {
|
||||
"default": "UM",
|
||||
"values": {
|
||||
"PM": spu.picometer,
|
||||
"A": spu.angstrom,
|
||||
"NM": spu.nanometer,
|
||||
"UM": spu.micrometer,
|
||||
"MM": spu.millimeter,
|
||||
"CM": spu.centimeter,
|
||||
"M": spu.meter,
|
||||
"INCH": spu.inch,
|
||||
"FOOT": spu.foot,
|
||||
"YARD": spu.yard,
|
||||
"MILE": spu.mile,
|
||||
'default': 'UM',
|
||||
'values': {
|
||||
'PM': spu.picometer,
|
||||
'A': spu.angstrom,
|
||||
'NM': spu.nanometer,
|
||||
'UM': spu.micrometer,
|
||||
'MM': spu.millimeter,
|
||||
'CM': spu.centimeter,
|
||||
'M': spu.meter,
|
||||
'INCH': spu.inch,
|
||||
'FOOT': spu.foot,
|
||||
'YARD': spu.yard,
|
||||
'MILE': spu.mile,
|
||||
},
|
||||
},
|
||||
ST.PhysicalSize3D: {
|
||||
"default": "UM",
|
||||
"values": {
|
||||
"PM": spu.picometer,
|
||||
"A": spu.angstrom,
|
||||
"NM": spu.nanometer,
|
||||
"UM": spu.micrometer,
|
||||
"MM": spu.millimeter,
|
||||
"CM": spu.centimeter,
|
||||
"M": spu.meter,
|
||||
"INCH": spu.inch,
|
||||
"FOOT": spu.foot,
|
||||
"YARD": spu.yard,
|
||||
"MILE": spu.mile,
|
||||
'default': 'UM',
|
||||
'values': {
|
||||
'PM': spu.picometer,
|
||||
'A': spu.angstrom,
|
||||
'NM': spu.nanometer,
|
||||
'UM': spu.micrometer,
|
||||
'MM': spu.millimeter,
|
||||
'CM': spu.centimeter,
|
||||
'M': spu.meter,
|
||||
'INCH': spu.inch,
|
||||
'FOOT': spu.foot,
|
||||
'YARD': spu.yard,
|
||||
'MILE': spu.mile,
|
||||
},
|
||||
},
|
||||
|
||||
ST.PhysicalMass: {
|
||||
"default": "UG",
|
||||
"values": {
|
||||
"E_REST": spu.electron_rest_mass,
|
||||
"DAL": spu.dalton,
|
||||
"UG": spu.microgram,
|
||||
"MG": spu.milligram,
|
||||
"G": spu.gram,
|
||||
"KG": spu.kilogram,
|
||||
"TON": spu.metric_ton,
|
||||
'default': 'UG',
|
||||
'values': {
|
||||
'E_REST': spu.electron_rest_mass,
|
||||
'DAL': spu.dalton,
|
||||
'UG': spu.microgram,
|
||||
'MG': spu.milligram,
|
||||
'G': spu.gram,
|
||||
'KG': spu.kilogram,
|
||||
'TON': spu.metric_ton,
|
||||
},
|
||||
},
|
||||
|
||||
ST.PhysicalSpeed: {
|
||||
"default": "UM_S",
|
||||
"values": {
|
||||
"PM_S": spu.picometer / spu.second,
|
||||
"NM_S": spu.nanometer / spu.second,
|
||||
"UM_S": spu.micrometer / spu.second,
|
||||
"MM_S": spu.millimeter / spu.second,
|
||||
"M_S": spu.meter / spu.second,
|
||||
"KM_S": spu.kilometer / spu.second,
|
||||
"KM_H": spu.kilometer / spu.hour,
|
||||
"FT_S": spu.feet / spu.second,
|
||||
"MI_H": spu.mile / spu.hour,
|
||||
'default': 'UM_S',
|
||||
'values': {
|
||||
'PM_S': spu.picometer / spu.second,
|
||||
'NM_S': spu.nanometer / spu.second,
|
||||
'UM_S': spu.micrometer / spu.second,
|
||||
'MM_S': spu.millimeter / spu.second,
|
||||
'M_S': spu.meter / spu.second,
|
||||
'KM_S': spu.kilometer / spu.second,
|
||||
'KM_H': spu.kilometer / spu.hour,
|
||||
'FT_S': spu.feet / spu.second,
|
||||
'MI_H': spu.mile / spu.hour,
|
||||
},
|
||||
},
|
||||
ST.PhysicalAccelScalar: {
|
||||
"default": "UM_S_SQ",
|
||||
"values": {
|
||||
"PM_S_SQ": spu.picometer / spu.second**2,
|
||||
"NM_S_SQ": spu.nanometer / spu.second**2,
|
||||
"UM_S_SQ": spu.micrometer / spu.second**2,
|
||||
"MM_S_SQ": spu.millimeter / spu.second**2,
|
||||
"M_S_SQ": spu.meter / spu.second**2,
|
||||
"KM_S_SQ": spu.kilometer / spu.second**2,
|
||||
"FT_S_SQ": spu.feet / spu.second**2,
|
||||
'default': 'UM_S_SQ',
|
||||
'values': {
|
||||
'PM_S_SQ': spu.picometer / spu.second**2,
|
||||
'NM_S_SQ': spu.nanometer / spu.second**2,
|
||||
'UM_S_SQ': spu.micrometer / spu.second**2,
|
||||
'MM_S_SQ': spu.millimeter / spu.second**2,
|
||||
'M_S_SQ': spu.meter / spu.second**2,
|
||||
'KM_S_SQ': spu.kilometer / spu.second**2,
|
||||
'FT_S_SQ': spu.feet / spu.second**2,
|
||||
},
|
||||
},
|
||||
ST.PhysicalForceScalar: {
|
||||
"default": "UNEWT",
|
||||
"values": {
|
||||
"KG_M_S_SQ": spu.kg * spu.m/spu.second**2,
|
||||
"NNEWT": spux.nanonewton,
|
||||
"UNEWT": spux.micronewton,
|
||||
"MNEWT": spux.millinewton,
|
||||
"NEWT": spu.newton,
|
||||
'default': 'UNEWT',
|
||||
'values': {
|
||||
'KG_M_S_SQ': spu.kg * spu.m / spu.second**2,
|
||||
'NNEWT': spux.nanonewton,
|
||||
'UNEWT': spux.micronewton,
|
||||
'MNEWT': spux.millinewton,
|
||||
'NEWT': spu.newton,
|
||||
},
|
||||
},
|
||||
ST.PhysicalAccel3D: {
|
||||
"default": "UM_S_SQ",
|
||||
"values": {
|
||||
"PM_S_SQ": spu.picometer / spu.second**2,
|
||||
"NM_S_SQ": spu.nanometer / spu.second**2,
|
||||
"UM_S_SQ": spu.micrometer / spu.second**2,
|
||||
"MM_S_SQ": spu.millimeter / spu.second**2,
|
||||
"M_S_SQ": spu.meter / spu.second**2,
|
||||
"KM_S_SQ": spu.kilometer / spu.second**2,
|
||||
"FT_S_SQ": spu.feet / spu.second**2,
|
||||
'default': 'UM_S_SQ',
|
||||
'values': {
|
||||
'PM_S_SQ': spu.picometer / spu.second**2,
|
||||
'NM_S_SQ': spu.nanometer / spu.second**2,
|
||||
'UM_S_SQ': spu.micrometer / spu.second**2,
|
||||
'MM_S_SQ': spu.millimeter / spu.second**2,
|
||||
'M_S_SQ': spu.meter / spu.second**2,
|
||||
'KM_S_SQ': spu.kilometer / spu.second**2,
|
||||
'FT_S_SQ': spu.feet / spu.second**2,
|
||||
},
|
||||
},
|
||||
ST.PhysicalForce3D: {
|
||||
"default": "UNEWT",
|
||||
"values": {
|
||||
"KG_M_S_SQ": spu.kg * spu.m/spu.second**2,
|
||||
"NNEWT": spux.nanonewton,
|
||||
"UNEWT": spux.micronewton,
|
||||
"MNEWT": spux.millinewton,
|
||||
"NEWT": spu.newton,
|
||||
'default': 'UNEWT',
|
||||
'values': {
|
||||
'KG_M_S_SQ': spu.kg * spu.m / spu.second**2,
|
||||
'NNEWT': spux.nanonewton,
|
||||
'UNEWT': spux.micronewton,
|
||||
'MNEWT': spux.millinewton,
|
||||
'NEWT': spu.newton,
|
||||
},
|
||||
},
|
||||
|
||||
ST.PhysicalFreq: {
|
||||
"default": "THZ",
|
||||
"values": {
|
||||
"HZ": spu.hertz,
|
||||
"KHZ": spux.kilohertz,
|
||||
"MHZ": spux.megahertz,
|
||||
"GHZ": spux.gigahertz,
|
||||
"THZ": spux.terahertz,
|
||||
"PHZ": spux.petahertz,
|
||||
"EHZ": spux.exahertz,
|
||||
'default': 'THZ',
|
||||
'values': {
|
||||
'HZ': spu.hertz,
|
||||
'KHZ': spux.kilohertz,
|
||||
'MHZ': spux.megahertz,
|
||||
'GHZ': spux.gigahertz,
|
||||
'THZ': spux.terahertz,
|
||||
'PHZ': spux.petahertz,
|
||||
'EHZ': spux.exahertz,
|
||||
},
|
||||
},
|
||||
ST.PhysicalPol: {
|
||||
"default": "RADIAN",
|
||||
"values": {
|
||||
"RADIAN": spu.radian,
|
||||
"DEGREE": spu.degree,
|
||||
"STERAD": spu.steradian,
|
||||
"ANGMIL": spu.angular_mil,
|
||||
'default': 'RADIAN',
|
||||
'values': {
|
||||
'RADIAN': spu.radian,
|
||||
'DEGREE': spu.degree,
|
||||
'STERAD': spu.steradian,
|
||||
'ANGMIL': spu.angular_mil,
|
||||
},
|
||||
},
|
||||
ST.MaxwellMedium: {
|
||||
"default": "NM",
|
||||
"values": {
|
||||
"PM": spu.picometer, ## c(vac) = wl*freq
|
||||
"A": spu.angstrom,
|
||||
"NM": spu.nanometer,
|
||||
"UM": spu.micrometer,
|
||||
"MM": spu.millimeter,
|
||||
"CM": spu.centimeter,
|
||||
"M": spu.meter,
|
||||
'default': 'NM',
|
||||
'values': {
|
||||
'PM': spu.picometer, ## c(vac) = wl*freq
|
||||
'A': spu.angstrom,
|
||||
'NM': spu.nanometer,
|
||||
'UM': spu.micrometer,
|
||||
'MM': spu.millimeter,
|
||||
'CM': spu.centimeter,
|
||||
'M': spu.meter,
|
||||
},
|
||||
},
|
||||
ST.MaxwellMonitor: {
|
||||
"default": "NM",
|
||||
"values": {
|
||||
"PM": spu.picometer, ## c(vac) = wl*freq
|
||||
"A": spu.angstrom,
|
||||
"NM": spu.nanometer,
|
||||
"UM": spu.micrometer,
|
||||
"MM": spu.millimeter,
|
||||
"CM": spu.centimeter,
|
||||
"M": spu.meter,
|
||||
'default': 'NM',
|
||||
'values': {
|
||||
'PM': spu.picometer, ## c(vac) = wl*freq
|
||||
'A': spu.angstrom,
|
||||
'NM': spu.nanometer,
|
||||
'UM': spu.micrometer,
|
||||
'MM': spu.millimeter,
|
||||
'CM': spu.centimeter,
|
||||
'M': spu.meter,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import enum
|
||||
|
||||
from ....utils.blender_type_enum import (
|
||||
BlenderTypeEnum, append_cls_name_to_values
|
||||
BlenderTypeEnum,
|
||||
append_cls_name_to_values,
|
||||
)
|
||||
|
||||
|
||||
@append_cls_name_to_values
|
||||
class TreeType(BlenderTypeEnum):
|
||||
MaxwellSim = enum.auto()
|
||||
|
|
|
@ -10,48 +10,49 @@ import bpy
|
|||
|
||||
from .. import contracts as ct
|
||||
|
||||
AREA_TYPE = "IMAGE_EDITOR"
|
||||
SPACE_TYPE = "IMAGE_EDITOR"
|
||||
AREA_TYPE = 'IMAGE_EDITOR'
|
||||
SPACE_TYPE = 'IMAGE_EDITOR'
|
||||
|
||||
|
||||
class ManagedBLImage(ct.schemas.ManagedObj):
|
||||
managed_obj_type = ct.ManagedObjType.ManagedBLImage
|
||||
_bl_image_name: str
|
||||
|
||||
|
||||
def __init__(self, name: str):
|
||||
## TODO: Check that blender doesn't have any other images by the same name.
|
||||
self._bl_image_name = name
|
||||
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._bl_image_name
|
||||
|
||||
|
||||
@name.setter
|
||||
def name(self, value: str):
|
||||
# Image Doesn't Exist
|
||||
# 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"
|
||||
msg = f'Desired name {value} for BL image is taken'
|
||||
raise ValueError(msg)
|
||||
|
||||
|
||||
# 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 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
|
||||
####################
|
||||
|
@ -59,27 +60,24 @@ class ManagedBLImage(ct.schemas.ManagedObj):
|
|||
self,
|
||||
width_px: int,
|
||||
height_px: int,
|
||||
color_model: typx.Literal["RGB", "RGBA"],
|
||||
dtype: typx.Literal["uint8", "float32"],
|
||||
color_model: typx.Literal['RGB', 'RGBA'],
|
||||
dtype: typx.Literal['uint8', 'float32'],
|
||||
):
|
||||
"""Returns the managed blender image.
|
||||
|
||||
|
||||
If the requested image properties are different from the image's, then delete the old image make a new image with correct geometry.
|
||||
"""
|
||||
channels = 4 if color_model == "RGBA" else 3
|
||||
|
||||
channels = 4 if color_model == 'RGBA' else 3
|
||||
|
||||
# Remove Image (if mismatch)
|
||||
if (
|
||||
(bl_image := bpy.data.images.get(self.name))
|
||||
and (
|
||||
bl_image.size[0] != width_px
|
||||
or bl_image.size[1] != height_px
|
||||
or bl_image.channels != channels
|
||||
or bl_image.is_float ^ (dtype == "float32")
|
||||
)
|
||||
if (bl_image := bpy.data.images.get(self.name)) and (
|
||||
bl_image.size[0] != width_px
|
||||
or bl_image.size[1] != height_px
|
||||
or bl_image.channels != channels
|
||||
or bl_image.is_float ^ (dtype == 'float32')
|
||||
):
|
||||
self.free()
|
||||
|
||||
|
||||
# Create Image w/Geometry (if none exists)
|
||||
if not (bl_image := bpy.data.images.get(self.name)):
|
||||
bl_image = bpy.data.images.new(
|
||||
|
@ -87,9 +85,9 @@ class ManagedBLImage(ct.schemas.ManagedObj):
|
|||
width=width_px,
|
||||
height=height_px,
|
||||
)
|
||||
|
||||
|
||||
return bl_image
|
||||
|
||||
|
||||
####################
|
||||
# - Editor UX Manipulation
|
||||
####################
|
||||
|
@ -99,25 +97,23 @@ class ManagedBLImage(ct.schemas.ManagedObj):
|
|||
If none are valid, return None.
|
||||
"""
|
||||
valid_areas = [
|
||||
area
|
||||
for area in bpy.context.screen.areas
|
||||
if area.type == AREA_TYPE
|
||||
area for area in bpy.context.screen.areas if area.type == AREA_TYPE
|
||||
]
|
||||
if valid_areas:
|
||||
return valid_areas[0]
|
||||
|
||||
|
||||
@property
|
||||
def preview_space(self) -> bpy.types.SpaceProperties:
|
||||
"""Returns the visible preview space in the visible preview area of
|
||||
the Blender UI
|
||||
"""
|
||||
if (preview_area := self.preview_area):
|
||||
if preview_area := self.preview_area:
|
||||
return next(
|
||||
space
|
||||
for space in preview_area.spaces
|
||||
if space.type == SPACE_TYPE
|
||||
)
|
||||
|
||||
|
||||
####################
|
||||
# - Actions
|
||||
####################
|
||||
|
@ -125,9 +121,9 @@ class ManagedBLImage(ct.schemas.ManagedObj):
|
|||
"""Synchronizes the managed object to the preview, by manipulating
|
||||
relevant editors.
|
||||
"""
|
||||
if (bl_image := bpy.data.images.get(self.name)):
|
||||
if bl_image := bpy.data.images.get(self.name):
|
||||
self.preview_space.image = bl_image
|
||||
|
||||
|
||||
####################
|
||||
# - Special Methods
|
||||
####################
|
||||
|
@ -140,37 +136,37 @@ class ManagedBLImage(ct.schemas.ManagedObj):
|
|||
bl_select: bool = False,
|
||||
):
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
|
||||
# Compute Image Geometry
|
||||
if (preview_area := self.preview_area):
|
||||
if preview_area := self.preview_area:
|
||||
# Retrieve DPI from Blender Preferences
|
||||
_dpi = bpy.context.preferences.system.dpi
|
||||
|
||||
|
||||
# Retrieve Image Geometry from Area
|
||||
width_px = preview_area.width
|
||||
height_px = preview_area.height
|
||||
|
||||
|
||||
# Compute Inches
|
||||
_width_inches = width_px / _dpi
|
||||
_height_inches = height_px / _dpi
|
||||
|
||||
|
||||
elif width_inches and height_inches and dpi:
|
||||
# Copy Parameters
|
||||
_dpi = dpi
|
||||
_width_inches = height_inches
|
||||
_height_inches = height_inches
|
||||
|
||||
|
||||
# Compute Pixel Geometry
|
||||
width_px = int(_width_inches * _dpi)
|
||||
height_px = int(_height_inches * _dpi)
|
||||
|
||||
|
||||
else:
|
||||
msg = f"There must either be a preview area, or defined `width_inches`, `height_inches`, and `dpi`"
|
||||
msg = f'There must either be a preview area, or defined `width_inches`, `height_inches`, and `dpi`'
|
||||
raise ValueError(msg)
|
||||
|
||||
|
||||
# Compute Plot Dimensions
|
||||
aspect_ratio = _width_inches / _height_inches
|
||||
|
||||
|
||||
# Create MPL Figure, Axes, and Compute Figure Geometry
|
||||
fig, ax = plt.subplots(
|
||||
figsize=[_width_inches, _height_inches],
|
||||
|
@ -179,10 +175,10 @@ class ManagedBLImage(ct.schemas.ManagedObj):
|
|||
ax.set_aspect(aspect_ratio)
|
||||
cmp_width_px, cmp_height_px = fig.canvas.get_width_height()
|
||||
## Use computed pixel w/h to preempt off-by-one size errors.
|
||||
|
||||
|
||||
# Plot w/User Parameter
|
||||
func_plotter(ax)
|
||||
|
||||
|
||||
# Save Figure to BytesIO
|
||||
with io.BytesIO() as buff:
|
||||
fig.savefig(buff, format='raw', dpi=dpi)
|
||||
|
@ -191,15 +187,14 @@ class ManagedBLImage(ct.schemas.ManagedObj):
|
|||
buff.getvalue(),
|
||||
dtype=np.uint8,
|
||||
).reshape([cmp_height_px, cmp_width_px, -1])
|
||||
|
||||
|
||||
image_data = np.flipud(image_data).astype(np.float32) / 255
|
||||
plt.close(fig)
|
||||
|
||||
|
||||
# Optimized Write to Blender Image
|
||||
bl_image = self.bl_image(cmp_width_px, cmp_height_px, "RGBA", "uint8")
|
||||
bl_image = self.bl_image(cmp_width_px, cmp_height_px, 'RGBA', 'uint8')
|
||||
bl_image.pixels.foreach_set(image_data.ravel())
|
||||
bl_image.update()
|
||||
|
||||
|
||||
if bl_select:
|
||||
self.bl_select()
|
||||
|
||||
|
|
|
@ -13,13 +13,14 @@ import bmesh
|
|||
|
||||
from .. import contracts as ct
|
||||
|
||||
ModifierType = typx.Literal["NODES", "ARRAY"]
|
||||
ModifierType = typx.Literal['NODES', 'ARRAY']
|
||||
MODIFIER_NAMES = {
|
||||
"NODES": "BLMaxwell_GeoNodes",
|
||||
"ARRAY": "BLMaxwell_Array",
|
||||
'NODES': 'BLMaxwell_GeoNodes',
|
||||
'ARRAY': 'BLMaxwell_Array',
|
||||
}
|
||||
MANAGED_COLLECTION_NAME = "BLMaxwell"
|
||||
PREVIEW_COLLECTION_NAME = "BLMaxwell Visible"
|
||||
MANAGED_COLLECTION_NAME = 'BLMaxwell'
|
||||
PREVIEW_COLLECTION_NAME = 'BLMaxwell Visible'
|
||||
|
||||
|
||||
def bl_collection(
|
||||
collection_name: str, view_layer_exclude: bool
|
||||
|
@ -27,113 +28,133 @@ def bl_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)
|
||||
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:
|
||||
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):
|
||||
self._bl_object_name = name
|
||||
|
||||
|
||||
# Object Name
|
||||
@property
|
||||
def name(self):
|
||||
return self._bl_object_name
|
||||
|
||||
|
||||
@name.setter
|
||||
def set_name(self, value: str) -> None:
|
||||
# Object Doesn't Exist
|
||||
# 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"
|
||||
msg = f'Desired name {value} for BL object is taken'
|
||||
raise ValueError(msg)
|
||||
|
||||
|
||||
# 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.name
|
||||
|
||||
|
||||
@property
|
||||
def bl_volume_name(self):
|
||||
return self.name
|
||||
|
||||
|
||||
# Deallocation
|
||||
def free(self):
|
||||
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":
|
||||
if bl_object.type == 'MESH':
|
||||
bpy.data.meshes.remove(bl_object.data)
|
||||
elif bl_object.type == "EMPTY":
|
||||
elif bl_object.type == 'EMPTY':
|
||||
bpy.data.meshes.remove(bl_object.data)
|
||||
elif bl_object.type == "VOLUME":
|
||||
elif bl_object.type == 'VOLUME':
|
||||
bpy.data.volumes.remove(bl_object.data)
|
||||
else:
|
||||
msg = f"Type of to-delete `bl_object`, {bl_object.type}, is not valid"
|
||||
msg = f'Type of to-delete `bl_object`, {bl_object.type}, is not valid'
|
||||
raise ValueError(msg)
|
||||
|
||||
|
||||
####################
|
||||
# - Actions
|
||||
####################
|
||||
def show_preview(
|
||||
self,
|
||||
kind: typx.Literal["MESH", "EMPTY", "VOLUME"],
|
||||
kind: typx.Literal['MESH', 'EMPTY', 'VOLUME'],
|
||||
empty_display_type: typx.Literal[
|
||||
"PLAIN_AXES", "ARROWS", "SINGLE_ARROW", "CIRCLE", "CUBE",
|
||||
"SPHERE", "CONE", "IMAGE",
|
||||
] | None = None,
|
||||
'PLAIN_AXES',
|
||||
'ARROWS',
|
||||
'SINGLE_ARROW',
|
||||
'CIRCLE',
|
||||
'CUBE',
|
||||
'SPHERE',
|
||||
'CONE',
|
||||
'IMAGE',
|
||||
]
|
||||
| None = None,
|
||||
) -> None:
|
||||
"""Moves the managed Blender object to the preview collection.
|
||||
|
||||
|
||||
If it's already included, do nothing.
|
||||
"""
|
||||
bl_object = self.bl_object(kind)
|
||||
if bl_object.name not in (preview_collection := bl_collection(
|
||||
PREVIEW_COLLECTION_NAME, view_layer_exclude=False
|
||||
)).objects:
|
||||
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:
|
||||
|
||||
if kind == 'EMPTY' and empty_display_type is not None:
|
||||
bl_object.empty_display_type = empty_display_type
|
||||
|
||||
|
||||
def hide_preview(
|
||||
self,
|
||||
kind: typx.Literal["MESH", "EMPTY", "VOLUME"],
|
||||
kind: typx.Literal['MESH', 'EMPTY', 'VOLUME'],
|
||||
) -> None:
|
||||
"""Removes the managed Blender object from the preview collection.
|
||||
|
||||
|
||||
If it's already removed, do nothing.
|
||||
"""
|
||||
bl_object = self.bl_object(kind)
|
||||
if bl_object.name not in (preview_collection := bl_collection(
|
||||
PREVIEW_COLLECTION_NAME, view_layer_exclude=False
|
||||
)).objects:
|
||||
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:
|
||||
|
@ -141,68 +162,68 @@ class ManagedBLObject(ct.schemas.ManagedObj):
|
|||
outlined in the 3D viewport.
|
||||
"""
|
||||
if not (bl_object := bpy.data.objects.get(self.name)):
|
||||
msg = "Managed BLObject does not exist"
|
||||
msg = 'Managed BLObject does not exist'
|
||||
raise ValueError(msg)
|
||||
|
||||
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
bl_object.select_set(True)
|
||||
|
||||
|
||||
####################
|
||||
# - Managed Object Management
|
||||
####################
|
||||
def bl_object(
|
||||
self,
|
||||
kind: typx.Literal["MESH", "EMPTY", "VOLUME"],
|
||||
kind: typx.Literal['MESH', 'EMPTY', 'VOLUME'],
|
||||
):
|
||||
"""Returns the managed blender object.
|
||||
|
||||
|
||||
If the requested object data type 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
|
||||
):
|
||||
bl_object := bpy.data.objects.get(self.name)
|
||||
) and bl_object.type != kind:
|
||||
self.free()
|
||||
|
||||
|
||||
# Create Object w/Appropriate Data Block
|
||||
if not (bl_object := bpy.data.objects.get(self.name)):
|
||||
if kind == "MESH":
|
||||
if kind == 'MESH':
|
||||
bl_data = bpy.data.meshes.new(self.bl_mesh_name)
|
||||
elif kind == "EMPTY":
|
||||
elif kind == 'EMPTY':
|
||||
bl_data = None
|
||||
elif kind == "VOLUME":
|
||||
elif kind == 'VOLUME':
|
||||
raise NotImplementedError
|
||||
else:
|
||||
msg = f"Requested `bl_object` type {bl_object.type} is not valid"
|
||||
msg = (
|
||||
f'Requested `bl_object` type {bl_object.type} is not valid'
|
||||
)
|
||||
raise ValueError(msg)
|
||||
|
||||
|
||||
bl_object = bpy.data.objects.new(self.name, bl_data)
|
||||
bl_collection(
|
||||
MANAGED_COLLECTION_NAME, view_layer_exclude=True
|
||||
).objects.link(bl_object)
|
||||
|
||||
|
||||
return bl_object
|
||||
|
||||
|
||||
####################
|
||||
# - Mesh Data Properties
|
||||
####################
|
||||
@property
|
||||
def raw_mesh(self) -> bpy.types.Mesh:
|
||||
"""Returns the object's raw mesh data.
|
||||
|
||||
|
||||
Raises an error if the object has no mesh data.
|
||||
"""
|
||||
if (
|
||||
(bl_object := bpy.data.objects.get(self.name))
|
||||
and bl_object.type == "MESH"
|
||||
):
|
||||
bl_object := bpy.data.objects.get(self.name)
|
||||
) and bl_object.type == 'MESH':
|
||||
return bl_object.data
|
||||
|
||||
msg = f"Requested MESH data from `bl_object` of type {bl_object.type}"
|
||||
|
||||
msg = f'Requested MESH data from `bl_object` of type {bl_object.type}'
|
||||
raise ValueError(msg)
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def mesh_as_bmesh(
|
||||
self,
|
||||
|
@ -210,9 +231,8 @@ class ManagedBLObject(ct.schemas.ManagedObj):
|
|||
triangulate: bool = False,
|
||||
) -> bpy.types.Mesh:
|
||||
if (
|
||||
(bl_object := bpy.data.objects.get(self.name))
|
||||
and bl_object.type == "MESH"
|
||||
):
|
||||
bl_object := bpy.data.objects.get(self.name)
|
||||
) and bl_object.type == 'MESH':
|
||||
bmesh_mesh = None
|
||||
try:
|
||||
bmesh_mesh = bmesh.new()
|
||||
|
@ -223,52 +243,53 @@ class ManagedBLObject(ct.schemas.ManagedObj):
|
|||
)
|
||||
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()
|
||||
|
||||
if bmesh_mesh:
|
||||
bmesh_mesh.free()
|
||||
|
||||
else:
|
||||
msg = f"Requested BMesh from `bl_object` of type {bl_object.type}"
|
||||
msg = f'Requested BMesh from `bl_object` of type {bl_object.type}'
|
||||
raise ValueError(msg)
|
||||
|
||||
|
||||
@property
|
||||
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")
|
||||
_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>
|
||||
verts = np.zeros(3 * len(_mesh.vertices), dtype=np.float64)
|
||||
_mesh.vertices.foreach_get('co', verts)
|
||||
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**.
|
||||
faces = np.zeros(3 * len(_mesh.polygons), dtype=np.uint64)
|
||||
_mesh.polygons.foreach_get('vertices', faces)
|
||||
faces = np.zeros(3 * len(_mesh.polygons), dtype=np.uint64)
|
||||
_mesh.polygons.foreach_get('vertices', faces)
|
||||
faces.shape = (-1, 3)
|
||||
|
||||
|
||||
# Remove Temporary Mesh
|
||||
bpy.data.meshes.remove(_mesh)
|
||||
|
||||
|
||||
return {
|
||||
"verts": verts,
|
||||
"faces": faces,
|
||||
'verts': verts,
|
||||
'faces': faces,
|
||||
}
|
||||
|
||||
|
||||
####################
|
||||
# - Modifier Methods
|
||||
####################
|
||||
|
@ -277,13 +298,13 @@ class ManagedBLObject(ct.schemas.ManagedObj):
|
|||
modifier_type: ModifierType,
|
||||
):
|
||||
"""Creates a new modifier for the current `bl_object`.
|
||||
|
||||
|
||||
For all Blender modifier type names, see: <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 = "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:
|
||||
|
@ -292,33 +313,33 @@ class ManagedBLObject(ct.schemas.ManagedObj):
|
|||
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":
|
||||
|
||||
if modifier_type == 'NODES':
|
||||
return {
|
||||
"node_group": bl_modifier.node_group,
|
||||
'node_group': bl_modifier.node_group,
|
||||
}
|
||||
elif modifier_type == "ARRAY":
|
||||
elif modifier_type == 'ARRAY':
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
def s_modifier_attrs(
|
||||
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":
|
||||
|
||||
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
|
||||
####################
|
||||
|
@ -328,65 +349,64 @@ class ManagedBLObject(ct.schemas.ManagedObj):
|
|||
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")
|
||||
|
||||
bl_object = self.bl_object('MESH')
|
||||
|
||||
# Get (/make) a GeoModes Modifier
|
||||
bl_modifier = self.bl_modifier("NODES")
|
||||
|
||||
bl_modifier = self.bl_modifier('NODES')
|
||||
|
||||
# Set GeoNodes Modifier Attributes (specifically, the 'node_group')
|
||||
self.s_modifier_attrs("NODES", {"node_group": geonodes_node_group})
|
||||
|
||||
self.s_modifier_attrs('NODES', {'node_group': geonodes_node_group})
|
||||
|
||||
# Set GeoNodes Values
|
||||
modifier_altered = False
|
||||
for interface_identifier, value in (
|
||||
geonodes_identifier_to_value.items()
|
||||
):
|
||||
for (
|
||||
interface_identifier,
|
||||
value,
|
||||
) in geonodes_identifier_to_value.items():
|
||||
if bl_modifier[interface_identifier] != value:
|
||||
# Quickly Determine if IDPropertyArray is Equal
|
||||
if hasattr(
|
||||
bl_modifier[interface_identifier],
|
||||
"to_list"
|
||||
) and tuple(
|
||||
bl_modifier[interface_identifier].to_list()
|
||||
) == value:
|
||||
if (
|
||||
hasattr(bl_modifier[interface_identifier], 'to_list')
|
||||
and tuple(bl_modifier[interface_identifier].to_list())
|
||||
== value
|
||||
):
|
||||
continue
|
||||
|
||||
|
||||
# Quickly Determine int/float Mismatch
|
||||
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.
|
||||
#
|
||||
# Raises an error if the object has no volume data.
|
||||
# """
|
||||
# if (
|
||||
# (bl_object := bpy.data.objects.get(self.bl_object_name))
|
||||
# and bl_object.type == "VOLUME"
|
||||
# ):
|
||||
# return bl_object.data
|
||||
|
||||
# @property
|
||||
# def volume(self) -> bpy.types.Volume:
|
||||
# """Returns the object's volume data.
|
||||
#
|
||||
# msg = f"Requested VOLUME data from `bl_object` of type {bl_object.type}"
|
||||
# raise ValueError(msg)
|
||||
# Raises an error if the object has no volume data.
|
||||
# """
|
||||
# if (
|
||||
# (bl_object := bpy.data.objects.get(self.bl_object_name))
|
||||
# and bl_object.type == "VOLUME"
|
||||
# ):
|
||||
# return bl_object.data
|
||||
#
|
||||
# msg = f"Requested VOLUME data from `bl_object` of type {bl_object.type}"
|
||||
# raise ValueError(msg)
|
||||
|
|
|
@ -9,10 +9,12 @@ from . import contracts as ct
|
|||
####################
|
||||
MemAddr = int
|
||||
|
||||
|
||||
class DeltaNodeLinkCache(typ.TypedDict):
|
||||
added: set[MemAddr]
|
||||
removed: set[MemAddr]
|
||||
|
||||
|
||||
class NodeLinkCache:
|
||||
def __init__(self, node_tree: bpy.types.NodeTree):
|
||||
# Initialize Parameters
|
||||
|
@ -21,46 +23,47 @@ class NodeLinkCache:
|
|||
self.link_ptrs = set()
|
||||
self.link_ptrs_from_sockets = {}
|
||||
self.link_ptrs_to_sockets = {}
|
||||
|
||||
|
||||
# Fill Cache
|
||||
self.regenerate()
|
||||
|
||||
|
||||
def remove(self, link_ptrs: set[MemAddr]) -> None:
|
||||
for link_ptr in link_ptrs:
|
||||
self.link_ptrs.remove(link_ptr)
|
||||
self.link_ptrs_to_links.pop(link_ptr, None)
|
||||
|
||||
|
||||
def regenerate(self) -> DeltaNodeLinkCache:
|
||||
current_link_ptrs_to_links = {
|
||||
link.as_pointer(): link for link in self._node_tree.links
|
||||
}
|
||||
current_link_ptrs = set(current_link_ptrs_to_links.keys())
|
||||
|
||||
|
||||
# Compute Delta
|
||||
added_link_ptrs = current_link_ptrs - self.link_ptrs
|
||||
removed_link_ptrs = self.link_ptrs - current_link_ptrs
|
||||
|
||||
|
||||
# Update Caches Incrementally
|
||||
self.remove(removed_link_ptrs)
|
||||
|
||||
|
||||
self.link_ptrs |= added_link_ptrs
|
||||
for link_ptr in added_link_ptrs:
|
||||
link = current_link_ptrs_to_links[link_ptr]
|
||||
|
||||
|
||||
self.link_ptrs_to_links[link_ptr] = link
|
||||
self.link_ptrs_from_sockets[link_ptr] = link.from_socket
|
||||
self.link_ptrs_to_sockets[link_ptr] = link.to_socket
|
||||
|
||||
return {"added": added_link_ptrs, "removed": removed_link_ptrs}
|
||||
|
||||
return {'added': added_link_ptrs, 'removed': removed_link_ptrs}
|
||||
|
||||
|
||||
####################
|
||||
# - Node Tree Definition
|
||||
####################
|
||||
class MaxwellSimTree(bpy.types.NodeTree):
|
||||
bl_idname = ct.TreeType.MaxwellSim.value
|
||||
bl_label = "Maxwell Sim Editor"
|
||||
bl_label = 'Maxwell Sim Editor'
|
||||
bl_icon = ct.Icon.SimNodeEditor.value
|
||||
|
||||
|
||||
####################
|
||||
# - Lock Methods
|
||||
####################
|
||||
|
@ -69,116 +72,117 @@ class MaxwellSimTree(bpy.types.NodeTree):
|
|||
node.locked = False
|
||||
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)
|
||||
|
||||
|
||||
if hasattr(self, '_node_link_cache'):
|
||||
self._node_link_cache.regenerate()
|
||||
else:
|
||||
self._node_link_cache = NodeLinkCache(self)
|
||||
|
||||
####################
|
||||
# - Update Methods
|
||||
####################
|
||||
def sync_node_removed(self, node: bpy.types.Node):
|
||||
"""Run by `Node.free()` when a node is being removed.
|
||||
|
||||
|
||||
Removes node input links from the internal cache (so we don't attempt to update non-existant sockets).
|
||||
"""
|
||||
for bl_socket in node.inputs.values():
|
||||
# Retrieve Socket Links (if any)
|
||||
self._node_link_cache.remove({
|
||||
link.as_pointer()
|
||||
for link in bl_socket.links
|
||||
})
|
||||
self._node_link_cache.remove(
|
||||
{link.as_pointer() for link in bl_socket.links}
|
||||
)
|
||||
## ONLY Input Socket Links are Removed from the NodeLink Cache
|
||||
## - update() handles link-removal from still-existing node just fine.
|
||||
## - update() does NOT handle link-removal of non-existant nodes.
|
||||
|
||||
|
||||
def update(self):
|
||||
"""Run by Blender when 'something changes' in the node tree.
|
||||
|
||||
|
||||
Updates an internal node link cache, then updates sockets that just lost/gained an input link.
|
||||
"""
|
||||
if not hasattr(self, "_node_link_cache"):
|
||||
if not hasattr(self, '_node_link_cache'):
|
||||
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.
|
||||
## - More testing needed to prevent this 'first-link bug'.
|
||||
## - Therefore, self.on_load() is also called as a load_post handler.
|
||||
return
|
||||
|
||||
|
||||
# Compute Changes to NodeLink Cache
|
||||
delta_links = self._node_link_cache.regenerate()
|
||||
|
||||
|
||||
link_alterations = {
|
||||
"to_remove": [],
|
||||
"to_add": [],
|
||||
'to_remove': [],
|
||||
'to_add': [],
|
||||
}
|
||||
for link_ptr in delta_links["removed"]:
|
||||
from_socket = self._node_link_cache.link_ptrs_from_sockets[link_ptr]
|
||||
for link_ptr in delta_links['removed']:
|
||||
from_socket = self._node_link_cache.link_ptrs_from_sockets[
|
||||
link_ptr
|
||||
]
|
||||
to_socket = self._node_link_cache.link_ptrs_to_sockets[link_ptr]
|
||||
|
||||
|
||||
# Update Socket Caches
|
||||
self._node_link_cache.link_ptrs_from_sockets.pop(link_ptr, None)
|
||||
self._node_link_cache.link_ptrs_to_sockets.pop(link_ptr, None)
|
||||
|
||||
|
||||
# Trigger Report Chain on Socket that Just Lost a Link
|
||||
## Aka. Forward-Refresh Caches Relying on Linkage
|
||||
if not (
|
||||
consent_removal := to_socket.sync_link_removed(from_socket)
|
||||
):
|
||||
# Did Not Consent to Removal: Queue Add Link
|
||||
link_alterations["to_add"].append((from_socket, to_socket))
|
||||
|
||||
for link_ptr in delta_links["added"]:
|
||||
link_alterations['to_add'].append((from_socket, to_socket))
|
||||
|
||||
for link_ptr in delta_links['added']:
|
||||
link = self._node_link_cache.link_ptrs_to_links.get(link_ptr)
|
||||
if link is None: continue
|
||||
|
||||
if link is None:
|
||||
continue
|
||||
|
||||
# Trigger Report Chain on Socket that Just Gained a Link
|
||||
## Aka. Forward-Refresh Caches Relying on Linkage
|
||||
|
||||
if not (
|
||||
consent_added := link.to_socket.sync_link_added(link)
|
||||
):
|
||||
|
||||
if not (consent_added := link.to_socket.sync_link_added(link)):
|
||||
# Did Not Consent to Addition: Queue Remove Link
|
||||
link_alterations["to_remove"].append(link)
|
||||
|
||||
link_alterations['to_remove'].append(link)
|
||||
|
||||
# Execute Queued Operations
|
||||
## - Especially undoing undesirable link changes.
|
||||
## - This is important for locked graphs, whose links must not change.
|
||||
for link in link_alterations["to_remove"]:
|
||||
for link in link_alterations['to_remove']:
|
||||
self.links.remove(link)
|
||||
for from_socket, to_socket in link_alterations["to_add"]:
|
||||
for from_socket, to_socket in link_alterations['to_add']:
|
||||
self.links.new(from_socket, to_socket)
|
||||
|
||||
|
||||
# If Queued Operations: Regenerate Cache
|
||||
## - This prevents the next update() from picking up on alterations.
|
||||
if link_alterations["to_remove"] or link_alterations["to_add"]:
|
||||
if link_alterations['to_remove'] or link_alterations['to_add']:
|
||||
self._node_link_cache.regenerate()
|
||||
|
||||
|
||||
####################
|
||||
# - Post-Load Handler
|
||||
####################
|
||||
def initialize_sim_tree_node_link_cache(scene: bpy.types.Scene):
|
||||
"""Whenever a file is loaded, create/regenerate the NodeLinkCache in all trees.
|
||||
"""
|
||||
def initialize_sim_tree_node_link_cache(_: bpy.types.Scene):
|
||||
"""Whenever a file is loaded, create/regenerate the NodeLinkCache in all trees."""
|
||||
for node_tree in bpy.data.node_groups:
|
||||
if node_tree.bl_idname == "MaxwellSimTree":
|
||||
if not hasattr(node_tree, "_node_link_cache"):
|
||||
node_tree._node_link_cache = NodeLinkCache(node_tree)
|
||||
else:
|
||||
node_tree._node_link_cache.regenerate()
|
||||
if node_tree.bl_idname == 'MaxwellSimTree':
|
||||
node_tree.on_load()
|
||||
|
||||
|
||||
####################
|
||||
# - Blender Registration
|
||||
####################
|
||||
bpy.app.handlers.load_post.append(initialize_sim_tree_node_link_cache)
|
||||
## TODO: Move to top-level registration.
|
||||
|
||||
BL_REGISTER = [
|
||||
MaxwellSimTree,
|
||||
|
|
|
@ -1,37 +1,38 @@
|
|||
#from . import kitchen_sink
|
||||
# from . import kitchen_sink
|
||||
|
||||
from . import inputs
|
||||
from . import outputs
|
||||
from . import sources
|
||||
from . import mediums
|
||||
from . import structures
|
||||
#from . import bounds
|
||||
|
||||
# from . import bounds
|
||||
from . import monitors
|
||||
from . import simulations
|
||||
from . import utilities
|
||||
from . import viz
|
||||
|
||||
BL_REGISTER = [
|
||||
#*kitchen_sink.BL_REGISTER,
|
||||
# *kitchen_sink.BL_REGISTER,
|
||||
*inputs.BL_REGISTER,
|
||||
*outputs.BL_REGISTER,
|
||||
*sources.BL_REGISTER,
|
||||
*mediums.BL_REGISTER,
|
||||
*structures.BL_REGISTER,
|
||||
# *bounds.BL_REGISTER,
|
||||
# *bounds.BL_REGISTER,
|
||||
*monitors.BL_REGISTER,
|
||||
*simulations.BL_REGISTER,
|
||||
*utilities.BL_REGISTER,
|
||||
*viz.BL_REGISTER,
|
||||
]
|
||||
BL_NODES = {
|
||||
#**kitchen_sink.BL_NODES,
|
||||
# **kitchen_sink.BL_NODES,
|
||||
**inputs.BL_NODES,
|
||||
**outputs.BL_NODES,
|
||||
**sources.BL_NODES,
|
||||
**mediums.BL_NODES,
|
||||
**structures.BL_NODES,
|
||||
# **bounds.BL_NODES,
|
||||
# **bounds.BL_NODES,
|
||||
**monitors.BL_NODES,
|
||||
**simulations.BL_NODES,
|
||||
**utilities.BL_NODES,
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -6,41 +6,41 @@ from ... import contracts as ct
|
|||
from ... import sockets
|
||||
from .. import base
|
||||
|
||||
|
||||
class BoundCondsNode(base.MaxwellSimNode):
|
||||
node_type = ct.NodeType.BoundConds
|
||||
bl_label = "Bound Box"
|
||||
#bl_icon = ...
|
||||
|
||||
bl_label = 'Bound Box'
|
||||
# bl_icon = ...
|
||||
|
||||
####################
|
||||
# - Sockets
|
||||
####################
|
||||
input_sockets = {
|
||||
"+X": sockets.MaxwellBoundCondSocketDef(),
|
||||
"-X": sockets.MaxwellBoundCondSocketDef(),
|
||||
"+Y": sockets.MaxwellBoundCondSocketDef(),
|
||||
"-Y": sockets.MaxwellBoundCondSocketDef(),
|
||||
"+Z": sockets.MaxwellBoundCondSocketDef(),
|
||||
"-Z": sockets.MaxwellBoundCondSocketDef(),
|
||||
'+X': sockets.MaxwellBoundCondSocketDef(),
|
||||
'-X': sockets.MaxwellBoundCondSocketDef(),
|
||||
'+Y': sockets.MaxwellBoundCondSocketDef(),
|
||||
'-Y': sockets.MaxwellBoundCondSocketDef(),
|
||||
'+Z': sockets.MaxwellBoundCondSocketDef(),
|
||||
'-Z': sockets.MaxwellBoundCondSocketDef(),
|
||||
}
|
||||
output_sockets = {
|
||||
"BCs": sockets.MaxwellBoundCondsSocketDef(),
|
||||
'BCs': sockets.MaxwellBoundCondsSocketDef(),
|
||||
}
|
||||
|
||||
|
||||
####################
|
||||
# - Output Socket Computation
|
||||
####################
|
||||
@base.computes_output_socket(
|
||||
"BCs",
|
||||
input_sockets={"+X", "-X", "+Y", "-Y", "+Z", "-Z"}
|
||||
'BCs', input_sockets={'+X', '-X', '+Y', '-Y', '+Z', '-Z'}
|
||||
)
|
||||
def compute_simulation(self, input_sockets) -> td.BoundarySpec:
|
||||
x_pos = input_sockets["+X"]
|
||||
x_neg = input_sockets["-X"]
|
||||
y_pos = input_sockets["+Y"]
|
||||
y_neg = input_sockets["-Y"]
|
||||
z_pos = input_sockets["+Z"]
|
||||
z_neg = input_sockets["-Z"]
|
||||
|
||||
x_pos = input_sockets['+X']
|
||||
x_neg = input_sockets['-X']
|
||||
y_pos = input_sockets['+Y']
|
||||
y_neg = input_sockets['-Y']
|
||||
z_pos = input_sockets['+Z']
|
||||
z_neg = input_sockets['-Z']
|
||||
|
||||
return td.BoundarySpec(
|
||||
x=td.Boundary(
|
||||
plus=x_pos,
|
||||
|
@ -57,15 +57,10 @@ class BoundCondsNode(base.MaxwellSimNode):
|
|||
)
|
||||
|
||||
|
||||
|
||||
####################
|
||||
# - Blender Registration
|
||||
####################
|
||||
BL_REGISTER = [
|
||||
BoundCondsNode,
|
||||
]
|
||||
BL_NODES = {
|
||||
ct.NodeType.BoundConds: (
|
||||
ct.NodeCategory.MAXWELLSIM_BOUNDS
|
||||
)
|
||||
}
|
||||
BL_NODES = {ct.NodeType.BoundConds: (ct.NodeCategory.MAXWELLSIM_BOUNDS)}
|
||||
|
|
|
@ -10,7 +10,6 @@ BL_REGISTER = [
|
|||
*pml_bound_face.BL_REGISTER,
|
||||
*pec_bound_face.BL_REGISTER,
|
||||
*pmc_bound_face.BL_REGISTER,
|
||||
|
||||
*bloch_bound_face.BL_REGISTER,
|
||||
*periodic_bound_face.BL_REGISTER,
|
||||
*absorbing_bound_face.BL_REGISTER,
|
||||
|
@ -19,7 +18,6 @@ BL_NODES = {
|
|||
**pml_bound_face.BL_NODES,
|
||||
**pec_bound_face.BL_NODES,
|
||||
**pmc_bound_face.BL_NODES,
|
||||
|
||||
**bloch_bound_face.BL_NODES,
|
||||
**periodic_bound_face.BL_NODES,
|
||||
**absorbing_bound_face.BL_NODES,
|
||||
|
|
|
@ -1,26 +1,22 @@
|
|||
from . import wave_constant
|
||||
#from . import unit_system
|
||||
# from . import unit_system
|
||||
|
||||
from . import constants
|
||||
|
||||
from . import web_importers
|
||||
#from . import file_importers
|
||||
# from . import file_importers
|
||||
|
||||
BL_REGISTER = [
|
||||
*wave_constant.BL_REGISTER,
|
||||
# *unit_system.BL_REGISTER,
|
||||
|
||||
# *unit_system.BL_REGISTER,
|
||||
*constants.BL_REGISTER,
|
||||
|
||||
*web_importers.BL_REGISTER,
|
||||
# *file_importers.BL_REGISTER,
|
||||
# *file_importers.BL_REGISTER,
|
||||
]
|
||||
BL_NODES = {
|
||||
**wave_constant.BL_NODES,
|
||||
# **unit_system.BL_NODES,
|
||||
|
||||
# **unit_system.BL_NODES,
|
||||
**constants.BL_NODES,
|
||||
|
||||
**web_importers.BL_NODES,
|
||||
# *file_importers.BL_REGISTER,
|
||||
# *file_importers.BL_REGISTER,
|
||||
}
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
#from . import scientific_constant
|
||||
# from . import scientific_constant
|
||||
from . import number_constant
|
||||
#from . import physical_constant
|
||||
|
||||
# from . import physical_constant
|
||||
from . import blender_constant
|
||||
|
||||
BL_REGISTER = [
|
||||
# *scientific_constant.BL_REGISTER,
|
||||
# *scientific_constant.BL_REGISTER,
|
||||
*number_constant.BL_REGISTER,
|
||||
# *physical_constant.BL_REGISTER,
|
||||
# *physical_constant.BL_REGISTER,
|
||||
*blender_constant.BL_REGISTER,
|
||||
]
|
||||
BL_NODES = {
|
||||
# **scientific_constant.BL_NODES,
|
||||
# **scientific_constant.BL_NODES,
|
||||
**number_constant.BL_NODES,
|
||||
# **physical_constant.BL_NODES,
|
||||
# **physical_constant.BL_NODES,
|
||||
**blender_constant.BL_NODES,
|
||||
}
|
||||
|
|
|
@ -4,39 +4,36 @@ from .... import contracts as ct
|
|||
from .... import sockets
|
||||
from ... import base
|
||||
|
||||
|
||||
class BlenderConstantNode(base.MaxwellSimNode):
|
||||
node_type = ct.NodeType.BlenderConstant
|
||||
bl_label = "Blender Constant"
|
||||
|
||||
bl_label = 'Blender Constant'
|
||||
|
||||
input_socket_sets = {
|
||||
"Object": {
|
||||
"Value": sockets.BlenderObjectSocketDef(),
|
||||
'Object': {
|
||||
'Value': sockets.BlenderObjectSocketDef(),
|
||||
},
|
||||
"Collection": {
|
||||
"Value": sockets.BlenderCollectionSocketDef(),
|
||||
'Collection': {
|
||||
'Value': sockets.BlenderCollectionSocketDef(),
|
||||
},
|
||||
"Text": {
|
||||
"Value": sockets.BlenderTextSocketDef(),
|
||||
'Text': {
|
||||
'Value': sockets.BlenderTextSocketDef(),
|
||||
},
|
||||
"Image": {
|
||||
"Value": sockets.BlenderImageSocketDef(),
|
||||
'Image': {
|
||||
'Value': sockets.BlenderImageSocketDef(),
|
||||
},
|
||||
"GeoNode Tree": {
|
||||
"Value": sockets.BlenderGeoNodesSocketDef(),
|
||||
'GeoNode Tree': {
|
||||
'Value': sockets.BlenderGeoNodesSocketDef(),
|
||||
},
|
||||
}
|
||||
output_socket_sets = input_socket_sets
|
||||
|
||||
|
||||
####################
|
||||
# - Callbacks
|
||||
####################
|
||||
@base.computes_output_socket(
|
||||
"Value",
|
||||
input_sockets={"Value"}
|
||||
)
|
||||
@base.computes_output_socket('Value', input_sockets={'Value'})
|
||||
def compute_value(self, input_sockets) -> typ.Any:
|
||||
return input_sockets["Value"]
|
||||
|
||||
return input_sockets['Value']
|
||||
|
||||
|
||||
####################
|
||||
|
@ -46,7 +43,5 @@ BL_REGISTER = [
|
|||
BlenderConstantNode,
|
||||
]
|
||||
BL_NODES = {
|
||||
ct.NodeType.BlenderConstant: (
|
||||
ct.NodeCategory.MAXWELLSIM_INPUTS_CONSTANTS
|
||||
)
|
||||
ct.NodeType.BlenderConstant: (ct.NodeCategory.MAXWELLSIM_INPUTS_CONSTANTS)
|
||||
}
|
||||
|
|
|
@ -7,36 +7,33 @@ from .... import contracts as ct
|
|||
from .... import sockets
|
||||
from ... import base
|
||||
|
||||
|
||||
class NumberConstantNode(base.MaxwellSimNode):
|
||||
node_type = ct.NodeType.NumberConstant
|
||||
bl_label = "Numerical Constant"
|
||||
|
||||
bl_label = 'Numerical Constant'
|
||||
|
||||
input_socket_sets = {
|
||||
"Integer": {
|
||||
"Value": sockets.IntegerNumberSocketDef(),
|
||||
'Integer': {
|
||||
'Value': sockets.IntegerNumberSocketDef(),
|
||||
},
|
||||
"Rational": {
|
||||
"Value": sockets.RationalNumberSocketDef(),
|
||||
'Rational': {
|
||||
'Value': sockets.RationalNumberSocketDef(),
|
||||
},
|
||||
"Real": {
|
||||
"Value": sockets.RealNumberSocketDef(),
|
||||
'Real': {
|
||||
'Value': sockets.RealNumberSocketDef(),
|
||||
},
|
||||
"Complex": {
|
||||
"Value": sockets.ComplexNumberSocketDef(),
|
||||
'Complex': {
|
||||
'Value': sockets.ComplexNumberSocketDef(),
|
||||
},
|
||||
}
|
||||
output_socket_sets = input_socket_sets
|
||||
|
||||
|
||||
####################
|
||||
# - Callbacks
|
||||
####################
|
||||
@base.computes_output_socket(
|
||||
"Value",
|
||||
input_sockets={"Value"}
|
||||
)
|
||||
@base.computes_output_socket('Value', input_sockets={'Value'})
|
||||
def compute_value(self, input_sockets) -> typ.Any:
|
||||
return input_sockets["Value"]
|
||||
|
||||
return input_sockets['Value']
|
||||
|
||||
|
||||
####################
|
||||
|
@ -46,7 +43,5 @@ BL_REGISTER = [
|
|||
NumberConstantNode,
|
||||
]
|
||||
BL_NODES = {
|
||||
ct.NodeType.NumberConstant: (
|
||||
ct.NodeCategory.MAXWELLSIM_INPUTS_CONSTANTS
|
||||
)
|
||||
ct.NodeType.NumberConstant: (ct.NodeCategory.MAXWELLSIM_INPUTS_CONSTANTS)
|
||||
}
|
||||
|
|
|
@ -5,61 +5,61 @@ from .... import contracts
|
|||
from .... import sockets
|
||||
from ... import base
|
||||
|
||||
|
||||
class PhysicalConstantNode(base.MaxwellSimTreeNode):
|
||||
node_type = contracts.NodeType.PhysicalConstant
|
||||
|
||||
bl_label = "Physical Constant"
|
||||
#bl_icon = constants.ICON_SIM_INPUT
|
||||
|
||||
|
||||
bl_label = 'Physical Constant'
|
||||
# bl_icon = constants.ICON_SIM_INPUT
|
||||
|
||||
input_sockets = {}
|
||||
input_socket_sets = {
|
||||
"time": {
|
||||
"value": sockets.PhysicalTimeSocketDef(
|
||||
label="Time",
|
||||
'time': {
|
||||
'value': sockets.PhysicalTimeSocketDef(
|
||||
label='Time',
|
||||
),
|
||||
},
|
||||
"angle": {
|
||||
"value": sockets.PhysicalAngleSocketDef(
|
||||
label="Angle",
|
||||
'angle': {
|
||||
'value': sockets.PhysicalAngleSocketDef(
|
||||
label='Angle',
|
||||
),
|
||||
},
|
||||
"length": {
|
||||
"value": sockets.PhysicalLengthSocketDef(
|
||||
label="Length",
|
||||
'length': {
|
||||
'value': sockets.PhysicalLengthSocketDef(
|
||||
label='Length',
|
||||
),
|
||||
},
|
||||
"area": {
|
||||
"value": sockets.PhysicalAreaSocketDef(
|
||||
label="Area",
|
||||
'area': {
|
||||
'value': sockets.PhysicalAreaSocketDef(
|
||||
label='Area',
|
||||
),
|
||||
},
|
||||
"volume": {
|
||||
"value": sockets.PhysicalVolumeSocketDef(
|
||||
label="Volume",
|
||||
'volume': {
|
||||
'value': sockets.PhysicalVolumeSocketDef(
|
||||
label='Volume',
|
||||
),
|
||||
},
|
||||
"point_3d": {
|
||||
"value": sockets.PhysicalPoint3DSocketDef(
|
||||
label="3D Point",
|
||||
'point_3d': {
|
||||
'value': sockets.PhysicalPoint3DSocketDef(
|
||||
label='3D Point',
|
||||
),
|
||||
},
|
||||
"size_3d": {
|
||||
"value": sockets.PhysicalSize3DSocketDef(
|
||||
label="3D Size",
|
||||
'size_3d': {
|
||||
'value': sockets.PhysicalSize3DSocketDef(
|
||||
label='3D Size',
|
||||
),
|
||||
},
|
||||
## I got bored so maybe the rest later
|
||||
}
|
||||
output_sockets = {}
|
||||
output_socket_sets = input_socket_sets
|
||||
|
||||
|
||||
####################
|
||||
# - Callbacks
|
||||
####################
|
||||
@base.computes_output_socket("value")
|
||||
@base.computes_output_socket('value')
|
||||
def compute_value(self: contracts.NodeTypeProtocol) -> sp.Expr:
|
||||
return self.compute_input("value")
|
||||
|
||||
return self.compute_input('value')
|
||||
|
||||
|
||||
####################
|
||||
|
|
|
@ -5,29 +5,29 @@ from ... import contracts as ct
|
|||
from ... import sockets
|
||||
from .. import base
|
||||
|
||||
|
||||
class PhysicalUnitSystemNode(base.MaxwellSimNode):
|
||||
node_type = ct.NodeType.UnitSystem
|
||||
bl_label = "Unit System"
|
||||
|
||||
bl_label = 'Unit System'
|
||||
|
||||
input_sockets = {
|
||||
"Unit System": sockets.PhysicalUnitSystemSocketDef(
|
||||
'Unit System': sockets.PhysicalUnitSystemSocketDef(
|
||||
show_by_default=True,
|
||||
),
|
||||
}
|
||||
output_sockets = {
|
||||
"Unit System": sockets.PhysicalUnitSystemSocketDef(),
|
||||
'Unit System': sockets.PhysicalUnitSystemSocketDef(),
|
||||
}
|
||||
|
||||
|
||||
####################
|
||||
# - Callbacks
|
||||
####################
|
||||
@base.computes_output_socket(
|
||||
"Unit System",
|
||||
input_sockets = {"Unit System"},
|
||||
'Unit System',
|
||||
input_sockets={'Unit System'},
|
||||
)
|
||||
def compute_value(self, input_sockets) -> dict:
|
||||
return input_sockets["Unit System"]
|
||||
|
||||
return input_sockets['Unit System']
|
||||
|
||||
|
||||
####################
|
||||
|
@ -36,8 +36,4 @@ class PhysicalUnitSystemNode(base.MaxwellSimNode):
|
|||
BL_REGISTER = [
|
||||
PhysicalUnitSystemNode,
|
||||
]
|
||||
BL_NODES = {
|
||||
ct.NodeType.UnitSystem: (
|
||||
ct.NodeCategory.MAXWELLSIM_INPUTS
|
||||
)
|
||||
}
|
||||
BL_NODES = {ct.NodeType.UnitSystem: (ct.NodeCategory.MAXWELLSIM_INPUTS)}
|
||||
|
|
|
@ -8,89 +8,86 @@ from ... import contracts as ct
|
|||
from ... import sockets
|
||||
from .. import base
|
||||
|
||||
VAC_SPEED_OF_LIGHT = (
|
||||
sc.constants.speed_of_light
|
||||
* spu.meter/spu.second
|
||||
)
|
||||
VAC_SPEED_OF_LIGHT = sc.constants.speed_of_light * spu.meter / spu.second
|
||||
|
||||
|
||||
class WaveConstantNode(base.MaxwellSimNode):
|
||||
node_type = ct.NodeType.WaveConstant
|
||||
|
||||
bl_label = "Wave Constant"
|
||||
|
||||
|
||||
bl_label = 'Wave Constant'
|
||||
|
||||
input_socket_sets = {
|
||||
# Single
|
||||
"Vacuum WL": {
|
||||
"WL": sockets.PhysicalLengthSocketDef(
|
||||
default_value=500*spu.nm,
|
||||
'Vacuum WL': {
|
||||
'WL': sockets.PhysicalLengthSocketDef(
|
||||
default_value=500 * spu.nm,
|
||||
default_unit=spu.nm,
|
||||
),
|
||||
},
|
||||
"Frequency": {
|
||||
"Freq": sockets.PhysicalFreqSocketDef(
|
||||
default_value=500*spux.THz,
|
||||
'Frequency': {
|
||||
'Freq': sockets.PhysicalFreqSocketDef(
|
||||
default_value=500 * spux.THz,
|
||||
default_unit=spux.THz,
|
||||
),
|
||||
},
|
||||
|
||||
# Listy
|
||||
"Vacuum WLs": {
|
||||
"WLs": sockets.PhysicalLengthSocketDef(
|
||||
'Vacuum WLs': {
|
||||
'WLs': sockets.PhysicalLengthSocketDef(
|
||||
is_list=True,
|
||||
),
|
||||
},
|
||||
"Frequencies": {
|
||||
"Freqs": sockets.PhysicalFreqSocketDef(
|
||||
'Frequencies': {
|
||||
'Freqs': sockets.PhysicalFreqSocketDef(
|
||||
is_list=True,
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
####################
|
||||
# - Callbacks
|
||||
####################
|
||||
@base.computes_output_socket(
|
||||
"WL",
|
||||
input_sockets={"WL", "Freq"},
|
||||
'WL',
|
||||
input_sockets={'WL', 'Freq'},
|
||||
)
|
||||
def compute_vac_wl(self, input_sockets: dict) -> sp.Expr:
|
||||
if (vac_wl := input_sockets["WL"]) is not None:
|
||||
if (vac_wl := input_sockets['WL']) is not None:
|
||||
return vac_wl
|
||||
|
||||
elif (freq := input_sockets["Freq"]) is not None:
|
||||
|
||||
elif (freq := input_sockets['Freq']) is not None:
|
||||
return spu.convert_to(
|
||||
VAC_SPEED_OF_LIGHT / freq,
|
||||
spu.meter,
|
||||
)
|
||||
|
||||
raise RuntimeError("Vac WL and Freq are both None")
|
||||
|
||||
|
||||
raise RuntimeError('Vac WL and Freq are both None')
|
||||
|
||||
@base.computes_output_socket(
|
||||
"Freq",
|
||||
input_sockets={"WL", "Freq"},
|
||||
'Freq',
|
||||
input_sockets={'WL', 'Freq'},
|
||||
)
|
||||
def compute_freq(self, input_sockets: dict) -> sp.Expr:
|
||||
if (vac_wl := input_sockets["WL"]) is not None:
|
||||
if (vac_wl := input_sockets['WL']) is not None:
|
||||
return spu.convert_to(
|
||||
VAC_SPEED_OF_LIGHT / vac_wl,
|
||||
spu.hertz,
|
||||
)
|
||||
elif (freq := input_sockets["Freq"]) is not None:
|
||||
elif (freq := input_sockets['Freq']) is not None:
|
||||
return freq
|
||||
|
||||
raise RuntimeError("Vac WL and Freq are both None")
|
||||
|
||||
|
||||
raise RuntimeError('Vac WL and Freq are both None')
|
||||
|
||||
####################
|
||||
# - Listy Callbacks
|
||||
####################
|
||||
@base.computes_output_socket(
|
||||
"WLs",
|
||||
input_sockets={"WLs", "Freqs"},
|
||||
'WLs',
|
||||
input_sockets={'WLs', 'Freqs'},
|
||||
)
|
||||
def compute_vac_wls(self, input_sockets: dict) -> sp.Expr:
|
||||
if (vac_wls := input_sockets["WLs"]) is not None:
|
||||
if (vac_wls := input_sockets['WLs']) is not None:
|
||||
return vac_wls
|
||||
elif (freqs := input_sockets["Freqs"]) is not None:
|
||||
elif (freqs := input_sockets['Freqs']) is not None:
|
||||
return [
|
||||
spu.convert_to(
|
||||
VAC_SPEED_OF_LIGHT / freq,
|
||||
|
@ -98,15 +95,15 @@ class WaveConstantNode(base.MaxwellSimNode):
|
|||
)
|
||||
for freq in freqs
|
||||
][::-1]
|
||||
|
||||
raise RuntimeError("Vac WLs and Freqs are both None")
|
||||
|
||||
|
||||
raise RuntimeError('Vac WLs and Freqs are both None')
|
||||
|
||||
@base.computes_output_socket(
|
||||
"Freqs",
|
||||
input_sockets={"WLs", "Freqs"},
|
||||
'Freqs',
|
||||
input_sockets={'WLs', 'Freqs'},
|
||||
)
|
||||
def compute_freqs(self, input_sockets: dict) -> sp.Expr:
|
||||
if (vac_wls := input_sockets["WLs"]) is not None:
|
||||
if (vac_wls := input_sockets['WLs']) is not None:
|
||||
return [
|
||||
spu.convert_to(
|
||||
VAC_SPEED_OF_LIGHT / vac_wl,
|
||||
|
@ -114,43 +111,42 @@ class WaveConstantNode(base.MaxwellSimNode):
|
|||
)
|
||||
for vac_wl in vac_wls
|
||||
][::-1]
|
||||
elif (freqs := input_sockets["Freqs"]) is not None:
|
||||
elif (freqs := input_sockets['Freqs']) is not None:
|
||||
return freqs
|
||||
|
||||
raise RuntimeError("Vac WLs and Freqs are both None")
|
||||
|
||||
|
||||
raise RuntimeError('Vac WLs and Freqs are both None')
|
||||
|
||||
####################
|
||||
# - Callbacks
|
||||
####################
|
||||
@base.on_value_changed(
|
||||
prop_name="active_socket_set",
|
||||
props={"active_socket_set"}
|
||||
prop_name='active_socket_set', props={'active_socket_set'}
|
||||
)
|
||||
def on_value_changed__active_socket_set(self, props: dict):
|
||||
# Singular: Normal Output Sockets
|
||||
if props["active_socket_set"] in {"Vacuum WL", "Frequency"}:
|
||||
if props['active_socket_set'] in {'Vacuum WL', 'Frequency'}:
|
||||
self.loose_output_sockets = {}
|
||||
self.loose_output_sockets = {
|
||||
"Freq": sockets.PhysicalFreqSocketDef(),
|
||||
"WL": sockets.PhysicalLengthSocketDef(),
|
||||
'Freq': sockets.PhysicalFreqSocketDef(),
|
||||
'WL': sockets.PhysicalLengthSocketDef(),
|
||||
}
|
||||
|
||||
|
||||
# Plural: Listy Output Sockets
|
||||
elif props["active_socket_set"] in {"Vacuum WLs", "Frequencies"}:
|
||||
elif props['active_socket_set'] in {'Vacuum WLs', 'Frequencies'}:
|
||||
self.loose_output_sockets = {}
|
||||
self.loose_output_sockets = {
|
||||
"Freqs": sockets.PhysicalFreqSocketDef(is_list=True),
|
||||
"WLs": sockets.PhysicalLengthSocketDef(is_list=True),
|
||||
'Freqs': sockets.PhysicalFreqSocketDef(is_list=True),
|
||||
'WLs': sockets.PhysicalLengthSocketDef(is_list=True),
|
||||
}
|
||||
|
||||
|
||||
else:
|
||||
msg = f"Active socket set invalid for wave constant: {props['active_socket_set']}"
|
||||
raise RuntimeError(msg)
|
||||
|
||||
|
||||
@base.on_init()
|
||||
def on_init(self):
|
||||
self.on_value_changed__active_socket_set()
|
||||
|
||||
|
||||
|
||||
####################
|
||||
# - Blender Registration
|
||||
|
@ -158,8 +154,4 @@ class WaveConstantNode(base.MaxwellSimNode):
|
|||
BL_REGISTER = [
|
||||
WaveConstantNode,
|
||||
]
|
||||
BL_NODES = {
|
||||
ct.NodeType.WaveConstant: (
|
||||
ct.NodeCategory.MAXWELLSIM_INPUTS
|
||||
)
|
||||
}
|
||||
BL_NODES = {ct.NodeType.WaveConstant: (ct.NodeCategory.MAXWELLSIM_INPUTS)}
|
||||
|
|
|
@ -17,109 +17,108 @@ from ... import base
|
|||
|
||||
CACHE = {}
|
||||
|
||||
|
||||
####################
|
||||
# - Node
|
||||
####################
|
||||
class Tidy3DWebImporterNode(base.MaxwellSimNode):
|
||||
node_type = ct.NodeType.Tidy3DWebImporter
|
||||
bl_label = "Tidy3DWebImporter"
|
||||
|
||||
bl_label = 'Tidy3DWebImporter'
|
||||
|
||||
input_sockets = {
|
||||
"Cloud Task": sockets.Tidy3DCloudTaskSocketDef(
|
||||
'Cloud Task': sockets.Tidy3DCloudTaskSocketDef(
|
||||
should_exist=True,
|
||||
),
|
||||
"Cache Path": sockets.FilePathSocketDef(
|
||||
default_path=Path("loaded_simulation.hdf5")
|
||||
)
|
||||
'Cache Path': sockets.FilePathSocketDef(
|
||||
default_path=Path('loaded_simulation.hdf5')
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
####################
|
||||
# - Output Methods
|
||||
####################
|
||||
@base.computes_output_socket(
|
||||
"FDTD Sim Data",
|
||||
input_sockets={"Cloud Task", "Cache Path"},
|
||||
'FDTD Sim Data',
|
||||
input_sockets={'Cloud Task', 'Cache Path'},
|
||||
)
|
||||
def compute_fdtd_sim_data(self, input_sockets: dict) -> str:
|
||||
global CACHE
|
||||
if not CACHE.get(self.instance_id):
|
||||
CACHE[self.instance_id] = {"fdtd_sim_data": None}
|
||||
|
||||
if CACHE[self.instance_id]["fdtd_sim_data"] is not None:
|
||||
return CACHE[self.instance_id]["fdtd_sim_data"]
|
||||
|
||||
CACHE[self.instance_id] = {'fdtd_sim_data': None}
|
||||
|
||||
if CACHE[self.instance_id]['fdtd_sim_data'] is not None:
|
||||
return CACHE[self.instance_id]['fdtd_sim_data']
|
||||
|
||||
if not (
|
||||
(cloud_task := input_sockets["Cloud Task"]) is not None
|
||||
(cloud_task := input_sockets['Cloud Task']) is not None
|
||||
and isinstance(cloud_task, tdcloud.CloudTask)
|
||||
and cloud_task.status == "success"
|
||||
and cloud_task.status == 'success'
|
||||
):
|
||||
msg ="Won't attempt getting SimData"
|
||||
msg = "Won't attempt getting SimData"
|
||||
raise RuntimeError(msg)
|
||||
|
||||
|
||||
# Load the Simulation
|
||||
cache_path = input_sockets["Cache Path"]
|
||||
if cache_path is None:
|
||||
print("CACHE PATH IS NONE WHY")
|
||||
return ## I guess?
|
||||
cache_path = input_sockets['Cache Path']
|
||||
if cache_path is None:
|
||||
print('CACHE PATH IS NONE WHY')
|
||||
return ## I guess?
|
||||
if cache_path.is_file():
|
||||
sim_data = td.SimulationData.from_file(str(cache_path))
|
||||
|
||||
|
||||
else:
|
||||
sim_data = td_web.api.webapi.load(
|
||||
cloud_task.task_id,
|
||||
path=str(cache_path),
|
||||
)
|
||||
|
||||
CACHE[self.instance_id]["fdtd_sim_data"] = sim_data
|
||||
|
||||
CACHE[self.instance_id]['fdtd_sim_data'] = sim_data
|
||||
return sim_data
|
||||
|
||||
|
||||
@base.computes_output_socket(
|
||||
"FDTD Sim",
|
||||
input_sockets={"Cloud Task"},
|
||||
'FDTD Sim',
|
||||
input_sockets={'Cloud Task'},
|
||||
)
|
||||
def compute_fdtd_sim(self, input_sockets: dict) -> str:
|
||||
if not isinstance(
|
||||
cloud_task := input_sockets["Cloud Task"],
|
||||
tdcloud.CloudTask
|
||||
cloud_task := input_sockets['Cloud Task'], tdcloud.CloudTask
|
||||
):
|
||||
msg ="Input cloud task does not exist"
|
||||
msg = 'Input cloud task does not exist'
|
||||
raise RuntimeError(msg)
|
||||
|
||||
|
||||
# Load the Simulation
|
||||
with tempfile.NamedTemporaryFile(delete=False) as f:
|
||||
_path_tmp = Path(f.name)
|
||||
_path_tmp.rename(f.name + ".json")
|
||||
path_tmp = Path(f.name + ".json")
|
||||
|
||||
_path_tmp.rename(f.name + '.json')
|
||||
path_tmp = Path(f.name + '.json')
|
||||
|
||||
sim = td_web.api.webapi.load_simulation(
|
||||
cloud_task.task_id,
|
||||
path=str(path_tmp),
|
||||
) ## TODO: Don't use td_web directly. Only through tdcloud
|
||||
Path(path_tmp).unlink()
|
||||
|
||||
|
||||
return sim
|
||||
|
||||
|
||||
####################
|
||||
# - Update
|
||||
####################
|
||||
@base.on_value_changed(
|
||||
socket_name="Cloud Task",
|
||||
input_sockets={"Cloud Task"}
|
||||
socket_name='Cloud Task', input_sockets={'Cloud Task'}
|
||||
)
|
||||
def on_value_changed__cloud_task(self, input_sockets: dict):
|
||||
if (
|
||||
(cloud_task := input_sockets["Cloud Task"]) is not None
|
||||
(cloud_task := input_sockets['Cloud Task']) is not None
|
||||
and isinstance(cloud_task, tdcloud.CloudTask)
|
||||
and cloud_task.status == "success"
|
||||
and cloud_task.status == 'success'
|
||||
):
|
||||
self.loose_output_sockets = {
|
||||
"FDTD Sim Data": sockets.MaxwellFDTDSimDataSocketDef(),
|
||||
"FDTD Sim": sockets.MaxwellFDTDSimSocketDef(),
|
||||
'FDTD Sim Data': sockets.MaxwellFDTDSimDataSocketDef(),
|
||||
'FDTD Sim': sockets.MaxwellFDTDSimSocketDef(),
|
||||
}
|
||||
return
|
||||
|
||||
|
||||
self.loose_output_sockets = {}
|
||||
|
||||
|
||||
@base.on_init()
|
||||
def on_init(self):
|
||||
self.on_value_changed__cloud_task()
|
||||
|
|
|
@ -8,95 +8,91 @@ from .. import contracts as ct
|
|||
from .. import sockets
|
||||
from . import base
|
||||
|
||||
|
||||
class KitchenSinkNode(base.MaxwellSimNode):
|
||||
node_type = ct.NodeType.KitchenSink
|
||||
|
||||
bl_label = "Kitchen Sink"
|
||||
#bl_icon = ...
|
||||
|
||||
|
||||
bl_label = 'Kitchen Sink'
|
||||
# bl_icon = ...
|
||||
|
||||
####################
|
||||
# - Sockets
|
||||
####################
|
||||
input_sockets = {
|
||||
"Static Data": sockets.AnySocketDef(),
|
||||
'Static Data': sockets.AnySocketDef(),
|
||||
}
|
||||
input_socket_sets = {
|
||||
"Basic": {
|
||||
"Any": sockets.AnySocketDef(),
|
||||
"Bool": sockets.BoolSocketDef(),
|
||||
"FilePath": sockets.FilePathSocketDef(),
|
||||
"Text": sockets.TextSocketDef(),
|
||||
'Basic': {
|
||||
'Any': sockets.AnySocketDef(),
|
||||
'Bool': sockets.BoolSocketDef(),
|
||||
'FilePath': sockets.FilePathSocketDef(),
|
||||
'Text': sockets.TextSocketDef(),
|
||||
},
|
||||
"Number": {
|
||||
"Integer": sockets.IntegerNumberSocketDef(),
|
||||
"Rational": sockets.RationalNumberSocketDef(),
|
||||
"Real": sockets.RealNumberSocketDef(),
|
||||
"Complex": sockets.ComplexNumberSocketDef(),
|
||||
'Number': {
|
||||
'Integer': sockets.IntegerNumberSocketDef(),
|
||||
'Rational': sockets.RationalNumberSocketDef(),
|
||||
'Real': sockets.RealNumberSocketDef(),
|
||||
'Complex': sockets.ComplexNumberSocketDef(),
|
||||
},
|
||||
"Vector": {
|
||||
"Real 2D": sockets.Real2DVectorSocketDef(),
|
||||
"Real 3D": sockets.Real3DVectorSocketDef(
|
||||
'Vector': {
|
||||
'Real 2D': sockets.Real2DVectorSocketDef(),
|
||||
'Real 3D': sockets.Real3DVectorSocketDef(
|
||||
default_value=sp.Matrix([0.0, 0.0, 0.0])
|
||||
),
|
||||
"Complex 2D": sockets.Complex2DVectorSocketDef(),
|
||||
"Complex 3D": sockets.Complex3DVectorSocketDef(),
|
||||
'Complex 2D': sockets.Complex2DVectorSocketDef(),
|
||||
'Complex 3D': sockets.Complex3DVectorSocketDef(),
|
||||
},
|
||||
"Physical": {
|
||||
"Time": sockets.PhysicalTimeSocketDef(),
|
||||
#"physical_point_2d": sockets.PhysicalPoint2DSocketDef(),
|
||||
"Angle": sockets.PhysicalAngleSocketDef(),
|
||||
"Length": sockets.PhysicalLengthSocketDef(),
|
||||
"Area": sockets.PhysicalAreaSocketDef(),
|
||||
"Volume": sockets.PhysicalVolumeSocketDef(),
|
||||
"Point 3D": sockets.PhysicalPoint3DSocketDef(),
|
||||
'Physical': {
|
||||
'Time': sockets.PhysicalTimeSocketDef(),
|
||||
# "physical_point_2d": sockets.PhysicalPoint2DSocketDef(),
|
||||
'Angle': sockets.PhysicalAngleSocketDef(),
|
||||
'Length': sockets.PhysicalLengthSocketDef(),
|
||||
'Area': sockets.PhysicalAreaSocketDef(),
|
||||
'Volume': sockets.PhysicalVolumeSocketDef(),
|
||||
'Point 3D': sockets.PhysicalPoint3DSocketDef(),
|
||||
##"physical_size_2d": sockets.PhysicalSize2DSocketDef(),
|
||||
"Size 3D": sockets.PhysicalSize3DSocketDef(),
|
||||
"Mass": sockets.PhysicalMassSocketDef(),
|
||||
"Speed": sockets.PhysicalSpeedSocketDef(),
|
||||
"Accel Scalar": sockets.PhysicalAccelScalarSocketDef(),
|
||||
"Force Scalar": sockets.PhysicalForceScalarSocketDef(),
|
||||
#"physical_accel_3dvector": sockets.PhysicalAccel3DVectorSocketDef(),
|
||||
'Size 3D': sockets.PhysicalSize3DSocketDef(),
|
||||
'Mass': sockets.PhysicalMassSocketDef(),
|
||||
'Speed': sockets.PhysicalSpeedSocketDef(),
|
||||
'Accel Scalar': sockets.PhysicalAccelScalarSocketDef(),
|
||||
'Force Scalar': sockets.PhysicalForceScalarSocketDef(),
|
||||
# "physical_accel_3dvector": sockets.PhysicalAccel3DVectorSocketDef(),
|
||||
##"physical_force_3dvector": sockets.PhysicalForce3DVectorSocketDef(),
|
||||
"Pol": sockets.PhysicalPolSocketDef(),
|
||||
"Freq": sockets.PhysicalFreqSocketDef(),
|
||||
'Pol': sockets.PhysicalPolSocketDef(),
|
||||
'Freq': sockets.PhysicalFreqSocketDef(),
|
||||
},
|
||||
"Blender": {
|
||||
"Object": sockets.BlenderObjectSocketDef(),
|
||||
"Collection": sockets.BlenderCollectionSocketDef(),
|
||||
"Image": sockets.BlenderImageSocketDef(),
|
||||
"GeoNodes": sockets.BlenderGeoNodesSocketDef(),
|
||||
"Text": sockets.BlenderTextSocketDef(),
|
||||
'Blender': {
|
||||
'Object': sockets.BlenderObjectSocketDef(),
|
||||
'Collection': sockets.BlenderCollectionSocketDef(),
|
||||
'Image': sockets.BlenderImageSocketDef(),
|
||||
'GeoNodes': sockets.BlenderGeoNodesSocketDef(),
|
||||
'Text': sockets.BlenderTextSocketDef(),
|
||||
},
|
||||
"Maxwell": {
|
||||
"Source": sockets.MaxwellSourceSocketDef(),
|
||||
"Temporal Shape": sockets.MaxwellTemporalShapeSocketDef(),
|
||||
"Medium": sockets.MaxwellMediumSocketDef(),
|
||||
"Medium Non-Linearity": sockets.MaxwellMediumNonLinearitySocketDef(),
|
||||
"Structure": sockets.MaxwellStructureSocketDef(),
|
||||
"Bound Box": sockets.MaxwellBoundBoxSocketDef(),
|
||||
"Bound Face": sockets.MaxwellBoundFaceSocketDef(),
|
||||
"Monitor": sockets.MaxwellMonitorSocketDef(),
|
||||
"FDTD Sim": sockets.MaxwellFDTDSimSocketDef(),
|
||||
"Sim Grid": sockets.MaxwellSimGridSocketDef(),
|
||||
"Sim Grid Axis": sockets.MaxwellSimGridAxisSocketDef(),
|
||||
'Maxwell': {
|
||||
'Source': sockets.MaxwellSourceSocketDef(),
|
||||
'Temporal Shape': sockets.MaxwellTemporalShapeSocketDef(),
|
||||
'Medium': sockets.MaxwellMediumSocketDef(),
|
||||
'Medium Non-Linearity': sockets.MaxwellMediumNonLinearitySocketDef(),
|
||||
'Structure': sockets.MaxwellStructureSocketDef(),
|
||||
'Bound Box': sockets.MaxwellBoundBoxSocketDef(),
|
||||
'Bound Face': sockets.MaxwellBoundFaceSocketDef(),
|
||||
'Monitor': sockets.MaxwellMonitorSocketDef(),
|
||||
'FDTD Sim': sockets.MaxwellFDTDSimSocketDef(),
|
||||
'Sim Grid': sockets.MaxwellSimGridSocketDef(),
|
||||
'Sim Grid Axis': sockets.MaxwellSimGridAxisSocketDef(),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
output_sockets = {
|
||||
"Static Data": sockets.AnySocketDef(),
|
||||
'Static Data': sockets.AnySocketDef(),
|
||||
}
|
||||
output_socket_sets = input_socket_sets
|
||||
|
||||
|
||||
|
||||
####################
|
||||
# - Blender Registration
|
||||
####################
|
||||
BL_REGISTER = [
|
||||
KitchenSinkNode,
|
||||
]
|
||||
BL_NODES = {
|
||||
ct.NodeType.KitchenSink: (
|
||||
ct.NodeCategory.MAXWELLSIM_INPUTS
|
||||
)
|
||||
}
|
||||
BL_NODES = {ct.NodeType.KitchenSink: (ct.NodeCategory.MAXWELLSIM_INPUTS)}
|
||||
|
|
|
@ -1,47 +1,45 @@
|
|||
from . import library_medium
|
||||
|
||||
#from . import pec_medium
|
||||
#from . import isotropic_medium
|
||||
#from . import anisotropic_medium
|
||||
# from . import pec_medium
|
||||
# from . import isotropic_medium
|
||||
# from . import anisotropic_medium
|
||||
#
|
||||
#from . import triple_sellmeier_medium
|
||||
#from . import sellmeier_medium
|
||||
#from . import pole_residue_medium
|
||||
#from . import drude_medium
|
||||
#from . import drude_lorentz_medium
|
||||
#from . import debye_medium
|
||||
# from . import triple_sellmeier_medium
|
||||
# from . import sellmeier_medium
|
||||
# from . import pole_residue_medium
|
||||
# from . import drude_medium
|
||||
# from . import drude_lorentz_medium
|
||||
# from . import debye_medium
|
||||
#
|
||||
#from . import non_linearities
|
||||
# from . import non_linearities
|
||||
|
||||
BL_REGISTER = [
|
||||
*library_medium.BL_REGISTER,
|
||||
|
||||
# *pec_medium.BL_REGISTER,
|
||||
# *isotropic_medium.BL_REGISTER,
|
||||
# *anisotropic_medium.BL_REGISTER,
|
||||
#
|
||||
# *triple_sellmeier_medium.BL_REGISTER,
|
||||
# *sellmeier_medium.BL_REGISTER,
|
||||
# *pole_residue_medium.BL_REGISTER,
|
||||
# *drude_medium.BL_REGISTER,
|
||||
# *drude_lorentz_medium.BL_REGISTER,
|
||||
# *debye_medium.BL_REGISTER,
|
||||
#
|
||||
# *non_linearities.BL_REGISTER,
|
||||
# *pec_medium.BL_REGISTER,
|
||||
# *isotropic_medium.BL_REGISTER,
|
||||
# *anisotropic_medium.BL_REGISTER,
|
||||
#
|
||||
# *triple_sellmeier_medium.BL_REGISTER,
|
||||
# *sellmeier_medium.BL_REGISTER,
|
||||
# *pole_residue_medium.BL_REGISTER,
|
||||
# *drude_medium.BL_REGISTER,
|
||||
# *drude_lorentz_medium.BL_REGISTER,
|
||||
# *debye_medium.BL_REGISTER,
|
||||
#
|
||||
# *non_linearities.BL_REGISTER,
|
||||
]
|
||||
BL_NODES = {
|
||||
**library_medium.BL_NODES,
|
||||
|
||||
# **pec_medium.BL_NODES,
|
||||
# **isotropic_medium.BL_NODES,
|
||||
# **anisotropic_medium.BL_NODES,
|
||||
#
|
||||
# **triple_sellmeier_medium.BL_NODES,
|
||||
# **sellmeier_medium.BL_NODES,
|
||||
# **pole_residue_medium.BL_NODES,
|
||||
# **drude_medium.BL_NODES,
|
||||
# **drude_lorentz_medium.BL_NODES,
|
||||
# **debye_medium.BL_NODES,
|
||||
#
|
||||
# **non_linearities.BL_NODES,
|
||||
# **pec_medium.BL_NODES,
|
||||
# **isotropic_medium.BL_NODES,
|
||||
# **anisotropic_medium.BL_NODES,
|
||||
#
|
||||
# **triple_sellmeier_medium.BL_NODES,
|
||||
# **sellmeier_medium.BL_NODES,
|
||||
# **pole_residue_medium.BL_NODES,
|
||||
# **drude_medium.BL_NODES,
|
||||
# **drude_lorentz_medium.BL_NODES,
|
||||
# **debye_medium.BL_NODES,
|
||||
#
|
||||
# **non_linearities.BL_NODES,
|
||||
}
|
||||
|
|
|
@ -3,4 +3,3 @@
|
|||
####################
|
||||
BL_REGISTER = []
|
||||
BL_NODES = {}
|
||||
|
||||
|
|
|
@ -6,67 +6,72 @@ from ... import contracts
|
|||
from ... import sockets
|
||||
from .. import base
|
||||
|
||||
|
||||
class DrudeLorentzMediumNode(base.MaxwellSimTreeNode):
|
||||
node_type = contracts.NodeType.DrudeLorentzMedium
|
||||
|
||||
bl_label = "Drude-Lorentz Medium"
|
||||
#bl_icon = ...
|
||||
|
||||
|
||||
bl_label = 'Drude-Lorentz Medium'
|
||||
# bl_icon = ...
|
||||
|
||||
####################
|
||||
# - Sockets
|
||||
####################
|
||||
input_sockets = {
|
||||
"eps_inf": sockets.RealNumberSocketDef(
|
||||
label=f"εr_∞",
|
||||
),
|
||||
} | {
|
||||
f"del_eps{i}": sockets.RealNumberSocketDef(
|
||||
label=f"Δεr_{i}",
|
||||
)
|
||||
for i in [1, 2, 3]
|
||||
} | {
|
||||
f"f{i}": sockets.PhysicalFreqSocketDef(
|
||||
label=f"f_{i}",
|
||||
)
|
||||
for i in [1, 2, 3]
|
||||
} | {
|
||||
f"delta{i}": sockets.PhysicalFreqSocketDef(
|
||||
label=f"δ_{i}",
|
||||
)
|
||||
for i in [1, 2, 3]
|
||||
}
|
||||
input_sockets = (
|
||||
{
|
||||
'eps_inf': sockets.RealNumberSocketDef(
|
||||
label=f'εr_∞',
|
||||
),
|
||||
}
|
||||
| {
|
||||
f'del_eps{i}': sockets.RealNumberSocketDef(
|
||||
label=f'Δεr_{i}',
|
||||
)
|
||||
for i in [1, 2, 3]
|
||||
}
|
||||
| {
|
||||
f'f{i}': sockets.PhysicalFreqSocketDef(
|
||||
label=f'f_{i}',
|
||||
)
|
||||
for i in [1, 2, 3]
|
||||
}
|
||||
| {
|
||||
f'delta{i}': sockets.PhysicalFreqSocketDef(
|
||||
label=f'δ_{i}',
|
||||
)
|
||||
for i in [1, 2, 3]
|
||||
}
|
||||
)
|
||||
output_sockets = {
|
||||
"medium": sockets.MaxwellMediumSocketDef(
|
||||
label="Medium"
|
||||
),
|
||||
'medium': sockets.MaxwellMediumSocketDef(label='Medium'),
|
||||
}
|
||||
|
||||
|
||||
####################
|
||||
# - Output Socket Computation
|
||||
####################
|
||||
@base.computes_output_socket("medium")
|
||||
@base.computes_output_socket('medium')
|
||||
def compute_medium(self: contracts.NodeTypeProtocol) -> td.Sellmeier:
|
||||
## Retrieval
|
||||
return td.Lorentz(
|
||||
eps_inf=self.compute_input(f"eps_inf"),
|
||||
coeffs = [
|
||||
(
|
||||
self.compute_input(f"del_eps{i}"),
|
||||
spu.convert_to(
|
||||
self.compute_input(f"f{i}"),
|
||||
spu.hertz,
|
||||
) / spu.hertz,
|
||||
spu.convert_to(
|
||||
self.compute_input(f"delta{i}"),
|
||||
spu.hertz,
|
||||
) / spu.hertz,
|
||||
)
|
||||
for i in [1, 2, 3]
|
||||
]
|
||||
eps_inf=self.compute_input(f'eps_inf'),
|
||||
coeffs=[
|
||||
(
|
||||
self.compute_input(f'del_eps{i}'),
|
||||
spu.convert_to(
|
||||
self.compute_input(f'f{i}'),
|
||||
spu.hertz,
|
||||
)
|
||||
/ spu.hertz,
|
||||
spu.convert_to(
|
||||
self.compute_input(f'delta{i}'),
|
||||
spu.hertz,
|
||||
)
|
||||
/ spu.hertz,
|
||||
)
|
||||
for i in [1, 2, 3]
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
|
||||
####################
|
||||
# - Blender Registration
|
||||
####################
|
||||
|
|
|
@ -3,4 +3,3 @@
|
|||
####################
|
||||
BL_REGISTER = []
|
||||
BL_NODES = {}
|
||||
|
||||
|
|
|
@ -14,55 +14,55 @@ from ... import sockets
|
|||
from ... import managed_objs
|
||||
from .. import base
|
||||
|
||||
VAC_SPEED_OF_LIGHT = (
|
||||
sc.constants.speed_of_light
|
||||
* spu.meter/spu.second
|
||||
)
|
||||
VAC_SPEED_OF_LIGHT = sc.constants.speed_of_light * spu.meter / spu.second
|
||||
|
||||
|
||||
class LibraryMediumNode(base.MaxwellSimNode):
|
||||
node_type = ct.NodeType.LibraryMedium
|
||||
bl_label = "Library Medium"
|
||||
|
||||
bl_label = 'Library Medium'
|
||||
|
||||
####################
|
||||
# - Sockets
|
||||
####################
|
||||
input_sockets = {}
|
||||
output_sockets = {
|
||||
"Medium": sockets.MaxwellMediumSocketDef(),
|
||||
'Medium': sockets.MaxwellMediumSocketDef(),
|
||||
}
|
||||
|
||||
|
||||
managed_obj_defs = {
|
||||
"nk_plot": ct.schemas.ManagedObjDef(
|
||||
'nk_plot': ct.schemas.ManagedObjDef(
|
||||
mk=lambda name: managed_objs.ManagedBLImage(name),
|
||||
name_prefix="",
|
||||
name_prefix='',
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
####################
|
||||
# - Properties
|
||||
####################
|
||||
material: bpy.props.EnumProperty(
|
||||
name="",
|
||||
description="",
|
||||
#icon="NODE_MATERIAL",
|
||||
name='',
|
||||
description='',
|
||||
# icon="NODE_MATERIAL",
|
||||
items=[
|
||||
(
|
||||
mat_key,
|
||||
td.material_library[mat_key].name,
|
||||
", ".join([
|
||||
ref.journal
|
||||
for ref in td.material_library[mat_key].variants[
|
||||
td.material_library[mat_key].default
|
||||
].reference
|
||||
])
|
||||
', '.join(
|
||||
[
|
||||
ref.journal
|
||||
for ref in td.material_library[mat_key]
|
||||
.variants[td.material_library[mat_key].default]
|
||||
.reference
|
||||
]
|
||||
),
|
||||
)
|
||||
for mat_key in td.material_library
|
||||
if mat_key != "graphene" ## For some reason, it's unique...
|
||||
if mat_key != 'graphene' ## For some reason, it's unique...
|
||||
],
|
||||
default="Au",
|
||||
update=(lambda self, context: self.sync_prop("material", context)),
|
||||
default='Au',
|
||||
update=(lambda self, context: self.sync_prop('material', context)),
|
||||
)
|
||||
|
||||
|
||||
@property
|
||||
def freq_range_str(self) -> tuple[sp.Expr, sp.Expr]:
|
||||
## TODO: Cache (node instances don't seem able to keep data outside of properties, not even cached_property)
|
||||
|
@ -71,14 +71,14 @@ class LibraryMediumNode(base.MaxwellSimNode):
|
|||
spu.convert_to(
|
||||
val * spu.hertz,
|
||||
spuex.terahertz,
|
||||
) / spuex.terahertz
|
||||
)
|
||||
/ spuex.terahertz
|
||||
for val in mat.medium.frequency_range
|
||||
]
|
||||
return sp.pretty(
|
||||
[freq_range[0].n(4), freq_range[1].n(4)],
|
||||
use_unicode=True
|
||||
[freq_range[0].n(4), freq_range[1].n(4)], use_unicode=True
|
||||
)
|
||||
|
||||
|
||||
@property
|
||||
def nm_range_str(self) -> str:
|
||||
## TODO: Cache (node instances don't seem able to keep data outside of properties, not even cached_property)
|
||||
|
@ -87,47 +87,47 @@ class LibraryMediumNode(base.MaxwellSimNode):
|
|||
spu.convert_to(
|
||||
VAC_SPEED_OF_LIGHT / (val * spu.hertz),
|
||||
spu.nanometer,
|
||||
) / spu.nanometer
|
||||
)
|
||||
/ spu.nanometer
|
||||
for val in reversed(mat.medium.frequency_range)
|
||||
]
|
||||
return sp.pretty(
|
||||
[nm_range[0].n(4), nm_range[1].n(4)],
|
||||
use_unicode=True
|
||||
[nm_range[0].n(4), nm_range[1].n(4)], use_unicode=True
|
||||
)
|
||||
|
||||
|
||||
####################
|
||||
# - UI
|
||||
####################
|
||||
def draw_props(self, context, layout):
|
||||
layout.prop(self, "material", text="")
|
||||
|
||||
layout.prop(self, 'material', text='')
|
||||
|
||||
def draw_info(self, context, col):
|
||||
# UI Drawing
|
||||
split = col.split(factor=0.23, align=True)
|
||||
|
||||
|
||||
_col = split.column(align=True)
|
||||
_col.alignment = "LEFT"
|
||||
_col.label(text="nm")
|
||||
_col.label(text="THz")
|
||||
|
||||
_col.alignment = 'LEFT'
|
||||
_col.label(text='nm')
|
||||
_col.label(text='THz')
|
||||
|
||||
_col = split.column(align=True)
|
||||
_col.alignment = "RIGHT"
|
||||
_col.alignment = 'RIGHT'
|
||||
_col.label(text=self.nm_range_str)
|
||||
_col.label(text=self.freq_range_str)
|
||||
|
||||
|
||||
####################
|
||||
# - Output Sockets
|
||||
####################
|
||||
@base.computes_output_socket("Medium")
|
||||
@base.computes_output_socket('Medium')
|
||||
def compute_vac_wl(self) -> sp.Expr:
|
||||
return td.material_library[self.material].medium
|
||||
|
||||
|
||||
####################
|
||||
# - Event Callbacks
|
||||
####################
|
||||
@base.on_show_plot(
|
||||
managed_objs={"nk_plot"},
|
||||
props={"material"},
|
||||
managed_objs={'nk_plot'},
|
||||
props={'material'},
|
||||
stop_propagation=True, ## Plot only the first plottable node
|
||||
)
|
||||
def on_show_plot(
|
||||
|
@ -135,28 +135,26 @@ class LibraryMediumNode(base.MaxwellSimNode):
|
|||
managed_objs: dict[str, ct.schemas.ManagedObj],
|
||||
props: dict[str, typ.Any],
|
||||
):
|
||||
medium = td.material_library[props["material"]].medium
|
||||
medium = td.material_library[props['material']].medium
|
||||
freq_range = [
|
||||
spu.convert_to(
|
||||
val * spu.hertz,
|
||||
spuex.terahertz,
|
||||
) / spu.hertz
|
||||
)
|
||||
/ spu.hertz
|
||||
for val in medium.frequency_range
|
||||
]
|
||||
|
||||
managed_objs["nk_plot"].mpl_plot_to_image(
|
||||
|
||||
managed_objs['nk_plot'].mpl_plot_to_image(
|
||||
lambda ax: medium.plot(medium.frequency_range, ax=ax),
|
||||
bl_select=True,
|
||||
)
|
||||
|
||||
|
||||
####################
|
||||
# - Blender Registration
|
||||
####################
|
||||
BL_REGISTER = [
|
||||
LibraryMediumNode,
|
||||
]
|
||||
BL_NODES = {
|
||||
ct.NodeType.LibraryMedium: (
|
||||
ct.NodeCategory.MAXWELLSIM_MEDIUMS
|
||||
)
|
||||
}
|
||||
BL_NODES = {ct.NodeType.LibraryMedium: (ct.NodeCategory.MAXWELLSIM_MEDIUMS)}
|
||||
|
|
|
@ -3,4 +3,3 @@
|
|||
####################
|
||||
BL_REGISTER = []
|
||||
BL_NODES = {}
|
||||
|
||||
|
|
|
@ -3,4 +3,3 @@
|
|||
####################
|
||||
BL_REGISTER = []
|
||||
BL_NODES = {}
|
||||
|
||||
|
|
|
@ -3,4 +3,3 @@
|
|||
####################
|
||||
BL_REGISTER = []
|
||||
BL_NODES = {}
|
||||
|
||||
|
|
|
@ -6,86 +6,86 @@ from ... import contracts
|
|||
from ... import sockets
|
||||
from .. import base
|
||||
|
||||
|
||||
class TripleSellmeierMediumNode(base.MaxwellSimTreeNode):
|
||||
node_type = contracts.NodeType.TripleSellmeierMedium
|
||||
|
||||
bl_label = "Three-Parameter Sellmeier Medium"
|
||||
#bl_icon = ...
|
||||
|
||||
|
||||
bl_label = 'Three-Parameter Sellmeier Medium'
|
||||
# bl_icon = ...
|
||||
|
||||
####################
|
||||
# - Sockets
|
||||
####################
|
||||
input_sockets = {
|
||||
f"B{i}": sockets.RealNumberSocketDef(
|
||||
label=f"B{i}",
|
||||
f'B{i}': sockets.RealNumberSocketDef(
|
||||
label=f'B{i}',
|
||||
)
|
||||
for i in [1, 2, 3]
|
||||
} | {
|
||||
f"C{i}": sockets.PhysicalAreaSocketDef(
|
||||
label=f"C{i}",
|
||||
default_unit=spu.um**2
|
||||
f'C{i}': sockets.PhysicalAreaSocketDef(
|
||||
label=f'C{i}', default_unit=spu.um**2
|
||||
)
|
||||
for i in [1, 2, 3]
|
||||
}
|
||||
output_sockets = {
|
||||
"medium": sockets.MaxwellMediumSocketDef(
|
||||
label="Medium"
|
||||
),
|
||||
'medium': sockets.MaxwellMediumSocketDef(label='Medium'),
|
||||
}
|
||||
|
||||
|
||||
####################
|
||||
# - Presets
|
||||
####################
|
||||
presets = {
|
||||
"BK7": contracts.PresetDef(
|
||||
label="BK7 Glass",
|
||||
description="Borosilicate crown glass (known as BK7)",
|
||||
'BK7': contracts.PresetDef(
|
||||
label='BK7 Glass',
|
||||
description='Borosilicate crown glass (known as BK7)',
|
||||
values={
|
||||
"B1": 1.03961212,
|
||||
"B2": 0.231792344,
|
||||
"B3": 1.01046945,
|
||||
"C1": 6.00069867e-3 * spu.um**2,
|
||||
"C2": 2.00179144e-2 * spu.um**2,
|
||||
"C3": 103.560653 * spu.um**2,
|
||||
}
|
||||
'B1': 1.03961212,
|
||||
'B2': 0.231792344,
|
||||
'B3': 1.01046945,
|
||||
'C1': 6.00069867e-3 * spu.um**2,
|
||||
'C2': 2.00179144e-2 * spu.um**2,
|
||||
'C3': 103.560653 * spu.um**2,
|
||||
},
|
||||
),
|
||||
"FUSED_SILICA": contracts.PresetDef(
|
||||
label="Fused Silica",
|
||||
description="Fused silica aka. SiO2",
|
||||
'FUSED_SILICA': contracts.PresetDef(
|
||||
label='Fused Silica',
|
||||
description='Fused silica aka. SiO2',
|
||||
values={
|
||||
"B1": 0.696166300,
|
||||
"B2": 0.407942600,
|
||||
"B3": 0.897479400,
|
||||
"C1": 4.67914826e-3 * spu.um**2,
|
||||
"C2": 1.35120631e-2 * spu.um**2,
|
||||
"C3": 97.9340025 * spu.um**2,
|
||||
}
|
||||
'B1': 0.696166300,
|
||||
'B2': 0.407942600,
|
||||
'B3': 0.897479400,
|
||||
'C1': 4.67914826e-3 * spu.um**2,
|
||||
'C2': 1.35120631e-2 * spu.um**2,
|
||||
'C3': 97.9340025 * spu.um**2,
|
||||
},
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
####################
|
||||
# - Output Socket Computation
|
||||
####################
|
||||
@base.computes_output_socket("medium")
|
||||
@base.computes_output_socket('medium')
|
||||
def compute_medium(self: contracts.NodeTypeProtocol) -> td.Sellmeier:
|
||||
## Retrieval
|
||||
#B1 = self.compute_input("B1")
|
||||
#C1_with_units = self.compute_input("C1")
|
||||
# B1 = self.compute_input("B1")
|
||||
# C1_with_units = self.compute_input("C1")
|
||||
#
|
||||
## Processing
|
||||
#C1 = spu.convert_to(C1_with_units, spu.um**2) / spu.um**2
|
||||
|
||||
return td.Sellmeier(coeffs = [
|
||||
(
|
||||
self.compute_input(f"B{i}"),
|
||||
spu.convert_to(
|
||||
self.compute_input(f"C{i}"),
|
||||
spu.um**2,
|
||||
) / spu.um**2
|
||||
)
|
||||
for i in [1, 2, 3]
|
||||
])
|
||||
# C1 = spu.convert_to(C1_with_units, spu.um**2) / spu.um**2
|
||||
|
||||
return td.Sellmeier(
|
||||
coeffs=[
|
||||
(
|
||||
self.compute_input(f'B{i}'),
|
||||
spu.convert_to(
|
||||
self.compute_input(f'C{i}'),
|
||||
spu.um**2,
|
||||
)
|
||||
/ spu.um**2,
|
||||
)
|
||||
for i in [1, 2, 3]
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
####################
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
from . import eh_field_monitor
|
||||
from . import field_power_flux_monitor
|
||||
#from . import epsilon_tensor_monitor
|
||||
#from . import diffraction_monitor
|
||||
# from . import epsilon_tensor_monitor
|
||||
# from . import diffraction_monitor
|
||||
|
||||
BL_REGISTER = [
|
||||
*eh_field_monitor.BL_REGISTER,
|
||||
*field_power_flux_monitor.BL_REGISTER,
|
||||
# *epsilon_tensor_monitor.BL_REGISTER,
|
||||
# *diffraction_monitor.BL_REGISTER,
|
||||
# *epsilon_tensor_monitor.BL_REGISTER,
|
||||
# *diffraction_monitor.BL_REGISTER,
|
||||
]
|
||||
BL_NODES = {
|
||||
**eh_field_monitor.BL_NODES,
|
||||
**field_power_flux_monitor.BL_NODES,
|
||||
# **epsilon_tensor_monitor.BL_NODES,
|
||||
# **diffraction_monitor.BL_NODES,
|
||||
# **epsilon_tensor_monitor.BL_NODES,
|
||||
# **diffraction_monitor.BL_NODES,
|
||||
}
|
||||
|
|
|
@ -3,4 +3,3 @@
|
|||
####################
|
||||
BL_REGISTER = []
|
||||
BL_NODES = {}
|
||||
|
||||
|
|
|
@ -15,90 +15,98 @@ from ... import sockets
|
|||
from ... import managed_objs
|
||||
from .. import base
|
||||
|
||||
GEONODES_MONITOR_BOX = "monitor_box"
|
||||
GEONODES_MONITOR_BOX = 'monitor_box'
|
||||
|
||||
|
||||
class EHFieldMonitorNode(base.MaxwellSimNode):
|
||||
node_type = ct.NodeType.EHFieldMonitor
|
||||
bl_label = "E/H Field Monitor"
|
||||
bl_label = 'E/H Field Monitor'
|
||||
use_sim_node_name = True
|
||||
|
||||
|
||||
####################
|
||||
# - Sockets
|
||||
####################
|
||||
input_sockets = {
|
||||
"Center": sockets.PhysicalPoint3DSocketDef(),
|
||||
"Size": sockets.PhysicalSize3DSocketDef(),
|
||||
"Samples/Space": sockets.Integer3DVectorSocketDef(
|
||||
'Center': sockets.PhysicalPoint3DSocketDef(),
|
||||
'Size': sockets.PhysicalSize3DSocketDef(),
|
||||
'Samples/Space': sockets.Integer3DVectorSocketDef(
|
||||
default_value=sp.Matrix([10, 10, 10])
|
||||
),
|
||||
}
|
||||
input_socket_sets = {
|
||||
"Freq Domain": {
|
||||
"Freqs": sockets.PhysicalFreqSocketDef(
|
||||
'Freq Domain': {
|
||||
'Freqs': sockets.PhysicalFreqSocketDef(
|
||||
is_list=True,
|
||||
),
|
||||
},
|
||||
"Time Domain": {
|
||||
"Rec Start": sockets.PhysicalTimeSocketDef(),
|
||||
"Rec Stop": sockets.PhysicalTimeSocketDef(
|
||||
default_value=200*spux.fs
|
||||
'Time Domain': {
|
||||
'Rec Start': sockets.PhysicalTimeSocketDef(),
|
||||
'Rec Stop': sockets.PhysicalTimeSocketDef(
|
||||
default_value=200 * spux.fs
|
||||
),
|
||||
"Samples/Time": sockets.IntegerNumberSocketDef(
|
||||
'Samples/Time': sockets.IntegerNumberSocketDef(
|
||||
default_value=100,
|
||||
),
|
||||
},
|
||||
}
|
||||
output_sockets = {
|
||||
"Monitor": sockets.MaxwellMonitorSocketDef(),
|
||||
'Monitor': sockets.MaxwellMonitorSocketDef(),
|
||||
}
|
||||
|
||||
|
||||
managed_obj_defs = {
|
||||
"monitor_box": ct.schemas.ManagedObjDef(
|
||||
'monitor_box': ct.schemas.ManagedObjDef(
|
||||
mk=lambda name: managed_objs.ManagedBLObject(name),
|
||||
name_prefix="",
|
||||
name_prefix='',
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
####################
|
||||
# - Properties
|
||||
####################
|
||||
|
||||
|
||||
####################
|
||||
# - UI
|
||||
####################
|
||||
def draw_props(self, context, layout):
|
||||
pass
|
||||
|
||||
|
||||
def draw_info(self, context, col):
|
||||
pass
|
||||
|
||||
|
||||
####################
|
||||
# - Output Sockets
|
||||
####################
|
||||
@base.computes_output_socket(
|
||||
"Monitor",
|
||||
'Monitor',
|
||||
input_sockets={
|
||||
"Rec Start", "Rec Stop", "Center", "Size", "Samples/Space",
|
||||
"Samples/Time", "Freqs",
|
||||
'Rec Start',
|
||||
'Rec Stop',
|
||||
'Center',
|
||||
'Size',
|
||||
'Samples/Space',
|
||||
'Samples/Time',
|
||||
'Freqs',
|
||||
},
|
||||
props={"active_socket_set", "sim_node_name"}
|
||||
props={'active_socket_set', 'sim_node_name'},
|
||||
)
|
||||
def compute_monitor(self, input_sockets: dict, props: dict) -> td.FieldTimeMonitor:
|
||||
_center = input_sockets["Center"]
|
||||
_size = input_sockets["Size"]
|
||||
_samples_space = input_sockets["Samples/Space"]
|
||||
|
||||
def compute_monitor(
|
||||
self, input_sockets: dict, props: dict
|
||||
) -> td.FieldTimeMonitor:
|
||||
_center = input_sockets['Center']
|
||||
_size = input_sockets['Size']
|
||||
_samples_space = input_sockets['Samples/Space']
|
||||
|
||||
center = tuple(spu.convert_to(_center, spu.um) / spu.um)
|
||||
size = tuple(spu.convert_to(_size, spu.um) / spu.um)
|
||||
samples_space = tuple(_samples_space)
|
||||
|
||||
if props["active_socket_set"] == "Freq Domain":
|
||||
freqs = input_sockets["Freqs"]
|
||||
|
||||
|
||||
if props['active_socket_set'] == 'Freq Domain':
|
||||
freqs = input_sockets['Freqs']
|
||||
|
||||
return td.FieldMonitor(
|
||||
center=center,
|
||||
size=size,
|
||||
name=props["sim_node_name"],
|
||||
name=props['sim_node_name'],
|
||||
interval_space=samples_space,
|
||||
freqs=[
|
||||
float(spu.convert_to(freq, spu.hertz) / spu.hertz)
|
||||
|
@ -106,91 +114,86 @@ class EHFieldMonitorNode(base.MaxwellSimNode):
|
|||
],
|
||||
)
|
||||
else: ## Time Domain
|
||||
_rec_start = input_sockets["Rec Start"]
|
||||
_rec_stop = input_sockets["Rec Stop"]
|
||||
samples_time = input_sockets["Samples/Time"]
|
||||
|
||||
_rec_start = input_sockets['Rec Start']
|
||||
_rec_stop = input_sockets['Rec Stop']
|
||||
samples_time = input_sockets['Samples/Time']
|
||||
|
||||
rec_start = spu.convert_to(_rec_start, spu.second) / spu.second
|
||||
rec_stop = spu.convert_to(_rec_stop, spu.second) / spu.second
|
||||
|
||||
|
||||
return td.FieldTimeMonitor(
|
||||
center=center,
|
||||
size=size,
|
||||
name=props["sim_node_name"],
|
||||
name=props['sim_node_name'],
|
||||
start=rec_start,
|
||||
stop=rec_stop,
|
||||
interval=samples_time,
|
||||
interval_space=samples_space,
|
||||
)
|
||||
|
||||
|
||||
####################
|
||||
# - Preview - Changes to Input Sockets
|
||||
####################
|
||||
@base.on_value_changed(
|
||||
socket_name={"Center", "Size"},
|
||||
input_sockets={"Center", "Size"},
|
||||
managed_objs={"monitor_box"},
|
||||
socket_name={'Center', 'Size'},
|
||||
input_sockets={'Center', 'Size'},
|
||||
managed_objs={'monitor_box'},
|
||||
)
|
||||
def on_value_changed__center_size(
|
||||
self,
|
||||
input_sockets: dict,
|
||||
managed_objs: dict[str, ct.schemas.ManagedObj],
|
||||
):
|
||||
_center = input_sockets["Center"]
|
||||
center = tuple([
|
||||
float(el)
|
||||
for el in spu.convert_to(_center, spu.um) / spu.um
|
||||
])
|
||||
|
||||
_size = input_sockets["Size"]
|
||||
size = tuple([
|
||||
float(el)
|
||||
for el in spu.convert_to(_size, spu.um) / spu.um
|
||||
])
|
||||
_center = input_sockets['Center']
|
||||
center = tuple(
|
||||
[float(el) for el in spu.convert_to(_center, spu.um) / spu.um]
|
||||
)
|
||||
|
||||
_size = input_sockets['Size']
|
||||
size = tuple(
|
||||
[float(el) for el in spu.convert_to(_size, spu.um) / spu.um]
|
||||
)
|
||||
## TODO: Preview unit system?? Presume um for now
|
||||
|
||||
|
||||
# Retrieve Hard-Coded GeoNodes and Analyze Input
|
||||
geo_nodes = bpy.data.node_groups[GEONODES_MONITOR_BOX]
|
||||
geonodes_interface = analyze_geonodes.interface(
|
||||
geo_nodes, direc="INPUT"
|
||||
geo_nodes, direc='INPUT'
|
||||
)
|
||||
|
||||
|
||||
# Sync Modifier Inputs
|
||||
managed_objs["monitor_box"].sync_geonodes_modifier(
|
||||
managed_objs['monitor_box'].sync_geonodes_modifier(
|
||||
geonodes_node_group=geo_nodes,
|
||||
geonodes_identifier_to_value={
|
||||
geonodes_interface["Size"].identifier: size,
|
||||
geonodes_interface['Size'].identifier: size,
|
||||
## TODO: Use 'bl_socket_map.value_to_bl`!
|
||||
## - This accounts for auto-conversion, unit systems, etc. .
|
||||
## - We could keep it in the node base class...
|
||||
## - ...But it needs aligning with Blender, too. Hmm.
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
# Sync Object Position
|
||||
managed_objs["monitor_box"].bl_object("MESH").location = center
|
||||
|
||||
managed_objs['monitor_box'].bl_object('MESH').location = center
|
||||
|
||||
####################
|
||||
# - Preview - Show Preview
|
||||
####################
|
||||
@base.on_show_preview(
|
||||
managed_objs={"monitor_box"},
|
||||
managed_objs={'monitor_box'},
|
||||
)
|
||||
def on_show_preview(
|
||||
self,
|
||||
managed_objs: dict[str, ct.schemas.ManagedObj],
|
||||
):
|
||||
managed_objs["monitor_box"].show_preview("MESH")
|
||||
managed_objs['monitor_box'].show_preview('MESH')
|
||||
self.on_value_changed__center_size()
|
||||
|
||||
|
||||
####################
|
||||
# - Blender Registration
|
||||
####################
|
||||
BL_REGISTER = [
|
||||
EHFieldMonitorNode,
|
||||
]
|
||||
BL_NODES = {
|
||||
ct.NodeType.EHFieldMonitor: (
|
||||
ct.NodeCategory.MAXWELLSIM_MONITORS
|
||||
)
|
||||
}
|
||||
BL_NODES = {ct.NodeType.EHFieldMonitor: (ct.NodeCategory.MAXWELLSIM_MONITORS)}
|
||||
|
|
|
@ -3,4 +3,3 @@
|
|||
####################
|
||||
BL_REGISTER = []
|
||||
BL_NODES = {}
|
||||
|
||||
|
|
|
@ -15,93 +15,102 @@ from ... import sockets
|
|||
from ... import managed_objs
|
||||
from .. import base
|
||||
|
||||
GEONODES_MONITOR_BOX = "monitor_flux_box"
|
||||
GEONODES_MONITOR_BOX = 'monitor_flux_box'
|
||||
|
||||
|
||||
class FieldPowerFluxMonitorNode(base.MaxwellSimNode):
|
||||
node_type = ct.NodeType.FieldPowerFluxMonitor
|
||||
bl_label = "Field Power Flux Monitor"
|
||||
bl_label = 'Field Power Flux Monitor'
|
||||
use_sim_node_name = True
|
||||
|
||||
|
||||
####################
|
||||
# - Sockets
|
||||
####################
|
||||
input_sockets = {
|
||||
"Center": sockets.PhysicalPoint3DSocketDef(),
|
||||
"Size": sockets.PhysicalSize3DSocketDef(),
|
||||
"Samples/Space": sockets.Integer3DVectorSocketDef(
|
||||
'Center': sockets.PhysicalPoint3DSocketDef(),
|
||||
'Size': sockets.PhysicalSize3DSocketDef(),
|
||||
'Samples/Space': sockets.Integer3DVectorSocketDef(
|
||||
default_value=sp.Matrix([10, 10, 10])
|
||||
),
|
||||
"Direction": sockets.BoolSocketDef(),
|
||||
'Direction': sockets.BoolSocketDef(),
|
||||
}
|
||||
input_socket_sets = {
|
||||
"Freq Domain": {
|
||||
"Freqs": sockets.PhysicalFreqSocketDef(
|
||||
'Freq Domain': {
|
||||
'Freqs': sockets.PhysicalFreqSocketDef(
|
||||
is_list=True,
|
||||
),
|
||||
},
|
||||
"Time Domain": {
|
||||
"Rec Start": sockets.PhysicalTimeSocketDef(),
|
||||
"Rec Stop": sockets.PhysicalTimeSocketDef(
|
||||
default_value=200*spux.fs
|
||||
'Time Domain': {
|
||||
'Rec Start': sockets.PhysicalTimeSocketDef(),
|
||||
'Rec Stop': sockets.PhysicalTimeSocketDef(
|
||||
default_value=200 * spux.fs
|
||||
),
|
||||
"Samples/Time": sockets.IntegerNumberSocketDef(
|
||||
'Samples/Time': sockets.IntegerNumberSocketDef(
|
||||
default_value=100,
|
||||
),
|
||||
},
|
||||
}
|
||||
output_sockets = {
|
||||
"Monitor": sockets.MaxwellMonitorSocketDef(),
|
||||
'Monitor': sockets.MaxwellMonitorSocketDef(),
|
||||
}
|
||||
|
||||
|
||||
managed_obj_defs = {
|
||||
"monitor_box": ct.schemas.ManagedObjDef(
|
||||
'monitor_box': ct.schemas.ManagedObjDef(
|
||||
mk=lambda name: managed_objs.ManagedBLObject(name),
|
||||
name_prefix="",
|
||||
name_prefix='',
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
####################
|
||||
# - Properties
|
||||
####################
|
||||
|
||||
|
||||
####################
|
||||
# - UI
|
||||
####################
|
||||
def draw_props(self, context, layout):
|
||||
pass
|
||||
|
||||
|
||||
def draw_info(self, context, col):
|
||||
pass
|
||||
|
||||
|
||||
####################
|
||||
# - Output Sockets
|
||||
####################
|
||||
@base.computes_output_socket(
|
||||
"Monitor",
|
||||
'Monitor',
|
||||
input_sockets={
|
||||
"Rec Start", "Rec Stop", "Center", "Size", "Samples/Space",
|
||||
"Samples/Time", "Freqs", "Direction",
|
||||
'Rec Start',
|
||||
'Rec Stop',
|
||||
'Center',
|
||||
'Size',
|
||||
'Samples/Space',
|
||||
'Samples/Time',
|
||||
'Freqs',
|
||||
'Direction',
|
||||
},
|
||||
props={"active_socket_set", "sim_node_name"}
|
||||
props={'active_socket_set', 'sim_node_name'},
|
||||
)
|
||||
def compute_monitor(self, input_sockets: dict, props: dict) -> td.FieldTimeMonitor:
|
||||
_center = input_sockets["Center"]
|
||||
_size = input_sockets["Size"]
|
||||
_samples_space = input_sockets["Samples/Space"]
|
||||
|
||||
def compute_monitor(
|
||||
self, input_sockets: dict, props: dict
|
||||
) -> td.FieldTimeMonitor:
|
||||
_center = input_sockets['Center']
|
||||
_size = input_sockets['Size']
|
||||
_samples_space = input_sockets['Samples/Space']
|
||||
|
||||
center = tuple(spu.convert_to(_center, spu.um) / spu.um)
|
||||
size = tuple(spu.convert_to(_size, spu.um) / spu.um)
|
||||
samples_space = tuple(_samples_space)
|
||||
|
||||
direction = "+" if input_sockets["Direction"] else "-"
|
||||
|
||||
if props["active_socket_set"] == "Freq Domain":
|
||||
freqs = input_sockets["Freqs"]
|
||||
|
||||
|
||||
direction = '+' if input_sockets['Direction'] else '-'
|
||||
|
||||
if props['active_socket_set'] == 'Freq Domain':
|
||||
freqs = input_sockets['Freqs']
|
||||
|
||||
return td.FluxMonitor(
|
||||
center=center,
|
||||
size=size,
|
||||
name=props["sim_node_name"],
|
||||
name=props['sim_node_name'],
|
||||
interval_space=samples_space,
|
||||
freqs=[
|
||||
float(spu.convert_to(freq, spu.hertz) / spu.hertz)
|
||||
|
@ -110,84 +119,85 @@ class FieldPowerFluxMonitorNode(base.MaxwellSimNode):
|
|||
normal_dir=direction,
|
||||
)
|
||||
else: ## Time Domain
|
||||
_rec_start = input_sockets["Rec Start"]
|
||||
_rec_stop = input_sockets["Rec Stop"]
|
||||
samples_time = input_sockets["Samples/Time"]
|
||||
|
||||
_rec_start = input_sockets['Rec Start']
|
||||
_rec_stop = input_sockets['Rec Stop']
|
||||
samples_time = input_sockets['Samples/Time']
|
||||
|
||||
rec_start = spu.convert_to(_rec_start, spu.second) / spu.second
|
||||
rec_stop = spu.convert_to(_rec_stop, spu.second) / spu.second
|
||||
|
||||
|
||||
return td.FieldTimeMonitor(
|
||||
center=center,
|
||||
size=size,
|
||||
name=props["sim_node_name"],
|
||||
name=props['sim_node_name'],
|
||||
start=rec_start,
|
||||
stop=rec_stop,
|
||||
interval=samples_time,
|
||||
interval_space=samples_space,
|
||||
)
|
||||
|
||||
|
||||
####################
|
||||
# - Preview - Changes to Input Sockets
|
||||
####################
|
||||
@base.on_value_changed(
|
||||
socket_name={"Center", "Size"},
|
||||
input_sockets={"Center", "Size", "Direction"},
|
||||
managed_objs={"monitor_box"},
|
||||
socket_name={'Center', 'Size'},
|
||||
input_sockets={'Center', 'Size', 'Direction'},
|
||||
managed_objs={'monitor_box'},
|
||||
)
|
||||
def on_value_changed__center_size(
|
||||
self,
|
||||
input_sockets: dict,
|
||||
managed_objs: dict[str, ct.schemas.ManagedObj],
|
||||
):
|
||||
_center = input_sockets["Center"]
|
||||
center = tuple([
|
||||
float(el)
|
||||
for el in spu.convert_to(_center, spu.um) / spu.um
|
||||
])
|
||||
|
||||
_size = input_sockets["Size"]
|
||||
size = tuple([
|
||||
float(el)
|
||||
for el in spu.convert_to(_size, spu.um) / spu.um
|
||||
])
|
||||
_center = input_sockets['Center']
|
||||
center = tuple(
|
||||
[float(el) for el in spu.convert_to(_center, spu.um) / spu.um]
|
||||
)
|
||||
|
||||
_size = input_sockets['Size']
|
||||
size = tuple(
|
||||
[float(el) for el in spu.convert_to(_size, spu.um) / spu.um]
|
||||
)
|
||||
## TODO: Preview unit system?? Presume um for now
|
||||
|
||||
|
||||
# Retrieve Hard-Coded GeoNodes and Analyze Input
|
||||
geo_nodes = bpy.data.node_groups[GEONODES_MONITOR_BOX]
|
||||
geonodes_interface = analyze_geonodes.interface(
|
||||
geo_nodes, direc="INPUT"
|
||||
geo_nodes, direc='INPUT'
|
||||
)
|
||||
|
||||
|
||||
# Sync Modifier Inputs
|
||||
managed_objs["monitor_box"].sync_geonodes_modifier(
|
||||
managed_objs['monitor_box'].sync_geonodes_modifier(
|
||||
geonodes_node_group=geo_nodes,
|
||||
geonodes_identifier_to_value={
|
||||
geonodes_interface["Size"].identifier: size,
|
||||
geonodes_interface["Direction"].identifier: input_sockets["Direction"],
|
||||
geonodes_interface['Size'].identifier: size,
|
||||
geonodes_interface['Direction'].identifier: input_sockets[
|
||||
'Direction'
|
||||
],
|
||||
## TODO: Use 'bl_socket_map.value_to_bl`!
|
||||
## - This accounts for auto-conversion, unit systems, etc. .
|
||||
## - We could keep it in the node base class...
|
||||
## - ...But it needs aligning with Blender, too. Hmm.
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
# Sync Object Position
|
||||
managed_objs["monitor_box"].bl_object("MESH").location = center
|
||||
|
||||
managed_objs['monitor_box'].bl_object('MESH').location = center
|
||||
|
||||
####################
|
||||
# - Preview - Show Preview
|
||||
####################
|
||||
@base.on_show_preview(
|
||||
managed_objs={"monitor_box"},
|
||||
managed_objs={'monitor_box'},
|
||||
)
|
||||
def on_show_preview(
|
||||
self,
|
||||
managed_objs: dict[str, ct.schemas.ManagedObj],
|
||||
):
|
||||
managed_objs["monitor_box"].show_preview("MESH")
|
||||
managed_objs['monitor_box'].show_preview('MESH')
|
||||
self.on_value_changed__center_size()
|
||||
|
||||
|
||||
####################
|
||||
# - Blender Registration
|
||||
####################
|
||||
|
@ -195,7 +205,5 @@ BL_REGISTER = [
|
|||
FieldPowerFluxMonitorNode,
|
||||
]
|
||||
BL_NODES = {
|
||||
ct.NodeType.FieldPowerFluxMonitor: (
|
||||
ct.NodeCategory.MAXWELLSIM_MONITORS
|
||||
)
|
||||
ct.NodeType.FieldPowerFluxMonitor: (ct.NodeCategory.MAXWELLSIM_MONITORS)
|
||||
}
|
||||
|
|
|
@ -11,11 +11,12 @@ from .... import contracts as ct
|
|||
from .... import sockets
|
||||
from ... import base
|
||||
|
||||
|
||||
####################
|
||||
# - Operators
|
||||
####################
|
||||
class JSONFileExporterSaveJSON(bpy.types.Operator):
|
||||
bl_idname = "blender_maxwell.json_file_exporter_save_json"
|
||||
bl_idname = 'blender_maxwell.json_file_exporter_save_json'
|
||||
bl_label = "Save the JSON of what's linked into a JSONFileExporterNode."
|
||||
|
||||
@classmethod
|
||||
|
@ -27,28 +28,29 @@ class JSONFileExporterSaveJSON(bpy.types.Operator):
|
|||
node.export_data_as_json()
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
####################
|
||||
# - Node
|
||||
####################
|
||||
class JSONFileExporterNode(base.MaxwellSimNode):
|
||||
node_type = ct.NodeType.JSONFileExporter
|
||||
|
||||
bl_label = "JSON File Exporter"
|
||||
#bl_icon = constants.ICON_SIM_INPUT
|
||||
|
||||
|
||||
bl_label = 'JSON File Exporter'
|
||||
# bl_icon = constants.ICON_SIM_INPUT
|
||||
|
||||
input_sockets = {
|
||||
"Data": sockets.AnySocketDef(),
|
||||
"JSON Path": sockets.FilePathSocketDef(
|
||||
default_path=Path("simulation.json")
|
||||
'Data': sockets.AnySocketDef(),
|
||||
'JSON Path': sockets.FilePathSocketDef(
|
||||
default_path=Path('simulation.json')
|
||||
),
|
||||
"JSON Indent": sockets.IntegerNumberSocketDef(
|
||||
'JSON Indent': sockets.IntegerNumberSocketDef(
|
||||
default_value=4,
|
||||
),
|
||||
}
|
||||
output_sockets = {
|
||||
"JSON String": sockets.StringSocketDef(),
|
||||
'JSON String': sockets.StringSocketDef(),
|
||||
}
|
||||
|
||||
|
||||
####################
|
||||
# - UI Layout
|
||||
####################
|
||||
|
@ -57,37 +59,39 @@ class JSONFileExporterNode(base.MaxwellSimNode):
|
|||
context: bpy.types.Context,
|
||||
layout: bpy.types.UILayout,
|
||||
) -> None:
|
||||
layout.operator(JSONFileExporterSaveJSON.bl_idname, text="Save JSON")
|
||||
layout.operator(JSONFileExporterSaveJSON.bl_idname, text='Save JSON')
|
||||
|
||||
####################
|
||||
# - Methods
|
||||
####################
|
||||
def export_data_as_json(self) -> None:
|
||||
if (json_str := self.compute_output("JSON String")):
|
||||
if json_str := self.compute_output('JSON String'):
|
||||
data_dict = json.loads(json_str)
|
||||
with self._compute_input("JSON Path").open("w") as f:
|
||||
indent = self._compute_input("JSON Indent")
|
||||
with self._compute_input('JSON Path').open('w') as f:
|
||||
indent = self._compute_input('JSON Indent')
|
||||
json.dump(data_dict, f, ensure_ascii=False, indent=indent)
|
||||
|
||||
|
||||
####################
|
||||
# - Output Sockets
|
||||
####################
|
||||
@base.computes_output_socket(
|
||||
"JSON String",
|
||||
input_sockets={"Data"},
|
||||
'JSON String',
|
||||
input_sockets={'Data'},
|
||||
)
|
||||
def compute_json_string(self, input_sockets: dict[str, typ.Any]) -> str | None:
|
||||
if not (data := input_sockets["Data"]):
|
||||
def compute_json_string(
|
||||
self, input_sockets: dict[str, typ.Any]
|
||||
) -> str | None:
|
||||
if not (data := input_sockets['Data']):
|
||||
return None
|
||||
|
||||
|
||||
# Tidy3D Objects: Call .json()
|
||||
if hasattr(data, "json"):
|
||||
if hasattr(data, 'json'):
|
||||
return data.json()
|
||||
|
||||
|
||||
# Pydantic Models: Call .model_dump_json()
|
||||
elif isinstance(data, pyd.BaseModel):
|
||||
return data.model_dump_json()
|
||||
|
||||
|
||||
else:
|
||||
json.dumps(data)
|
||||
|
||||
|
|
|
@ -16,25 +16,25 @@ from .... import contracts as ct
|
|||
from .... import sockets
|
||||
from ... import base
|
||||
|
||||
|
||||
####################
|
||||
# - Web Uploader / Loader / Runner / Releaser
|
||||
####################
|
||||
class UploadSimulation(bpy.types.Operator):
|
||||
bl_idname = "blender_maxwell.nodes__upload_simulation"
|
||||
bl_label = "Upload Tidy3D Simulation"
|
||||
bl_description = "Upload the attached (locked) simulation, such that it is ready to run on the Tidy3D cloud"
|
||||
bl_idname = 'blender_maxwell.nodes__upload_simulation'
|
||||
bl_label = 'Upload Tidy3D Simulation'
|
||||
bl_description = 'Upload the attached (locked) simulation, such that it is ready to run on the Tidy3D cloud'
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return (
|
||||
hasattr(context, "node")
|
||||
and hasattr(context.node, "node_type")
|
||||
hasattr(context, 'node')
|
||||
and hasattr(context.node, 'node_type')
|
||||
and context.node.node_type == ct.NodeType.Tidy3DWebExporter
|
||||
|
||||
and context.node.lock_tree
|
||||
and tdcloud.IS_AUTHENTICATED
|
||||
and not context.node.tracked_task_id
|
||||
and context.node.inputs["FDTD Sim"].is_linked
|
||||
and context.node.inputs['FDTD Sim'].is_linked
|
||||
)
|
||||
|
||||
def execute(self, context):
|
||||
|
@ -42,24 +42,27 @@ class UploadSimulation(bpy.types.Operator):
|
|||
node.upload_sim()
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class RunSimulation(bpy.types.Operator):
|
||||
bl_idname = "blender_maxwell.nodes__run_simulation"
|
||||
bl_label = "Run Tracked Tidy3D Sim"
|
||||
bl_description = "Run the currently tracked simulation task"
|
||||
bl_idname = 'blender_maxwell.nodes__run_simulation'
|
||||
bl_label = 'Run Tracked Tidy3D Sim'
|
||||
bl_description = 'Run the currently tracked simulation task'
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return (
|
||||
hasattr(context, "node")
|
||||
and hasattr(context.node, "node_type")
|
||||
hasattr(context, 'node')
|
||||
and hasattr(context.node, 'node_type')
|
||||
and context.node.node_type == ct.NodeType.Tidy3DWebExporter
|
||||
|
||||
and tdcloud.IS_AUTHENTICATED
|
||||
and context.node.tracked_task_id
|
||||
and (task_info := tdcloud.TidyCloudTasks.task_info(
|
||||
context.node.tracked_task_id
|
||||
)) is not None
|
||||
and task_info.status == "draft"
|
||||
and (
|
||||
task_info := tdcloud.TidyCloudTasks.task_info(
|
||||
context.node.tracked_task_id
|
||||
)
|
||||
)
|
||||
is not None
|
||||
and task_info.status == 'draft'
|
||||
)
|
||||
|
||||
def execute(self, context):
|
||||
|
@ -67,18 +70,18 @@ class RunSimulation(bpy.types.Operator):
|
|||
node.run_tracked_task()
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class ReloadTrackedTask(bpy.types.Operator):
|
||||
bl_idname = "blender_maxwell.nodes__reload_tracked_task"
|
||||
bl_label = "Reload Tracked Tidy3D Cloud Task"
|
||||
bl_description = "Reload the currently tracked simulation task"
|
||||
bl_idname = 'blender_maxwell.nodes__reload_tracked_task'
|
||||
bl_label = 'Reload Tracked Tidy3D Cloud Task'
|
||||
bl_description = 'Reload the currently tracked simulation task'
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return (
|
||||
hasattr(context, "node")
|
||||
and hasattr(context.node, "node_type")
|
||||
hasattr(context, 'node')
|
||||
and hasattr(context.node, 'node_type')
|
||||
and context.node.node_type == ct.NodeType.Tidy3DWebExporter
|
||||
|
||||
and tdcloud.IS_AUTHENTICATED
|
||||
and context.node.tracked_task_id
|
||||
)
|
||||
|
@ -90,22 +93,22 @@ class ReloadTrackedTask(bpy.types.Operator):
|
|||
) is None:
|
||||
msg = "Tried to reload tracked task, but it doesn't exist"
|
||||
raise RuntimeError(msg)
|
||||
|
||||
|
||||
cloud_task = tdcloud.TidyCloudTasks.update_task(cloud_task)
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class EstCostTrackedTask(bpy.types.Operator):
|
||||
bl_idname = "blender_maxwell.nodes__est_cost_tracked_task"
|
||||
bl_label = "Est Cost of Tracked Tidy3D Cloud Task"
|
||||
bl_description = "Reload the currently tracked simulation task"
|
||||
bl_idname = 'blender_maxwell.nodes__est_cost_tracked_task'
|
||||
bl_label = 'Est Cost of Tracked Tidy3D Cloud Task'
|
||||
bl_description = 'Reload the currently tracked simulation task'
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return (
|
||||
hasattr(context, "node")
|
||||
and hasattr(context.node, "node_type")
|
||||
hasattr(context, 'node')
|
||||
and hasattr(context.node, 'node_type')
|
||||
and context.node.node_type == ct.NodeType.Tidy3DWebExporter
|
||||
|
||||
and tdcloud.IS_AUTHENTICATED
|
||||
and context.node.tracked_task_id
|
||||
)
|
||||
|
@ -113,142 +116,145 @@ class EstCostTrackedTask(bpy.types.Operator):
|
|||
def execute(self, context):
|
||||
node = context.node
|
||||
if (
|
||||
task_info := tdcloud.TidyCloudTasks.task_info(context.node.tracked_task_id)
|
||||
task_info := tdcloud.TidyCloudTasks.task_info(
|
||||
context.node.tracked_task_id
|
||||
)
|
||||
) is None:
|
||||
msg = "Tried to estimate cost of tracked task, but it doesn't exist"
|
||||
msg = (
|
||||
"Tried to estimate cost of tracked task, but it doesn't exist"
|
||||
)
|
||||
raise RuntimeError(msg)
|
||||
|
||||
|
||||
node.cache_est_cost = task_info.cost_est()
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class ReleaseTrackedTask(bpy.types.Operator):
|
||||
bl_idname = "blender_maxwell.nodes__release_tracked_task"
|
||||
bl_label = "Release Tracked Tidy3D Cloud Task"
|
||||
bl_description = "Release the currently tracked simulation task"
|
||||
bl_idname = 'blender_maxwell.nodes__release_tracked_task'
|
||||
bl_label = 'Release Tracked Tidy3D Cloud Task'
|
||||
bl_description = 'Release the currently tracked simulation task'
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return (
|
||||
hasattr(context, "node")
|
||||
and hasattr(context.node, "node_type")
|
||||
hasattr(context, 'node')
|
||||
and hasattr(context.node, 'node_type')
|
||||
and context.node.node_type == ct.NodeType.Tidy3DWebExporter
|
||||
|
||||
#and tdcloud.IS_AUTHENTICATED
|
||||
# and tdcloud.IS_AUTHENTICATED
|
||||
and context.node.tracked_task_id
|
||||
)
|
||||
|
||||
def execute(self, context):
|
||||
node = context.node
|
||||
node.tracked_task_id = ""
|
||||
node.tracked_task_id = ''
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
|
||||
####################
|
||||
# - Node
|
||||
####################
|
||||
class Tidy3DWebExporterNode(base.MaxwellSimNode):
|
||||
node_type = ct.NodeType.Tidy3DWebExporter
|
||||
bl_label = "Tidy3D Web Exporter"
|
||||
|
||||
bl_label = 'Tidy3D Web Exporter'
|
||||
|
||||
input_sockets = {
|
||||
"FDTD Sim": sockets.MaxwellFDTDSimSocketDef(),
|
||||
"Cloud Task": sockets.Tidy3DCloudTaskSocketDef(
|
||||
'FDTD Sim': sockets.MaxwellFDTDSimSocketDef(),
|
||||
'Cloud Task': sockets.Tidy3DCloudTaskSocketDef(
|
||||
should_exist=False,
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
####################
|
||||
# - Properties
|
||||
####################
|
||||
lock_tree: bpy.props.BoolProperty(
|
||||
name="Whether to lock the attached tree",
|
||||
description="Whether or not to lock the attached tree",
|
||||
name='Whether to lock the attached tree',
|
||||
description='Whether or not to lock the attached tree',
|
||||
default=False,
|
||||
update=lambda self, context: self.sync_lock_tree(context),
|
||||
)
|
||||
tracked_task_id: bpy.props.StringProperty(
|
||||
name="Tracked Task ID",
|
||||
description="The currently tracked task ID",
|
||||
default="",
|
||||
name='Tracked Task ID',
|
||||
description='The currently tracked task ID',
|
||||
default='',
|
||||
update=lambda self, context: self.sync_tracked_task_id(context),
|
||||
)
|
||||
|
||||
|
||||
# Cache
|
||||
cache_total_monitor_data: bpy.props.FloatProperty(
|
||||
name="(Cached) Total Monitor Data",
|
||||
description="Required storage space by all monitors",
|
||||
name='(Cached) Total Monitor Data',
|
||||
description='Required storage space by all monitors',
|
||||
default=0.0,
|
||||
)
|
||||
cache_est_cost: bpy.props.FloatProperty(
|
||||
name="(Cached) Estimated Total Cost",
|
||||
description="Est. Cost in FlexCompute units",
|
||||
name='(Cached) Estimated Total Cost',
|
||||
description='Est. Cost in FlexCompute units',
|
||||
default=-1.0,
|
||||
)
|
||||
|
||||
|
||||
####################
|
||||
# - Sync Methods
|
||||
####################
|
||||
def sync_lock_tree(self, context):
|
||||
if self.lock_tree:
|
||||
self.trigger_action("enable_lock")
|
||||
self.trigger_action('enable_lock')
|
||||
self.locked = False
|
||||
for bl_socket in self.inputs:
|
||||
if bl_socket.name == "FDTD Sim": continue
|
||||
if bl_socket.name == 'FDTD Sim':
|
||||
continue
|
||||
bl_socket.locked = False
|
||||
|
||||
|
||||
else:
|
||||
self.trigger_action("disable_lock")
|
||||
|
||||
self.sync_prop("lock_tree", context)
|
||||
self.trigger_action('disable_lock')
|
||||
|
||||
self.sync_prop('lock_tree', context)
|
||||
|
||||
def sync_tracked_task_id(self, context):
|
||||
# Select Tracked Task
|
||||
if self.tracked_task_id:
|
||||
cloud_task = tdcloud.TidyCloudTasks.task(self.tracked_task_id)
|
||||
task_info = tdcloud.TidyCloudTasks.task_info(self.tracked_task_id)
|
||||
|
||||
|
||||
self.loose_output_sockets = {
|
||||
"Cloud Task": sockets.Tidy3DCloudTaskSocketDef(
|
||||
'Cloud Task': sockets.Tidy3DCloudTaskSocketDef(
|
||||
should_exist=True,
|
||||
),
|
||||
}
|
||||
self.inputs["Cloud Task"].locked = True
|
||||
|
||||
self.inputs['Cloud Task'].locked = True
|
||||
|
||||
# Release Tracked Task
|
||||
else:
|
||||
self.cache_est_cost = -1.0
|
||||
self.loose_output_sockets = {}
|
||||
self.inputs["Cloud Task"].sync_prepare_new_task()
|
||||
self.inputs["Cloud Task"].locked = False
|
||||
|
||||
self.sync_prop("tracked_task_id", context)
|
||||
|
||||
self.inputs['Cloud Task'].sync_prepare_new_task()
|
||||
self.inputs['Cloud Task'].locked = False
|
||||
|
||||
self.sync_prop('tracked_task_id', context)
|
||||
|
||||
####################
|
||||
# - Output Socket Callbacks
|
||||
####################
|
||||
def validate_sim(self):
|
||||
if (sim := self._compute_input("FDTD Sim")) is None:
|
||||
msg = "Tried to validate simulation, but none is attached"
|
||||
if (sim := self._compute_input('FDTD Sim')) is None:
|
||||
msg = 'Tried to validate simulation, but none is attached'
|
||||
raise ValueError(msg)
|
||||
|
||||
sim.validate_pre_upload(source_required = True)
|
||||
|
||||
|
||||
sim.validate_pre_upload(source_required=True)
|
||||
|
||||
def upload_sim(self):
|
||||
if (sim := self._compute_input("FDTD Sim")) is None:
|
||||
msg = "Tried to upload simulation, but none is attached"
|
||||
if (sim := self._compute_input('FDTD Sim')) is None:
|
||||
msg = 'Tried to upload simulation, but none is attached'
|
||||
raise ValueError(msg)
|
||||
|
||||
|
||||
if (
|
||||
(new_task := self._compute_input("Cloud Task")) is None
|
||||
or isinstance(
|
||||
new_task,
|
||||
tdcloud.CloudTask,
|
||||
)
|
||||
new_task := self._compute_input('Cloud Task')
|
||||
) is None or isinstance(
|
||||
new_task,
|
||||
tdcloud.CloudTask,
|
||||
):
|
||||
msg = "Tried to upload simulation to new task, but existing task was selected"
|
||||
msg = 'Tried to upload simulation to new task, but existing task was selected'
|
||||
raise ValueError(msg)
|
||||
|
||||
|
||||
# Create Cloud Task
|
||||
cloud_task = tdcloud.TidyCloudTasks.mk_task(
|
||||
task_name=new_task[0],
|
||||
|
@ -257,25 +263,27 @@ class Tidy3DWebExporterNode(base.MaxwellSimNode):
|
|||
upload_progress_cb=lambda uploaded_bytes: None, ## TODO: Use!
|
||||
verbose=True,
|
||||
)
|
||||
|
||||
|
||||
# Declare to Cloud Task that it Exists Now
|
||||
## This will change the UI to not allow free-text input.
|
||||
## If the socket is linked, this errors.
|
||||
self.inputs["Cloud Task"].sync_created_new_task(cloud_task)
|
||||
|
||||
self.inputs['Cloud Task'].sync_created_new_task(cloud_task)
|
||||
|
||||
# Track the Newly Uploaded Task ID
|
||||
self.tracked_task_id = cloud_task.task_id
|
||||
|
||||
|
||||
def run_tracked_task(self):
|
||||
if (
|
||||
cloud_task := tdcloud.TidyCloudTasks.task(self.tracked_task_id)
|
||||
) is None:
|
||||
msg = "Tried to run tracked task, but it doesn't exist"
|
||||
raise RuntimeError(msg)
|
||||
|
||||
|
||||
cloud_task.submit()
|
||||
tdcloud.TidyCloudTasks.update_task(cloud_task) ## TODO: Check that status is actually immediately updated.
|
||||
|
||||
tdcloud.TidyCloudTasks.update_task(
|
||||
cloud_task
|
||||
) ## TODO: Check that status is actually immediately updated.
|
||||
|
||||
####################
|
||||
# - UI
|
||||
####################
|
||||
|
@ -284,146 +292,156 @@ class Tidy3DWebExporterNode(base.MaxwellSimNode):
|
|||
row = layout.row(align=True)
|
||||
row.operator(
|
||||
UploadSimulation.bl_idname,
|
||||
text="Upload",
|
||||
text='Upload',
|
||||
)
|
||||
tree_lock_icon = "LOCKED" if self.lock_tree else "UNLOCKED"
|
||||
row.prop(self, "lock_tree", toggle=True, icon=tree_lock_icon, text="")
|
||||
|
||||
tree_lock_icon = 'LOCKED' if self.lock_tree else 'UNLOCKED'
|
||||
row.prop(self, 'lock_tree', toggle=True, icon=tree_lock_icon, text='')
|
||||
|
||||
# Row: Run Sim Buttons
|
||||
row = layout.row(align=True)
|
||||
row.operator(
|
||||
RunSimulation.bl_idname,
|
||||
text="Run",
|
||||
text='Run',
|
||||
)
|
||||
if self.tracked_task_id:
|
||||
tree_lock_icon = "LOOP_BACK"
|
||||
tree_lock_icon = 'LOOP_BACK'
|
||||
row.operator(
|
||||
ReleaseTrackedTask.bl_idname,
|
||||
icon="LOOP_BACK",
|
||||
text="",
|
||||
icon='LOOP_BACK',
|
||||
text='',
|
||||
)
|
||||
|
||||
|
||||
def draw_info(self, context, layout):
|
||||
# Connection Info
|
||||
auth_icon = "CHECKBOX_HLT" if tdcloud.IS_AUTHENTICATED else "CHECKBOX_DEHLT"
|
||||
conn_icon = "CHECKBOX_HLT" if tdcloud.IS_ONLINE else "CHECKBOX_DEHLT"
|
||||
|
||||
auth_icon = (
|
||||
'CHECKBOX_HLT' if tdcloud.IS_AUTHENTICATED else 'CHECKBOX_DEHLT'
|
||||
)
|
||||
conn_icon = 'CHECKBOX_HLT' if tdcloud.IS_ONLINE else 'CHECKBOX_DEHLT'
|
||||
|
||||
row = layout.row()
|
||||
row.alignment = "CENTER"
|
||||
row.label(text="Cloud Status")
|
||||
row.alignment = 'CENTER'
|
||||
row.label(text='Cloud Status')
|
||||
box = layout.box()
|
||||
split = box.split(factor=0.85)
|
||||
|
||||
|
||||
## Split: Left Column
|
||||
col = split.column(align=False)
|
||||
col.label(text="Authed")
|
||||
col.label(text="Connected")
|
||||
|
||||
col.label(text='Authed')
|
||||
col.label(text='Connected')
|
||||
|
||||
## Split: Right Column
|
||||
col = split.column(align=False)
|
||||
col.label(icon=auth_icon)
|
||||
col.label(icon=conn_icon)
|
||||
|
||||
|
||||
|
||||
# Simulation Info
|
||||
if self.inputs["FDTD Sim"].is_linked:
|
||||
if self.inputs['FDTD Sim'].is_linked:
|
||||
row = layout.row()
|
||||
row.alignment = "CENTER"
|
||||
row.label(text="Sim Info")
|
||||
row.alignment = 'CENTER'
|
||||
row.label(text='Sim Info')
|
||||
box = layout.box()
|
||||
split = box.split(factor=0.4)
|
||||
|
||||
|
||||
## Split: Left Column
|
||||
col = split.column(align=False)
|
||||
col.label(text="𝝨 Output")
|
||||
|
||||
col.label(text='𝝨 Output')
|
||||
|
||||
## Split: Right Column
|
||||
col = split.column(align=False)
|
||||
col.alignment = "RIGHT"
|
||||
col.label(text=f"{self.cache_total_monitor_data / 1_000_000:.2f}MB")
|
||||
|
||||
|
||||
col.alignment = 'RIGHT'
|
||||
col.label(
|
||||
text=f'{self.cache_total_monitor_data / 1_000_000:.2f}MB'
|
||||
)
|
||||
|
||||
# Cloud Task Info
|
||||
if self.tracked_task_id and tdcloud.IS_AUTHENTICATED:
|
||||
task_info = tdcloud.TidyCloudTasks.task_info(
|
||||
self.tracked_task_id
|
||||
)
|
||||
if task_info is None: return
|
||||
|
||||
task_info = tdcloud.TidyCloudTasks.task_info(self.tracked_task_id)
|
||||
if task_info is None:
|
||||
return
|
||||
|
||||
## Header
|
||||
row = layout.row()
|
||||
row.alignment = "CENTER"
|
||||
row.label(text="Task Info")
|
||||
|
||||
row.alignment = 'CENTER'
|
||||
row.label(text='Task Info')
|
||||
|
||||
## Progress Bar
|
||||
row = layout.row(align=True)
|
||||
row.progress(
|
||||
factor=0.0,
|
||||
type="BAR",
|
||||
text=f"Status: {task_info.status.capitalize()}",
|
||||
type='BAR',
|
||||
text=f'Status: {task_info.status.capitalize()}',
|
||||
)
|
||||
row.operator(
|
||||
ReloadTrackedTask.bl_idname,
|
||||
text="",
|
||||
icon="FILE_REFRESH",
|
||||
text='',
|
||||
icon='FILE_REFRESH',
|
||||
)
|
||||
row.operator(
|
||||
EstCostTrackedTask.bl_idname,
|
||||
text="",
|
||||
icon="SORTTIME",
|
||||
text='',
|
||||
icon='SORTTIME',
|
||||
)
|
||||
|
||||
|
||||
## Information
|
||||
box = layout.box()
|
||||
split = box.split(factor=0.4)
|
||||
|
||||
|
||||
## Split: Left Column
|
||||
col = split.column(align=False)
|
||||
col.label(text="Status")
|
||||
col.label(text="Est. Cost")
|
||||
col.label(text="Real Cost")
|
||||
|
||||
col.label(text='Status')
|
||||
col.label(text='Est. Cost')
|
||||
col.label(text='Real Cost')
|
||||
|
||||
## Split: Right Column
|
||||
cost_est = f"{self.cache_est_cost:.2f}" if self.cache_est_cost >= 0 else "TBD"
|
||||
cost_real = f"{task_info.cost_real:.2f}" if task_info.cost_real is not None else "TBD"
|
||||
|
||||
cost_est = (
|
||||
f'{self.cache_est_cost:.2f}'
|
||||
if self.cache_est_cost >= 0
|
||||
else 'TBD'
|
||||
)
|
||||
cost_real = (
|
||||
f'{task_info.cost_real:.2f}'
|
||||
if task_info.cost_real is not None
|
||||
else 'TBD'
|
||||
)
|
||||
|
||||
col = split.column(align=False)
|
||||
col.alignment = "RIGHT"
|
||||
col.alignment = 'RIGHT'
|
||||
col.label(text=task_info.status.capitalize())
|
||||
col.label(text=f"{cost_est} creds")
|
||||
col.label(text=f"{cost_real} creds")
|
||||
|
||||
col.label(text=f'{cost_est} creds')
|
||||
col.label(text=f'{cost_real} creds')
|
||||
|
||||
# Connection Information
|
||||
|
||||
|
||||
####################
|
||||
# - Output Methods
|
||||
####################
|
||||
@base.computes_output_socket(
|
||||
"Cloud Task",
|
||||
input_sockets={"Cloud Task"},
|
||||
'Cloud Task',
|
||||
input_sockets={'Cloud Task'},
|
||||
)
|
||||
def compute_cloud_task(self, input_sockets: dict) -> tdcloud.CloudTask | None:
|
||||
def compute_cloud_task(
|
||||
self, input_sockets: dict
|
||||
) -> tdcloud.CloudTask | None:
|
||||
if isinstance(
|
||||
cloud_task := input_sockets["Cloud Task"],
|
||||
tdcloud.CloudTask
|
||||
cloud_task := input_sockets['Cloud Task'], tdcloud.CloudTask
|
||||
):
|
||||
return cloud_task
|
||||
|
||||
|
||||
return None
|
||||
|
||||
|
||||
####################
|
||||
# - Output Methods
|
||||
####################
|
||||
@base.on_value_changed(
|
||||
socket_name="FDTD Sim",
|
||||
input_sockets={"FDTD Sim"},
|
||||
socket_name='FDTD Sim',
|
||||
input_sockets={'FDTD Sim'},
|
||||
)
|
||||
def on_value_changed__fdtd_sim(self, input_sockets):
|
||||
if (sim := self._compute_input("FDTD Sim")) is None:
|
||||
if (sim := self._compute_input('FDTD Sim')) is None:
|
||||
self.cache_total_monitor_data = 0
|
||||
return
|
||||
|
||||
sim.validate_pre_upload(source_required = True)
|
||||
|
||||
sim.validate_pre_upload(source_required=True)
|
||||
self.cache_total_monitor_data = sum(sim.monitors_data_size.values())
|
||||
|
||||
|
||||
|
|
|
@ -15,8 +15,8 @@ from ...managed_objs import managed_bl_object
|
|||
|
||||
|
||||
class ConsoleViewOperator(bpy.types.Operator):
|
||||
bl_idname = "blender_maxwell.console_view_operator"
|
||||
bl_label = "View Plots"
|
||||
bl_idname = 'blender_maxwell.console_view_operator'
|
||||
bl_label = 'View Plots'
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
|
@ -27,9 +27,10 @@ class ConsoleViewOperator(bpy.types.Operator):
|
|||
node.print_data_to_console()
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class RefreshPlotViewOperator(bpy.types.Operator):
|
||||
bl_idname = "blender_maxwell.refresh_plot_view_operator"
|
||||
bl_label = "Refresh Plots"
|
||||
bl_idname = 'blender_maxwell.refresh_plot_view_operator'
|
||||
bl_label = 'Refresh Plots'
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
|
@ -37,93 +38,96 @@ class RefreshPlotViewOperator(bpy.types.Operator):
|
|||
|
||||
def execute(self, context):
|
||||
node = context.node
|
||||
node.trigger_action("value_changed", "Data")
|
||||
node.trigger_action('value_changed', 'Data')
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
####################
|
||||
# - Node
|
||||
####################
|
||||
class ViewerNode(base.MaxwellSimNode):
|
||||
node_type = ct.NodeType.Viewer
|
||||
bl_label = "Viewer"
|
||||
|
||||
bl_label = 'Viewer'
|
||||
|
||||
input_sockets = {
|
||||
"Data": sockets.AnySocketDef(),
|
||||
'Data': sockets.AnySocketDef(),
|
||||
}
|
||||
|
||||
|
||||
####################
|
||||
# - Properties
|
||||
####################
|
||||
auto_plot: bpy.props.BoolProperty(
|
||||
name="Auto-Plot",
|
||||
description="Whether to auto-plot anything plugged into the viewer node",
|
||||
name='Auto-Plot',
|
||||
description='Whether to auto-plot anything plugged into the viewer node',
|
||||
default=False,
|
||||
update=lambda self, context: self.sync_prop("auto_plot", context),
|
||||
update=lambda self, context: self.sync_prop('auto_plot', context),
|
||||
)
|
||||
|
||||
|
||||
auto_3d_preview: bpy.props.BoolProperty(
|
||||
name="Auto 3D Preview",
|
||||
name='Auto 3D Preview',
|
||||
description="Whether to auto-preview anything 3D, that's plugged into the viewer node",
|
||||
default=False,
|
||||
update=lambda self, context: self.sync_prop("auto_3d_preview", context),
|
||||
update=lambda self, context: self.sync_prop(
|
||||
'auto_3d_preview', context
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
####################
|
||||
# - UI
|
||||
####################
|
||||
def draw_operators(self, context, layout):
|
||||
split = layout.split(factor=0.4)
|
||||
|
||||
|
||||
# Split LHS
|
||||
col = split.column(align=False)
|
||||
col.label(text="Console")
|
||||
col.label(text="Plot")
|
||||
col.label(text="3D")
|
||||
|
||||
col.label(text='Console')
|
||||
col.label(text='Plot')
|
||||
col.label(text='3D')
|
||||
|
||||
# Split RHS
|
||||
col = split.column(align=False)
|
||||
|
||||
|
||||
## Console Options
|
||||
col.operator(ConsoleViewOperator.bl_idname, text="Print")
|
||||
|
||||
col.operator(ConsoleViewOperator.bl_idname, text='Print')
|
||||
|
||||
## Plot Options
|
||||
row = col.row(align=True)
|
||||
row.prop(self, "auto_plot", text="Plot", toggle=True)
|
||||
row.prop(self, 'auto_plot', text='Plot', toggle=True)
|
||||
row.operator(
|
||||
RefreshPlotViewOperator.bl_idname,
|
||||
text="",
|
||||
icon="FILE_REFRESH",
|
||||
text='',
|
||||
icon='FILE_REFRESH',
|
||||
)
|
||||
|
||||
|
||||
## 3D Preview Options
|
||||
row = col.row(align=True)
|
||||
row.prop(self, "auto_3d_preview", text="3D Preview", toggle=True)
|
||||
|
||||
row.prop(self, 'auto_3d_preview', text='3D Preview', toggle=True)
|
||||
|
||||
####################
|
||||
# - Methods
|
||||
####################
|
||||
def print_data_to_console(self):
|
||||
if not (data := self._compute_input("Data")):
|
||||
if not (data := self._compute_input('Data')):
|
||||
return
|
||||
|
||||
|
||||
if isinstance(data, sp.Basic):
|
||||
sp.pprint(data, use_unicode=True)
|
||||
|
||||
|
||||
print(str(data))
|
||||
|
||||
|
||||
####################
|
||||
# - Updates
|
||||
####################
|
||||
@base.on_value_changed(
|
||||
socket_name="Data",
|
||||
props={"auto_3d_preview"},
|
||||
socket_name='Data',
|
||||
props={'auto_3d_preview'},
|
||||
)
|
||||
def on_value_changed__data(self, props):
|
||||
# Show Plot
|
||||
## Don't have to un-show other plots.
|
||||
if self.auto_plot:
|
||||
self.trigger_action("show_plot")
|
||||
|
||||
self.trigger_action('show_plot')
|
||||
|
||||
# Remove Anything Previewed
|
||||
preview_collection = managed_bl_object.bl_collection(
|
||||
managed_bl_object.PREVIEW_COLLECTION_NAME,
|
||||
|
@ -131,14 +135,14 @@ class ViewerNode(base.MaxwellSimNode):
|
|||
)
|
||||
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")
|
||||
|
||||
if props['auto_3d_preview']:
|
||||
self.trigger_action('show_preview')
|
||||
|
||||
@base.on_value_changed(
|
||||
prop_name="auto_3d_preview",
|
||||
props={"auto_3d_preview"},
|
||||
prop_name='auto_3d_preview',
|
||||
props={'auto_3d_preview'},
|
||||
)
|
||||
def on_value_changed__auto_3d_preview(self, props):
|
||||
# Remove Anything Previewed
|
||||
|
@ -148,10 +152,10 @@ class ViewerNode(base.MaxwellSimNode):
|
|||
)
|
||||
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")
|
||||
if props['auto_3d_preview']:
|
||||
self.trigger_action('show_preview')
|
||||
|
||||
|
||||
####################
|
||||
|
@ -162,8 +166,4 @@ BL_REGISTER = [
|
|||
RefreshPlotViewOperator,
|
||||
ViewerNode,
|
||||
]
|
||||
BL_NODES = {
|
||||
ct.NodeType.Viewer: (
|
||||
ct.NodeCategory.MAXWELLSIM_OUTPUTS
|
||||
)
|
||||
}
|
||||
BL_NODES = {ct.NodeType.Viewer: (ct.NodeCategory.MAXWELLSIM_OUTPUTS)}
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
from . import sim_domain
|
||||
|
||||
#from . import sim_grid
|
||||
#from . import sim_grid_axes
|
||||
# from . import sim_grid
|
||||
# from . import sim_grid_axes
|
||||
|
||||
from . import fdtd_sim
|
||||
|
||||
BL_REGISTER = [
|
||||
*sim_domain.BL_REGISTER,
|
||||
# *sim_grid.BL_REGISTER,
|
||||
# *sim_grid_axes.BL_REGISTER,
|
||||
# *sim_grid.BL_REGISTER,
|
||||
# *sim_grid_axes.BL_REGISTER,
|
||||
*fdtd_sim.BL_REGISTER,
|
||||
]
|
||||
BL_NODES = {
|
||||
**sim_domain.BL_NODES,
|
||||
# **sim_grid.BL_NODES,
|
||||
# **sim_grid_axes.BL_NODES,
|
||||
# **sim_grid.BL_NODES,
|
||||
# **sim_grid_axes.BL_NODES,
|
||||
**fdtd_sim.BL_NODES,
|
||||
}
|
||||
|
|
|
@ -6,54 +6,53 @@ from ... import contracts as ct
|
|||
from ... import sockets
|
||||
from .. import base
|
||||
|
||||
|
||||
class FDTDSimNode(base.MaxwellSimNode):
|
||||
node_type = ct.NodeType.FDTDSim
|
||||
bl_label = "FDTD Simulation"
|
||||
|
||||
bl_label = 'FDTD Simulation'
|
||||
|
||||
####################
|
||||
# - Sockets
|
||||
####################
|
||||
input_sockets = {
|
||||
"Domain": sockets.MaxwellSimDomainSocketDef(),
|
||||
"BCs": sockets.MaxwellBoundCondsSocketDef(),
|
||||
"Sources": sockets.MaxwellSourceSocketDef(
|
||||
'Domain': sockets.MaxwellSimDomainSocketDef(),
|
||||
'BCs': sockets.MaxwellBoundCondsSocketDef(),
|
||||
'Sources': sockets.MaxwellSourceSocketDef(
|
||||
is_list=True,
|
||||
),
|
||||
"Structures": sockets.MaxwellStructureSocketDef(
|
||||
'Structures': sockets.MaxwellStructureSocketDef(
|
||||
is_list=True,
|
||||
),
|
||||
"Monitors": sockets.MaxwellMonitorSocketDef(
|
||||
'Monitors': sockets.MaxwellMonitorSocketDef(
|
||||
is_list=True,
|
||||
),
|
||||
}
|
||||
output_sockets = {
|
||||
"FDTD Sim": sockets.MaxwellFDTDSimSocketDef(),
|
||||
'FDTD Sim': sockets.MaxwellFDTDSimSocketDef(),
|
||||
}
|
||||
|
||||
|
||||
####################
|
||||
# - Output Socket Computation
|
||||
####################
|
||||
@base.computes_output_socket(
|
||||
"FDTD Sim",
|
||||
'FDTD Sim',
|
||||
kind=ct.DataFlowKind.Value,
|
||||
input_sockets={
|
||||
"Sources", "Structures", "Domain", "BCs", "Monitors"
|
||||
},
|
||||
input_sockets={'Sources', 'Structures', 'Domain', 'BCs', 'Monitors'},
|
||||
)
|
||||
def compute_fdtd_sim(self, input_sockets: dict) -> sp.Expr:
|
||||
sim_domain = input_sockets["Domain"]
|
||||
sources = input_sockets["Sources"]
|
||||
structures = input_sockets["Structures"]
|
||||
bounds = input_sockets["BCs"]
|
||||
monitors = input_sockets["Monitors"]
|
||||
|
||||
#if not isinstance(sources, list):
|
||||
# sources = [sources]
|
||||
#if not isinstance(structures, list):
|
||||
# structures = [structures]
|
||||
#if not isinstance(monitors, list):
|
||||
# monitors = [monitors]
|
||||
|
||||
sim_domain = input_sockets['Domain']
|
||||
sources = input_sockets['Sources']
|
||||
structures = input_sockets['Structures']
|
||||
bounds = input_sockets['BCs']
|
||||
monitors = input_sockets['Monitors']
|
||||
|
||||
# if not isinstance(sources, list):
|
||||
# sources = [sources]
|
||||
# if not isinstance(structures, list):
|
||||
# structures = [structures]
|
||||
# if not isinstance(monitors, list):
|
||||
# monitors = [monitors]
|
||||
|
||||
return td.Simulation(
|
||||
**sim_domain, ## run_time=, size=, grid=, medium=
|
||||
structures=structures,
|
||||
|
@ -62,14 +61,11 @@ class FDTDSimNode(base.MaxwellSimNode):
|
|||
boundary_spec=bounds,
|
||||
)
|
||||
|
||||
|
||||
####################
|
||||
# - Blender Registration
|
||||
####################
|
||||
BL_REGISTER = [
|
||||
FDTDSimNode,
|
||||
]
|
||||
BL_NODES = {
|
||||
ct.NodeType.FDTDSim: (
|
||||
ct.NodeCategory.MAXWELLSIM_SIMS
|
||||
)
|
||||
}
|
||||
BL_NODES = {ct.NodeType.FDTDSim: (ct.NodeCategory.MAXWELLSIM_SIMS)}
|
||||
|
|
|
@ -9,48 +9,51 @@ from ... import sockets
|
|||
from .. import base
|
||||
from ... import managed_objs
|
||||
|
||||
GEONODES_DOMAIN_BOX = "simdomain_box"
|
||||
GEONODES_DOMAIN_BOX = 'simdomain_box'
|
||||
|
||||
|
||||
class SimDomainNode(base.MaxwellSimNode):
|
||||
node_type = ct.NodeType.SimDomain
|
||||
bl_label = "Sim Domain"
|
||||
|
||||
bl_label = 'Sim Domain'
|
||||
|
||||
input_sockets = {
|
||||
"Duration": sockets.PhysicalTimeSocketDef(
|
||||
default_value = 5 * spu.ps,
|
||||
default_unit = spu.ps,
|
||||
'Duration': sockets.PhysicalTimeSocketDef(
|
||||
default_value=5 * spu.ps,
|
||||
default_unit=spu.ps,
|
||||
),
|
||||
"Center": sockets.PhysicalSize3DSocketDef(),
|
||||
"Size": sockets.PhysicalSize3DSocketDef(),
|
||||
"Grid": sockets.MaxwellSimGridSocketDef(),
|
||||
"Ambient Medium": sockets.MaxwellMediumSocketDef(),
|
||||
'Center': sockets.PhysicalSize3DSocketDef(),
|
||||
'Size': sockets.PhysicalSize3DSocketDef(),
|
||||
'Grid': sockets.MaxwellSimGridSocketDef(),
|
||||
'Ambient Medium': sockets.MaxwellMediumSocketDef(),
|
||||
}
|
||||
output_sockets = {
|
||||
"Domain": sockets.MaxwellSimDomainSocketDef(),
|
||||
'Domain': sockets.MaxwellSimDomainSocketDef(),
|
||||
}
|
||||
|
||||
|
||||
managed_obj_defs = {
|
||||
"domain_box": ct.schemas.ManagedObjDef(
|
||||
'domain_box': ct.schemas.ManagedObjDef(
|
||||
mk=lambda name: managed_objs.ManagedBLObject(name),
|
||||
name_prefix="",
|
||||
name_prefix='',
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
####################
|
||||
# - Callbacks
|
||||
####################
|
||||
@base.computes_output_socket(
|
||||
"Domain",
|
||||
input_sockets={"Duration", "Center", "Size", "Grid", "Ambient Medium"},
|
||||
'Domain',
|
||||
input_sockets={'Duration', 'Center', 'Size', 'Grid', 'Ambient Medium'},
|
||||
)
|
||||
def compute_sim_domain(self, input_sockets: dict) -> sp.Expr:
|
||||
if all([
|
||||
(_duration := input_sockets["Duration"]),
|
||||
(_center := input_sockets["Center"]),
|
||||
(_size := input_sockets["Size"]),
|
||||
(grid := input_sockets["Grid"]),
|
||||
(medium := input_sockets["Ambient Medium"]),
|
||||
]):
|
||||
if all(
|
||||
[
|
||||
(_duration := input_sockets['Duration']),
|
||||
(_center := input_sockets['Center']),
|
||||
(_size := input_sockets['Size']),
|
||||
(grid := input_sockets['Grid']),
|
||||
(medium := input_sockets['Ambient Medium']),
|
||||
]
|
||||
):
|
||||
duration = spu.convert_to(_duration, spu.second) / spu.second
|
||||
center = tuple(spu.convert_to(_center, spu.um) / spu.um)
|
||||
size = tuple(spu.convert_to(_size, spu.um) / spu.um)
|
||||
|
@ -61,72 +64,67 @@ class SimDomainNode(base.MaxwellSimNode):
|
|||
grid_spec=grid,
|
||||
medium=medium,
|
||||
)
|
||||
|
||||
|
||||
####################
|
||||
# - Preview
|
||||
####################
|
||||
@base.on_value_changed(
|
||||
socket_name={"Center", "Size"},
|
||||
input_sockets={"Center", "Size"},
|
||||
managed_objs={"domain_box"},
|
||||
socket_name={'Center', 'Size'},
|
||||
input_sockets={'Center', 'Size'},
|
||||
managed_objs={'domain_box'},
|
||||
)
|
||||
def on_value_changed__center_size(
|
||||
self,
|
||||
input_sockets: dict,
|
||||
managed_objs: dict[str, ct.schemas.ManagedObj],
|
||||
):
|
||||
_center = input_sockets["Center"]
|
||||
center = tuple([
|
||||
float(el)
|
||||
for el in spu.convert_to(_center, spu.um) / spu.um
|
||||
])
|
||||
|
||||
_size = input_sockets["Size"]
|
||||
size = tuple([
|
||||
float(el)
|
||||
for el in spu.convert_to(_size, spu.um) / spu.um
|
||||
])
|
||||
_center = input_sockets['Center']
|
||||
center = tuple(
|
||||
[float(el) for el in spu.convert_to(_center, spu.um) / spu.um]
|
||||
)
|
||||
|
||||
_size = input_sockets['Size']
|
||||
size = tuple(
|
||||
[float(el) for el in spu.convert_to(_size, spu.um) / spu.um]
|
||||
)
|
||||
## TODO: Preview unit system?? Presume um for now
|
||||
|
||||
|
||||
# Retrieve Hard-Coded GeoNodes and Analyze Input
|
||||
geo_nodes = bpy.data.node_groups[GEONODES_DOMAIN_BOX]
|
||||
geonodes_interface = analyze_geonodes.interface(
|
||||
geo_nodes, direc="INPUT"
|
||||
geo_nodes, direc='INPUT'
|
||||
)
|
||||
|
||||
|
||||
# Sync Modifier Inputs
|
||||
managed_objs["domain_box"].sync_geonodes_modifier(
|
||||
managed_objs['domain_box'].sync_geonodes_modifier(
|
||||
geonodes_node_group=geo_nodes,
|
||||
geonodes_identifier_to_value={
|
||||
geonodes_interface["Size"].identifier: size,
|
||||
geonodes_interface['Size'].identifier: size,
|
||||
## TODO: Use 'bl_socket_map.value_to_bl`!
|
||||
## - This accounts for auto-conversion, unit systems, etc. .
|
||||
## - We could keep it in the node base class...
|
||||
## - ...But it needs aligning with Blender, too. Hmm.
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
# Sync Object Position
|
||||
managed_objs["domain_box"].bl_object("MESH").location = center
|
||||
|
||||
managed_objs['domain_box'].bl_object('MESH').location = center
|
||||
|
||||
@base.on_show_preview(
|
||||
managed_objs={"domain_box"},
|
||||
managed_objs={'domain_box'},
|
||||
)
|
||||
def on_show_preview(
|
||||
self,
|
||||
managed_objs: dict[str, ct.schemas.ManagedObj],
|
||||
):
|
||||
managed_objs["domain_box"].show_preview("MESH")
|
||||
managed_objs['domain_box'].show_preview('MESH')
|
||||
self.on_value_changed__center_size()
|
||||
|
||||
|
||||
####################
|
||||
# - Blender Registration
|
||||
####################
|
||||
BL_REGISTER = [
|
||||
SimDomainNode,
|
||||
]
|
||||
BL_NODES = {
|
||||
ct.NodeType.SimDomain: (
|
||||
ct.NodeCategory.MAXWELLSIM_SIMS
|
||||
)
|
||||
}
|
||||
BL_NODES = {ct.NodeType.SimDomain: (ct.NodeCategory.MAXWELLSIM_SIMS)}
|
||||
|
|
|
@ -3,4 +3,3 @@
|
|||
####################
|
||||
BL_REGISTER = []
|
||||
BL_NODES = {}
|
||||
|
||||
|
|
|
@ -3,4 +3,3 @@
|
|||
####################
|
||||
BL_REGISTER = []
|
||||
BL_NODES = {}
|
||||
|
||||
|
|
|
@ -3,4 +3,3 @@
|
|||
####################
|
||||
BL_REGISTER = []
|
||||
BL_NODES = {}
|
||||
|
||||
|
|
|
@ -3,4 +3,3 @@
|
|||
####################
|
||||
BL_REGISTER = []
|
||||
BL_NODES = {}
|
||||
|
||||
|
|
|
@ -1,27 +1,28 @@
|
|||
from . import temporal_shapes
|
||||
|
||||
from . import point_dipole_source
|
||||
#from . import uniform_current_source
|
||||
|
||||
# from . import uniform_current_source
|
||||
from . import plane_wave_source
|
||||
#from . import gaussian_beam_source
|
||||
#from . import astigmatic_gaussian_beam_source
|
||||
#from . import tfsf_source
|
||||
# from . import gaussian_beam_source
|
||||
# from . import astigmatic_gaussian_beam_source
|
||||
# from . import tfsf_source
|
||||
|
||||
BL_REGISTER = [
|
||||
*temporal_shapes.BL_REGISTER,
|
||||
*point_dipole_source.BL_REGISTER,
|
||||
# *uniform_current_source.BL_REGISTER,
|
||||
# *uniform_current_source.BL_REGISTER,
|
||||
*plane_wave_source.BL_REGISTER,
|
||||
# *gaussian_beam_source.BL_REGISTER,
|
||||
# *astigmatic_gaussian_beam_source.BL_REGISTER,
|
||||
# *tfsf_source.BL_REGISTER,
|
||||
# *gaussian_beam_source.BL_REGISTER,
|
||||
# *astigmatic_gaussian_beam_source.BL_REGISTER,
|
||||
# *tfsf_source.BL_REGISTER,
|
||||
]
|
||||
BL_NODES = {
|
||||
**temporal_shapes.BL_NODES,
|
||||
**point_dipole_source.BL_NODES,
|
||||
# **uniform_current_source.BL_NODES,
|
||||
# **uniform_current_source.BL_NODES,
|
||||
**plane_wave_source.BL_NODES,
|
||||
# **gaussian_beam_source.BL_NODES,
|
||||
# **astigmatic_gaussian_beam_source.BL_NODES,
|
||||
# **tfsf_source.BL_NODES,
|
||||
# **gaussian_beam_source.BL_NODES,
|
||||
# **astigmatic_gaussian_beam_source.BL_NODES,
|
||||
# **tfsf_source.BL_NODES,
|
||||
}
|
||||
|
|
|
@ -3,4 +3,3 @@
|
|||
####################
|
||||
BL_REGISTER = []
|
||||
BL_NODES = {}
|
||||
|
||||
|
|
|
@ -3,4 +3,3 @@
|
|||
####################
|
||||
BL_REGISTER = []
|
||||
BL_NODES = {}
|
||||
|
||||
|
|
|
@ -3,4 +3,3 @@
|
|||
####################
|
||||
BL_REGISTER = []
|
||||
BL_NODES = {}
|
||||
|
||||
|
|
|
@ -3,4 +3,3 @@
|
|||
####################
|
||||
BL_REGISTER = []
|
||||
BL_NODES = {}
|
||||
|
||||
|
|
|
@ -13,88 +13,89 @@ from ... import contracts as ct
|
|||
from ... import sockets
|
||||
from .. import base
|
||||
|
||||
GEONODES_PLANE_WAVE = "source_plane_wave"
|
||||
GEONODES_PLANE_WAVE = 'source_plane_wave'
|
||||
|
||||
|
||||
def convert_vector_to_spherical(
|
||||
v: sp.MatrixBase,
|
||||
) -> tuple[str, str, sp.Expr, sp.Expr]:
|
||||
"""Converts a vector (maybe normalized) to spherical coordinates from an arbitrary choice of injection axis.
|
||||
|
||||
|
||||
Injection axis is chosen to minimize `theta`
|
||||
"""
|
||||
x, y, z = v
|
||||
|
||||
|
||||
injection_axis = max(
|
||||
('x', abs(x)),
|
||||
('y', abs(y)),
|
||||
('z', abs(z)),
|
||||
key=lambda item: item[1]
|
||||
('x', abs(x)), ('y', abs(y)), ('z', abs(z)), key=lambda item: item[1]
|
||||
)[0]
|
||||
## Select injection axis that minimizes 'theta'
|
||||
|
||||
if injection_axis == "x":
|
||||
direction = "+" if x >= 0 else "-"
|
||||
|
||||
if injection_axis == 'x':
|
||||
direction = '+' if x >= 0 else '-'
|
||||
theta = sp.acos(x / sp.sqrt(x**2 + y**2 + z**2))
|
||||
phi = sp.atan2(z, y)
|
||||
elif injection_axis == "y":
|
||||
direction = "+" if y >= 0 else "-"
|
||||
elif injection_axis == 'y':
|
||||
direction = '+' if y >= 0 else '-'
|
||||
theta = sp.acos(y / sp.sqrt(x**2 + y**2 + z**2))
|
||||
phi = sp.atan2(x, z)
|
||||
else:
|
||||
direction = "+" if z >= 0 else "-"
|
||||
direction = '+' if z >= 0 else '-'
|
||||
theta = sp.acos(z / sp.sqrt(x**2 + y**2 + z**2))
|
||||
phi = sp.atan2(y, x)
|
||||
|
||||
|
||||
return injection_axis, direction, theta, phi
|
||||
|
||||
|
||||
class PlaneWaveSourceNode(base.MaxwellSimNode):
|
||||
node_type = ct.NodeType.PlaneWaveSource
|
||||
bl_label = "Plane Wave Source"
|
||||
|
||||
bl_label = 'Plane Wave Source'
|
||||
|
||||
####################
|
||||
# - Sockets
|
||||
####################
|
||||
input_sockets = {
|
||||
"Temporal Shape": sockets.MaxwellTemporalShapeSocketDef(),
|
||||
"Center": sockets.PhysicalPoint3DSocketDef(),
|
||||
"Direction": sockets.Real3DVectorSocketDef(
|
||||
'Temporal Shape': sockets.MaxwellTemporalShapeSocketDef(),
|
||||
'Center': sockets.PhysicalPoint3DSocketDef(),
|
||||
'Direction': sockets.Real3DVectorSocketDef(
|
||||
default_value=sp.Matrix([0, 0, -1])
|
||||
),
|
||||
"Pol Angle": sockets.PhysicalAngleSocketDef(),
|
||||
'Pol Angle': sockets.PhysicalAngleSocketDef(),
|
||||
}
|
||||
output_sockets = {
|
||||
"Source": sockets.MaxwellSourceSocketDef(),
|
||||
'Source': sockets.MaxwellSourceSocketDef(),
|
||||
}
|
||||
|
||||
|
||||
managed_obj_defs = {
|
||||
"plane_wave_source": ct.schemas.ManagedObjDef(
|
||||
'plane_wave_source': ct.schemas.ManagedObjDef(
|
||||
mk=lambda name: managed_objs.ManagedBLObject(name),
|
||||
name_prefix="",
|
||||
name_prefix='',
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
####################
|
||||
# - Output Socket Computation
|
||||
####################
|
||||
@base.computes_output_socket(
|
||||
"Source",
|
||||
input_sockets={"Temporal Shape", "Center", "Direction", "Pol Angle"},
|
||||
'Source',
|
||||
input_sockets={'Temporal Shape', 'Center', 'Direction', 'Pol Angle'},
|
||||
)
|
||||
def compute_source(self, input_sockets: dict):
|
||||
temporal_shape = input_sockets["Temporal Shape"]
|
||||
_center = input_sockets["Center"]
|
||||
direction = input_sockets["Direction"]
|
||||
pol_angle = input_sockets["Pol Angle"]
|
||||
|
||||
injection_axis, dir_sgn, theta, phi = convert_vector_to_spherical(direction)
|
||||
|
||||
temporal_shape = input_sockets['Temporal Shape']
|
||||
_center = input_sockets['Center']
|
||||
direction = input_sockets['Direction']
|
||||
pol_angle = input_sockets['Pol Angle']
|
||||
|
||||
injection_axis, dir_sgn, theta, phi = convert_vector_to_spherical(
|
||||
direction
|
||||
)
|
||||
|
||||
size = {
|
||||
"x": (0, math.inf, math.inf),
|
||||
"y": (math.inf, 0, math.inf),
|
||||
"z": (math.inf, math.inf, 0),
|
||||
'x': (0, math.inf, math.inf),
|
||||
'y': (math.inf, 0, math.inf),
|
||||
'z': (math.inf, math.inf, 0),
|
||||
}[injection_axis]
|
||||
center = tuple(spu.convert_to(_center, spu.um) / spu.um)
|
||||
|
||||
|
||||
# Display the results
|
||||
return td.PlaneWave(
|
||||
center=center,
|
||||
|
@ -105,74 +106,65 @@ class PlaneWaveSourceNode(base.MaxwellSimNode):
|
|||
angle_phi=phi,
|
||||
pol_angle=pol_angle,
|
||||
)
|
||||
|
||||
|
||||
####################
|
||||
# - Preview
|
||||
####################
|
||||
@base.on_value_changed(
|
||||
socket_name={"Center", "Direction"},
|
||||
input_sockets={"Center", "Direction"},
|
||||
managed_objs={"plane_wave_source"},
|
||||
socket_name={'Center', 'Direction'},
|
||||
input_sockets={'Center', 'Direction'},
|
||||
managed_objs={'plane_wave_source'},
|
||||
)
|
||||
def on_value_changed__center_direction(
|
||||
self,
|
||||
input_sockets: dict,
|
||||
managed_objs: dict[str, ct.schemas.ManagedObj],
|
||||
):
|
||||
_center = input_sockets["Center"]
|
||||
center = tuple([
|
||||
float(el)
|
||||
for el in spu.convert_to(_center, spu.um) / spu.um
|
||||
])
|
||||
|
||||
_direction = input_sockets["Direction"]
|
||||
direction = tuple([
|
||||
float(el)
|
||||
for el in _direction
|
||||
])
|
||||
_center = input_sockets['Center']
|
||||
center = tuple(
|
||||
[float(el) for el in spu.convert_to(_center, spu.um) / spu.um]
|
||||
)
|
||||
|
||||
_direction = input_sockets['Direction']
|
||||
direction = tuple([float(el) for el in _direction])
|
||||
## TODO: Preview unit system?? Presume um for now
|
||||
|
||||
|
||||
# Retrieve Hard-Coded GeoNodes and Analyze Input
|
||||
geo_nodes = bpy.data.node_groups[GEONODES_PLANE_WAVE]
|
||||
geonodes_interface = analyze_geonodes.interface(
|
||||
geo_nodes, direc="INPUT"
|
||||
geo_nodes, direc='INPUT'
|
||||
)
|
||||
|
||||
|
||||
# Sync Modifier Inputs
|
||||
managed_objs["plane_wave_source"].sync_geonodes_modifier(
|
||||
managed_objs['plane_wave_source'].sync_geonodes_modifier(
|
||||
geonodes_node_group=geo_nodes,
|
||||
geonodes_identifier_to_value={
|
||||
geonodes_interface["Direction"].identifier: direction,
|
||||
geonodes_interface['Direction'].identifier: direction,
|
||||
## TODO: Use 'bl_socket_map.value_to_bl`!
|
||||
## - This accounts for auto-conversion, unit systems, etc. .
|
||||
## - We could keep it in the node base class...
|
||||
## - ...But it needs aligning with Blender, too. Hmm.
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
# Sync Object Position
|
||||
managed_objs["plane_wave_source"].bl_object("MESH").location = center
|
||||
|
||||
managed_objs['plane_wave_source'].bl_object('MESH').location = center
|
||||
|
||||
@base.on_show_preview(
|
||||
managed_objs={"plane_wave_source"},
|
||||
managed_objs={'plane_wave_source'},
|
||||
)
|
||||
def on_show_preview(
|
||||
self,
|
||||
managed_objs: dict[str, ct.schemas.ManagedObj],
|
||||
):
|
||||
managed_objs["plane_wave_source"].show_preview("MESH")
|
||||
managed_objs['plane_wave_source'].show_preview('MESH')
|
||||
self.on_value_changed__center_direction()
|
||||
|
||||
|
||||
|
||||
####################
|
||||
# - Blender Registration
|
||||
####################
|
||||
BL_REGISTER = [
|
||||
PlaneWaveSourceNode,
|
||||
]
|
||||
BL_NODES = {
|
||||
ct.NodeType.PlaneWaveSource: (
|
||||
ct.NodeCategory.MAXWELLSIM_SOURCES
|
||||
)
|
||||
}
|
||||
BL_NODES = {ct.NodeType.PlaneWaveSource: (ct.NodeCategory.MAXWELLSIM_SOURCES)}
|
||||
|
|
|
@ -10,79 +10,82 @@ from ... import sockets
|
|||
from .. import base
|
||||
from ... import managed_objs
|
||||
|
||||
|
||||
class PointDipoleSourceNode(base.MaxwellSimNode):
|
||||
node_type = ct.NodeType.PointDipoleSource
|
||||
bl_label = "Point Dipole Source"
|
||||
|
||||
bl_label = 'Point Dipole Source'
|
||||
|
||||
####################
|
||||
# - Sockets
|
||||
####################
|
||||
input_sockets = {
|
||||
"Temporal Shape": sockets.MaxwellTemporalShapeSocketDef(),
|
||||
"Center": sockets.PhysicalPoint3DSocketDef(),
|
||||
"Interpolate": sockets.BoolSocketDef(
|
||||
'Temporal Shape': sockets.MaxwellTemporalShapeSocketDef(),
|
||||
'Center': sockets.PhysicalPoint3DSocketDef(),
|
||||
'Interpolate': sockets.BoolSocketDef(
|
||||
default_value=True,
|
||||
),
|
||||
}
|
||||
output_sockets = {
|
||||
"Source": sockets.MaxwellSourceSocketDef(),
|
||||
'Source': sockets.MaxwellSourceSocketDef(),
|
||||
}
|
||||
|
||||
|
||||
managed_obj_defs = {
|
||||
"sphere_empty": ct.schemas.ManagedObjDef(
|
||||
'sphere_empty': ct.schemas.ManagedObjDef(
|
||||
mk=lambda name: managed_objs.ManagedBLObject(name),
|
||||
name_prefix="",
|
||||
name_prefix='',
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
####################
|
||||
# - Properties
|
||||
####################
|
||||
pol_axis: bpy.props.EnumProperty(
|
||||
name="Polarization Axis",
|
||||
description="Polarization Axis",
|
||||
name='Polarization Axis',
|
||||
description='Polarization Axis',
|
||||
items=[
|
||||
("EX", "Ex", "Electric field in x-dir"),
|
||||
("EY", "Ey", "Electric field in y-dir"),
|
||||
("EZ", "Ez", "Electric field in z-dir"),
|
||||
('EX', 'Ex', 'Electric field in x-dir'),
|
||||
('EY', 'Ey', 'Electric field in y-dir'),
|
||||
('EZ', 'Ez', 'Electric field in z-dir'),
|
||||
],
|
||||
default="EX",
|
||||
update=(lambda self, context: self.sync_prop("pol_axis", context)),
|
||||
default='EX',
|
||||
update=(lambda self, context: self.sync_prop('pol_axis', context)),
|
||||
)
|
||||
|
||||
|
||||
####################
|
||||
# - UI
|
||||
####################
|
||||
def draw_props(self, context, layout):
|
||||
split = layout.split(factor=0.6)
|
||||
|
||||
|
||||
col = split.column()
|
||||
col.label(text="Pol Axis")
|
||||
|
||||
col.label(text='Pol Axis')
|
||||
|
||||
col = split.column()
|
||||
col.prop(self, "pol_axis", text="")
|
||||
|
||||
col.prop(self, 'pol_axis', text='')
|
||||
|
||||
####################
|
||||
# - Output Socket Computation
|
||||
####################
|
||||
@base.computes_output_socket(
|
||||
"Source",
|
||||
input_sockets={"Temporal Shape", "Center", "Interpolate"},
|
||||
props={"pol_axis"},
|
||||
'Source',
|
||||
input_sockets={'Temporal Shape', 'Center', 'Interpolate'},
|
||||
props={'pol_axis'},
|
||||
)
|
||||
def compute_source(self, input_sockets: dict[str, typ.Any], props: dict[str, typ.Any]) -> td.PointDipole:
|
||||
def compute_source(
|
||||
self, input_sockets: dict[str, typ.Any], props: dict[str, typ.Any]
|
||||
) -> td.PointDipole:
|
||||
pol_axis = {
|
||||
"EX": "Ex",
|
||||
"EY": "Ey",
|
||||
"EZ": "Ez",
|
||||
}[props["pol_axis"]]
|
||||
|
||||
temporal_shape = input_sockets["Temporal Shape"]
|
||||
_center = input_sockets["Center"]
|
||||
interpolate = input_sockets["Interpolate"]
|
||||
|
||||
'EX': 'Ex',
|
||||
'EY': 'Ey',
|
||||
'EZ': 'Ez',
|
||||
}[props['pol_axis']]
|
||||
|
||||
temporal_shape = input_sockets['Temporal Shape']
|
||||
_center = input_sockets['Center']
|
||||
interpolate = input_sockets['Interpolate']
|
||||
|
||||
center = tuple(spu.convert_to(_center, spu.um) / spu.um)
|
||||
|
||||
|
||||
_res = td.PointDipole(
|
||||
center=center,
|
||||
source_time=temporal_shape,
|
||||
|
@ -90,41 +93,42 @@ class PointDipoleSourceNode(base.MaxwellSimNode):
|
|||
polarization=pol_axis,
|
||||
)
|
||||
return _res
|
||||
|
||||
|
||||
####################
|
||||
# - Preview
|
||||
####################
|
||||
@base.on_value_changed(
|
||||
socket_name="Center",
|
||||
input_sockets={"Center"},
|
||||
managed_objs={"sphere_empty"},
|
||||
socket_name='Center',
|
||||
input_sockets={'Center'},
|
||||
managed_objs={'sphere_empty'},
|
||||
)
|
||||
def on_value_changed__center(
|
||||
self,
|
||||
input_sockets: dict,
|
||||
managed_objs: dict[str, ct.schemas.ManagedObj],
|
||||
):
|
||||
_center = input_sockets["Center"]
|
||||
_center = input_sockets['Center']
|
||||
center = tuple(spu.convert_to(_center, spu.um) / spu.um)
|
||||
## TODO: Preview unit system?? Presume um for now
|
||||
|
||||
mobj = managed_objs["sphere_empty"]
|
||||
bl_object = mobj.bl_object("EMPTY")
|
||||
bl_object.location = center #tuple([float(el) for el in center])
|
||||
|
||||
|
||||
mobj = managed_objs['sphere_empty']
|
||||
bl_object = mobj.bl_object('EMPTY')
|
||||
bl_object.location = center # tuple([float(el) for el in center])
|
||||
|
||||
@base.on_show_preview(
|
||||
managed_objs={"sphere_empty"},
|
||||
managed_objs={'sphere_empty'},
|
||||
)
|
||||
def on_show_preview(
|
||||
self,
|
||||
managed_objs: dict[str, ct.schemas.ManagedObj],
|
||||
):
|
||||
managed_objs["sphere_empty"].show_preview(
|
||||
"EMPTY",
|
||||
empty_display_type="SPHERE",
|
||||
managed_objs['sphere_empty'].show_preview(
|
||||
'EMPTY',
|
||||
empty_display_type='SPHERE',
|
||||
)
|
||||
managed_objs["sphere_empty"].bl_object("EMPTY").empty_display_size = 0.2
|
||||
|
||||
managed_objs['sphere_empty'].bl_object(
|
||||
'EMPTY'
|
||||
).empty_display_size = 0.2
|
||||
|
||||
|
||||
####################
|
||||
|
@ -134,7 +138,5 @@ BL_REGISTER = [
|
|||
PointDipoleSourceNode,
|
||||
]
|
||||
BL_NODES = {
|
||||
ct.NodeType.PointDipoleSource: (
|
||||
ct.NodeCategory.MAXWELLSIM_SOURCES
|
||||
)
|
||||
ct.NodeType.PointDipoleSource: (ct.NodeCategory.MAXWELLSIM_SOURCES)
|
||||
}
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
from . import gaussian_pulse_temporal_shape
|
||||
#from . import continuous_wave_temporal_shape
|
||||
#from . import array_temporal_shape
|
||||
# from . import continuous_wave_temporal_shape
|
||||
# from . import array_temporal_shape
|
||||
|
||||
BL_REGISTER = [
|
||||
*gaussian_pulse_temporal_shape.BL_REGISTER,
|
||||
# *continuous_wave_temporal_shape.BL_REGISTER,
|
||||
# *array_temporal_shape.BL_REGISTER,
|
||||
# *continuous_wave_temporal_shape.BL_REGISTER,
|
||||
# *array_temporal_shape.BL_REGISTER,
|
||||
]
|
||||
BL_NODES = {
|
||||
**gaussian_pulse_temporal_shape.BL_NODES,
|
||||
# **continuous_wave_temporal_shape.BL_NODES,
|
||||
# **array_temporal_shape.BL_NODES,
|
||||
# **continuous_wave_temporal_shape.BL_NODES,
|
||||
# **array_temporal_shape.BL_NODES,
|
||||
}
|
||||
|
|
|
@ -3,4 +3,3 @@
|
|||
####################
|
||||
BL_REGISTER = []
|
||||
BL_NODES = {}
|
||||
|
||||
|
|
|
@ -6,54 +6,55 @@ from .... import contracts
|
|||
from .... import sockets
|
||||
from ... import base
|
||||
|
||||
|
||||
class ContinuousWaveTemporalShapeNode(base.MaxwellSimTreeNode):
|
||||
node_type = contracts.NodeType.ContinuousWaveTemporalShape
|
||||
|
||||
bl_label = "Continuous Wave Temporal Shape"
|
||||
#bl_icon = ...
|
||||
|
||||
|
||||
bl_label = 'Continuous Wave Temporal Shape'
|
||||
# bl_icon = ...
|
||||
|
||||
####################
|
||||
# - Sockets
|
||||
####################
|
||||
input_sockets = {
|
||||
#"amplitude": sockets.RealNumberSocketDef(
|
||||
# label="Temporal Shape",
|
||||
#), ## Should have a unit of some kind...
|
||||
"phase": sockets.PhysicalAngleSocketDef(
|
||||
label="Phase",
|
||||
# "amplitude": sockets.RealNumberSocketDef(
|
||||
# label="Temporal Shape",
|
||||
# ), ## Should have a unit of some kind...
|
||||
'phase': sockets.PhysicalAngleSocketDef(
|
||||
label='Phase',
|
||||
),
|
||||
"freq_center": sockets.PhysicalFreqSocketDef(
|
||||
label="Freq Center",
|
||||
'freq_center': sockets.PhysicalFreqSocketDef(
|
||||
label='Freq Center',
|
||||
),
|
||||
"freq_std": sockets.PhysicalFreqSocketDef(
|
||||
label="Freq STD",
|
||||
'freq_std': sockets.PhysicalFreqSocketDef(
|
||||
label='Freq STD',
|
||||
),
|
||||
"time_delay_rel_ang_freq": sockets.RealNumberSocketDef(
|
||||
label="Time Delay rel. Ang. Freq",
|
||||
'time_delay_rel_ang_freq': sockets.RealNumberSocketDef(
|
||||
label='Time Delay rel. Ang. Freq',
|
||||
default_value=5.0,
|
||||
),
|
||||
}
|
||||
output_sockets = {
|
||||
"temporal_shape": sockets.MaxwellTemporalShapeSocketDef(
|
||||
label="Temporal Shape",
|
||||
'temporal_shape': sockets.MaxwellTemporalShapeSocketDef(
|
||||
label='Temporal Shape',
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
####################
|
||||
# - Output Socket Computation
|
||||
####################
|
||||
@base.computes_output_socket("temporal_shape")
|
||||
@base.computes_output_socket('temporal_shape')
|
||||
def compute_source(self: contracts.NodeTypeProtocol) -> td.PointDipole:
|
||||
_phase = self.compute_input("phase")
|
||||
_freq_center = self.compute_input("freq_center")
|
||||
_freq_std = self.compute_input("freq_std")
|
||||
time_delay_rel_ang_freq = self.compute_input("time_delay_rel_ang_freq")
|
||||
|
||||
_phase = self.compute_input('phase')
|
||||
_freq_center = self.compute_input('freq_center')
|
||||
_freq_std = self.compute_input('freq_std')
|
||||
time_delay_rel_ang_freq = self.compute_input('time_delay_rel_ang_freq')
|
||||
|
||||
cheating_amplitude = 1.0
|
||||
phase = spu.convert_to(_phase, spu.radian) / spu.radian
|
||||
freq_center = spu.convert_to(_freq_center, spu.hertz) / spu.hertz
|
||||
freq_std = spu.convert_to(_freq_std, spu.hertz) / spu.hertz
|
||||
|
||||
|
||||
return td.ContinuousWave(
|
||||
amplitude=cheating_amplitude,
|
||||
phase=phase,
|
||||
|
@ -63,7 +64,6 @@ class ContinuousWaveTemporalShapeNode(base.MaxwellSimTreeNode):
|
|||
)
|
||||
|
||||
|
||||
|
||||
####################
|
||||
# - Blender Registration
|
||||
####################
|
||||
|
|
|
@ -13,98 +13,107 @@ from .... import sockets
|
|||
from .... import managed_objs
|
||||
from ... import base
|
||||
|
||||
|
||||
class GaussianPulseTemporalShapeNode(base.MaxwellSimNode):
|
||||
node_type = ct.NodeType.GaussianPulseTemporalShape
|
||||
bl_label = "Gaussian Pulse Temporal Shape"
|
||||
#bl_icon = ...
|
||||
|
||||
bl_label = 'Gaussian Pulse Temporal Shape'
|
||||
# bl_icon = ...
|
||||
|
||||
####################
|
||||
# - Sockets
|
||||
####################
|
||||
input_sockets = {
|
||||
#"amplitude": sockets.RealNumberSocketDef(
|
||||
# label="Temporal Shape",
|
||||
#), ## Should have a unit of some kind...
|
||||
"Freq Center": sockets.PhysicalFreqSocketDef(
|
||||
# "amplitude": sockets.RealNumberSocketDef(
|
||||
# label="Temporal Shape",
|
||||
# ), ## Should have a unit of some kind...
|
||||
'Freq Center': sockets.PhysicalFreqSocketDef(
|
||||
default_value=500 * spuex.terahertz,
|
||||
),
|
||||
"Freq Std.": sockets.PhysicalFreqSocketDef(
|
||||
'Freq Std.': sockets.PhysicalFreqSocketDef(
|
||||
default_value=200 * spuex.terahertz,
|
||||
),
|
||||
"Phase": sockets.PhysicalAngleSocketDef(),
|
||||
"Delay rel. AngFreq": sockets.RealNumberSocketDef(
|
||||
'Phase': sockets.PhysicalAngleSocketDef(),
|
||||
'Delay rel. AngFreq': sockets.RealNumberSocketDef(
|
||||
default_value=5.0,
|
||||
),
|
||||
"Remove DC": sockets.BoolSocketDef(
|
||||
'Remove DC': sockets.BoolSocketDef(
|
||||
default_value=True,
|
||||
),
|
||||
}
|
||||
output_sockets = {
|
||||
"Temporal Shape": sockets.MaxwellTemporalShapeSocketDef(),
|
||||
'Temporal Shape': sockets.MaxwellTemporalShapeSocketDef(),
|
||||
}
|
||||
|
||||
|
||||
managed_obj_defs = {
|
||||
"amp_time": ct.schemas.ManagedObjDef(
|
||||
'amp_time': ct.schemas.ManagedObjDef(
|
||||
mk=lambda name: managed_objs.ManagedBLImage(name),
|
||||
name_prefix="amp_time_",
|
||||
name_prefix='amp_time_',
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
####################
|
||||
# - Properties
|
||||
####################
|
||||
plot_time_start: bpy.props.FloatProperty(
|
||||
name="Plot Time Start (ps)",
|
||||
description="The instance ID of a particular MaxwellSimNode instance, used to index caches",
|
||||
name='Plot Time Start (ps)',
|
||||
description='The instance ID of a particular MaxwellSimNode instance, used to index caches',
|
||||
default=0.0,
|
||||
update=(lambda self, context: self.sync_prop("plot_time_start", context)),
|
||||
update=(
|
||||
lambda self, context: self.sync_prop('plot_time_start', context)
|
||||
),
|
||||
)
|
||||
plot_time_end: bpy.props.FloatProperty(
|
||||
name="Plot Time End (ps)",
|
||||
description="The instance ID of a particular MaxwellSimNode instance, used to index caches",
|
||||
name='Plot Time End (ps)',
|
||||
description='The instance ID of a particular MaxwellSimNode instance, used to index caches',
|
||||
default=5,
|
||||
update=(lambda self, context: self.sync_prop("plot_time_start", context)),
|
||||
update=(
|
||||
lambda self, context: self.sync_prop('plot_time_start', context)
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
####################
|
||||
# - UI
|
||||
####################
|
||||
def draw_props(self, context, layout):
|
||||
layout.label(text="Plot Settings")
|
||||
layout.label(text='Plot Settings')
|
||||
split = layout.split(factor=0.6)
|
||||
|
||||
|
||||
col = split.column()
|
||||
col.label(text="t-Range (ps)")
|
||||
|
||||
col.label(text='t-Range (ps)')
|
||||
|
||||
col = split.column()
|
||||
col.prop(self, "plot_time_start", text="")
|
||||
col.prop(self, "plot_time_end", text="")
|
||||
|
||||
col.prop(self, 'plot_time_start', text='')
|
||||
col.prop(self, 'plot_time_end', text='')
|
||||
|
||||
####################
|
||||
# - Output Socket Computation
|
||||
####################
|
||||
@base.computes_output_socket(
|
||||
"Temporal Shape",
|
||||
'Temporal Shape',
|
||||
input_sockets={
|
||||
"Freq Center", "Freq Std.", "Phase", "Delay rel. AngFreq",
|
||||
"Remove DC",
|
||||
}
|
||||
'Freq Center',
|
||||
'Freq Std.',
|
||||
'Phase',
|
||||
'Delay rel. AngFreq',
|
||||
'Remove DC',
|
||||
},
|
||||
)
|
||||
def compute_source(self, input_sockets: dict) -> td.GaussianPulse:
|
||||
if (
|
||||
(_freq_center := input_sockets["Freq Center"]) is None
|
||||
or (_freq_std := input_sockets["Freq Std."]) is None
|
||||
or (_phase := input_sockets["Phase"]) is None
|
||||
or (time_delay_rel_ang_freq := input_sockets["Delay rel. AngFreq"]) is None
|
||||
or (remove_dc_component := input_sockets["Remove DC"]) is None
|
||||
(_freq_center := input_sockets['Freq Center']) is None
|
||||
or (_freq_std := input_sockets['Freq Std.']) is None
|
||||
or (_phase := input_sockets['Phase']) is None
|
||||
or (time_delay_rel_ang_freq := input_sockets['Delay rel. AngFreq'])
|
||||
is None
|
||||
or (remove_dc_component := input_sockets['Remove DC']) is None
|
||||
):
|
||||
raise ValueError("Inputs not defined")
|
||||
|
||||
raise ValueError('Inputs not defined')
|
||||
|
||||
cheating_amplitude = 1.0
|
||||
freq_center = spu.convert_to(_freq_center, spu.hertz) / spu.hertz
|
||||
freq_std = spu.convert_to(_freq_std, spu.hertz) / spu.hertz
|
||||
phase = spu.convert_to(_phase, spu.radian) / spu.radian
|
||||
|
||||
|
||||
return td.GaussianPulse(
|
||||
amplitude=cheating_amplitude,
|
||||
phase=phase,
|
||||
|
@ -113,11 +122,11 @@ class GaussianPulseTemporalShapeNode(base.MaxwellSimNode):
|
|||
offset=time_delay_rel_ang_freq,
|
||||
remove_dc_component=remove_dc_component,
|
||||
)
|
||||
|
||||
|
||||
@base.on_show_plot(
|
||||
managed_objs={"amp_time"},
|
||||
props={"plot_time_start", "plot_time_end"},
|
||||
output_sockets={"Temporal Shape"},
|
||||
managed_objs={'amp_time'},
|
||||
props={'plot_time_start', 'plot_time_end'},
|
||||
output_sockets={'Temporal Shape'},
|
||||
stop_propagation=True,
|
||||
)
|
||||
def on_show_plot(
|
||||
|
@ -126,19 +135,18 @@ class GaussianPulseTemporalShapeNode(base.MaxwellSimNode):
|
|||
output_sockets: dict[str, typ.Any],
|
||||
props: dict[str, typ.Any],
|
||||
):
|
||||
temporal_shape = output_sockets["Temporal Shape"]
|
||||
plot_time_start = props["plot_time_start"] * 1e-15
|
||||
plot_time_end = props["plot_time_end"] * 1e-15
|
||||
|
||||
temporal_shape = output_sockets['Temporal Shape']
|
||||
plot_time_start = props['plot_time_start'] * 1e-15
|
||||
plot_time_end = props['plot_time_end'] * 1e-15
|
||||
|
||||
times = np.linspace(plot_time_start, plot_time_end)
|
||||
|
||||
managed_objs["amp_time"].mpl_plot_to_image(
|
||||
|
||||
managed_objs['amp_time'].mpl_plot_to_image(
|
||||
lambda ax: temporal_shape.plot_spectrum(times, ax=ax),
|
||||
bl_select=True,
|
||||
)
|
||||
|
||||
|
||||
|
||||
####################
|
||||
# - Blender Registration
|
||||
####################
|
||||
|
|
|
@ -3,4 +3,3 @@
|
|||
####################
|
||||
BL_REGISTER = []
|
||||
BL_NODES = {}
|
||||
|
||||
|
|
|
@ -1,17 +1,15 @@
|
|||
#from . import object_structure
|
||||
# from . import object_structure
|
||||
from . import geonodes_structure
|
||||
|
||||
from . import primitives
|
||||
|
||||
BL_REGISTER = [
|
||||
# *object_structure.BL_REGISTER,
|
||||
# *object_structure.BL_REGISTER,
|
||||
*geonodes_structure.BL_REGISTER,
|
||||
|
||||
*primitives.BL_REGISTER,
|
||||
]
|
||||
BL_NODES = {
|
||||
# **object_structure.BL_NODES,
|
||||
# **object_structure.BL_NODES,
|
||||
**geonodes_structure.BL_NODES,
|
||||
|
||||
**primitives.BL_NODES,
|
||||
}
|
||||
|
|
|
@ -16,36 +16,37 @@ from ... import sockets
|
|||
from .. import base
|
||||
from ... import managed_objs
|
||||
|
||||
|
||||
class GeoNodesStructureNode(base.MaxwellSimNode):
|
||||
node_type = ct.NodeType.GeoNodesStructure
|
||||
bl_label = "GeoNodes Structure"
|
||||
|
||||
bl_label = 'GeoNodes Structure'
|
||||
|
||||
####################
|
||||
# - Sockets
|
||||
####################
|
||||
input_sockets = {
|
||||
"Unit System": sockets.PhysicalUnitSystemSocketDef(),
|
||||
"Medium": sockets.MaxwellMediumSocketDef(),
|
||||
"GeoNodes": sockets.BlenderGeoNodesSocketDef(),
|
||||
'Unit System': sockets.PhysicalUnitSystemSocketDef(),
|
||||
'Medium': sockets.MaxwellMediumSocketDef(),
|
||||
'GeoNodes': sockets.BlenderGeoNodesSocketDef(),
|
||||
}
|
||||
output_sockets = {
|
||||
"Structure": sockets.MaxwellStructureSocketDef(),
|
||||
'Structure': sockets.MaxwellStructureSocketDef(),
|
||||
}
|
||||
|
||||
|
||||
managed_obj_defs = {
|
||||
"geometry": ct.schemas.ManagedObjDef(
|
||||
'geometry': ct.schemas.ManagedObjDef(
|
||||
mk=lambda name: managed_objs.ManagedBLObject(name),
|
||||
name_prefix="",
|
||||
name_prefix='',
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
####################
|
||||
# - Output Socket Computation
|
||||
####################
|
||||
@base.computes_output_socket(
|
||||
"Structure",
|
||||
input_sockets={"Medium"},
|
||||
managed_objs={"geometry"},
|
||||
'Structure',
|
||||
input_sockets={'Medium'},
|
||||
managed_objs={'geometry'},
|
||||
)
|
||||
def compute_structure(
|
||||
self,
|
||||
|
@ -53,28 +54,27 @@ class GeoNodesStructureNode(base.MaxwellSimNode):
|
|||
managed_objs: dict[str, ct.schemas.ManagedObj],
|
||||
) -> td.Structure:
|
||||
# Extract the Managed Blender Object
|
||||
mobj = managed_objs["geometry"]
|
||||
|
||||
mobj = managed_objs['geometry']
|
||||
|
||||
# Extract Geometry as Arrays
|
||||
geometry_as_arrays = mobj.mesh_as_arrays
|
||||
|
||||
|
||||
# Return TriMesh Structure
|
||||
return td.Structure(
|
||||
geometry=td.TriangleMesh.from_vertices_faces(
|
||||
geometry_as_arrays["verts"],
|
||||
geometry_as_arrays["faces"],
|
||||
geometry_as_arrays['verts'],
|
||||
geometry_as_arrays['faces'],
|
||||
),
|
||||
medium=input_sockets["Medium"],
|
||||
medium=input_sockets['Medium'],
|
||||
)
|
||||
|
||||
|
||||
####################
|
||||
# - Event Methods
|
||||
####################
|
||||
@base.on_value_changed(
|
||||
socket_name="GeoNodes",
|
||||
|
||||
managed_objs={"geometry"},
|
||||
input_sockets={"GeoNodes"},
|
||||
socket_name='GeoNodes',
|
||||
managed_objs={'geometry'},
|
||||
input_sockets={'GeoNodes'},
|
||||
)
|
||||
def on_value_changed__geonodes(
|
||||
self,
|
||||
|
@ -82,20 +82,20 @@ class GeoNodesStructureNode(base.MaxwellSimNode):
|
|||
input_sockets: dict[str, typ.Any],
|
||||
) -> None:
|
||||
"""Called whenever the GeoNodes socket is changed.
|
||||
|
||||
|
||||
Refreshes the Loose Input Sockets, which map directly to the GeoNodes tree input sockets.
|
||||
"""
|
||||
if not (geo_nodes := input_sockets["GeoNodes"]):
|
||||
managed_objs["geometry"].free()
|
||||
if not (geo_nodes := input_sockets['GeoNodes']):
|
||||
managed_objs['geometry'].free()
|
||||
self.loose_input_sockets = {}
|
||||
return
|
||||
|
||||
|
||||
# Analyze GeoNodes
|
||||
## Extract Valid Inputs (via GeoNodes Tree "Interface")
|
||||
geonodes_interface = analyze_geonodes.interface(
|
||||
geo_nodes, direc="INPUT"
|
||||
geo_nodes, direc='INPUT'
|
||||
)
|
||||
|
||||
|
||||
# Set Loose Input Sockets
|
||||
## Retrieve the appropriate SocketDef for the Blender Interface Socket
|
||||
self.loose_input_sockets = {
|
||||
|
@ -104,21 +104,20 @@ class GeoNodesStructureNode(base.MaxwellSimNode):
|
|||
)() ## === <SocketType>SocketDef(), but with dynamic SocketDef
|
||||
for socket_name, bl_interface_socket in geonodes_interface.items()
|
||||
}
|
||||
|
||||
|
||||
## 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]
|
||||
|
||||
|
||||
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"},
|
||||
managed_objs={'geometry'},
|
||||
input_sockets={'Unit System', 'GeoNodes'},
|
||||
)
|
||||
def on_value_changed__loose_inputs(
|
||||
self,
|
||||
|
@ -127,26 +126,27 @@ class GeoNodesStructureNode(base.MaxwellSimNode):
|
|||
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
|
||||
|
||||
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"
|
||||
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,
|
||||
|
@ -159,24 +159,24 @@ class GeoNodesStructureNode(base.MaxwellSimNode):
|
|||
for socket_name, bl_interface_socket in (
|
||||
geonodes_interface.items()
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
####################
|
||||
# - Event Methods
|
||||
####################
|
||||
@base.on_show_preview(
|
||||
managed_objs={"geometry"},
|
||||
managed_objs={'geometry'},
|
||||
)
|
||||
def on_show_preview(
|
||||
self,
|
||||
managed_objs: dict[str, ct.schemas.ManagedObj],
|
||||
):
|
||||
"""Called whenever a Loose Input Socket is altered.
|
||||
|
||||
|
||||
Synchronizes the change to the actual GeoNodes modifier, so that the change is immediately visible.
|
||||
"""
|
||||
managed_objs["geometry"].show_preview("MESH")
|
||||
managed_objs['geometry'].show_preview('MESH')
|
||||
|
||||
|
||||
####################
|
||||
|
@ -186,7 +186,5 @@ BL_REGISTER = [
|
|||
GeoNodesStructureNode,
|
||||
]
|
||||
BL_NODES = {
|
||||
ct.NodeType.GeoNodesStructure: (
|
||||
ct.NodeCategory.MAXWELLSIM_STRUCTURES
|
||||
)
|
||||
ct.NodeType.GeoNodesStructure: (ct.NodeCategory.MAXWELLSIM_STRUCTURES)
|
||||
}
|
||||
|
|
|
@ -10,39 +10,40 @@ from ... import contracts
|
|||
from ... import sockets
|
||||
from .. import base
|
||||
|
||||
|
||||
class ObjectStructureNode(base.MaxwellSimTreeNode):
|
||||
node_type = contracts.NodeType.ObjectStructure
|
||||
bl_label = "Object Structure"
|
||||
#bl_icon = ...
|
||||
|
||||
bl_label = 'Object Structure'
|
||||
# bl_icon = ...
|
||||
|
||||
####################
|
||||
# - Sockets
|
||||
####################
|
||||
input_sockets = {
|
||||
"medium": sockets.MaxwellMediumSocketDef(
|
||||
label="Medium",
|
||||
'medium': sockets.MaxwellMediumSocketDef(
|
||||
label='Medium',
|
||||
),
|
||||
"object": sockets.BlenderObjectSocketDef(
|
||||
label="Object",
|
||||
'object': sockets.BlenderObjectSocketDef(
|
||||
label='Object',
|
||||
),
|
||||
}
|
||||
output_sockets = {
|
||||
"structure": sockets.MaxwellStructureSocketDef(
|
||||
label="Structure",
|
||||
'structure': sockets.MaxwellStructureSocketDef(
|
||||
label='Structure',
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
####################
|
||||
# - Output Socket Computation
|
||||
####################
|
||||
@base.computes_output_socket("structure")
|
||||
@base.computes_output_socket('structure')
|
||||
def compute_structure(self: contracts.NodeTypeProtocol) -> td.Structure:
|
||||
# Extract the Blender Object
|
||||
bl_object = self.compute_input("object")
|
||||
|
||||
bl_object = self.compute_input('object')
|
||||
|
||||
# Ensure Updated Geometry
|
||||
bpy.context.view_layer.update()
|
||||
|
||||
|
||||
# Triangulate Object Mesh
|
||||
bmesh_mesh = bmesh.new()
|
||||
bmesh_mesh.from_object(
|
||||
|
@ -50,26 +51,24 @@ class ObjectStructureNode(base.MaxwellSimTreeNode):
|
|||
bpy.context.evaluated_depsgraph_get(),
|
||||
)
|
||||
bmesh.ops.triangulate(bmesh_mesh, faces=bmesh_mesh.faces)
|
||||
|
||||
mesh = bpy.data.meshes.new(name="TriangulatedMesh")
|
||||
|
||||
mesh = bpy.data.meshes.new(name='TriangulatedMesh')
|
||||
bmesh_mesh.to_mesh(mesh)
|
||||
bmesh_mesh.free()
|
||||
|
||||
|
||||
# Extract Vertices and Faces
|
||||
vertices = np.array([vert.co for vert in mesh.vertices])
|
||||
faces = np.array([
|
||||
[vert for vert in poly.vertices]
|
||||
for poly in mesh.polygons
|
||||
])
|
||||
|
||||
# Remove Temporary Mesh
|
||||
bpy.data.meshes.remove(mesh)
|
||||
|
||||
return td.Structure(
|
||||
geometry=td.TriangleMesh.from_vertices_faces(vertices, faces),
|
||||
medium=self.compute_input("medium")
|
||||
faces = np.array(
|
||||
[[vert for vert in poly.vertices] for poly in mesh.polygons]
|
||||
)
|
||||
|
||||
# Remove Temporary Mesh
|
||||
bpy.data.meshes.remove(mesh)
|
||||
|
||||
return td.Structure(
|
||||
geometry=td.TriangleMesh.from_vertices_faces(vertices, faces),
|
||||
medium=self.compute_input('medium'),
|
||||
)
|
||||
|
||||
|
||||
####################
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
from . import box_structure
|
||||
#from . import cylinder_structure
|
||||
|
||||
# from . import cylinder_structure
|
||||
from . import sphere_structure
|
||||
|
||||
BL_REGISTER = [
|
||||
*box_structure.BL_REGISTER,
|
||||
# *cylinder_structure.BL_REGISTER,
|
||||
# *cylinder_structure.BL_REGISTER,
|
||||
*sphere_structure.BL_REGISTER,
|
||||
]
|
||||
BL_NODES = {
|
||||
**box_structure.BL_NODES,
|
||||
# **cylinder_structure.BL_NODES,
|
||||
# **cylinder_structure.BL_NODES,
|
||||
**sphere_structure.BL_NODES,
|
||||
}
|
||||
|
|
|
@ -10,48 +10,49 @@ from .... import sockets
|
|||
from .... import managed_objs
|
||||
from ... import base
|
||||
|
||||
GEONODES_STRUCTURE_BOX = "structure_box"
|
||||
GEONODES_STRUCTURE_BOX = 'structure_box'
|
||||
|
||||
|
||||
class BoxStructureNode(base.MaxwellSimNode):
|
||||
node_type = ct.NodeType.BoxStructure
|
||||
bl_label = "Box Structure"
|
||||
|
||||
bl_label = 'Box Structure'
|
||||
|
||||
####################
|
||||
# - Sockets
|
||||
####################
|
||||
input_sockets = {
|
||||
"Medium": sockets.MaxwellMediumSocketDef(),
|
||||
"Center": sockets.PhysicalPoint3DSocketDef(),
|
||||
"Size": sockets.PhysicalSize3DSocketDef(
|
||||
'Medium': sockets.MaxwellMediumSocketDef(),
|
||||
'Center': sockets.PhysicalPoint3DSocketDef(),
|
||||
'Size': sockets.PhysicalSize3DSocketDef(
|
||||
default_value=sp.Matrix([500, 500, 500]) * spu.nm
|
||||
),
|
||||
}
|
||||
output_sockets = {
|
||||
"Structure": sockets.MaxwellStructureSocketDef(),
|
||||
'Structure': sockets.MaxwellStructureSocketDef(),
|
||||
}
|
||||
|
||||
|
||||
managed_obj_defs = {
|
||||
"structure_box": ct.schemas.ManagedObjDef(
|
||||
'structure_box': ct.schemas.ManagedObjDef(
|
||||
mk=lambda name: managed_objs.ManagedBLObject(name),
|
||||
name_prefix="",
|
||||
name_prefix='',
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
####################
|
||||
# - Output Socket Computation
|
||||
####################
|
||||
@base.computes_output_socket(
|
||||
"Structure",
|
||||
input_sockets={"Medium", "Center", "Size"},
|
||||
'Structure',
|
||||
input_sockets={'Medium', 'Center', 'Size'},
|
||||
)
|
||||
def compute_simulation(self, input_sockets: dict) -> td.Box:
|
||||
medium = input_sockets["Medium"]
|
||||
_center = input_sockets["Center"]
|
||||
_size = input_sockets["Size"]
|
||||
|
||||
medium = input_sockets['Medium']
|
||||
_center = input_sockets['Center']
|
||||
_size = input_sockets['Size']
|
||||
|
||||
center = tuple(spu.convert_to(_center, spu.um) / spu.um)
|
||||
size = tuple(spu.convert_to(_size, spu.um) / spu.um)
|
||||
|
||||
|
||||
return td.Structure(
|
||||
geometry=td.Box(
|
||||
center=center,
|
||||
|
@ -59,69 +60,66 @@ class BoxStructureNode(base.MaxwellSimNode):
|
|||
),
|
||||
medium=medium,
|
||||
)
|
||||
|
||||
|
||||
####################
|
||||
# - Preview - Changes to Input Sockets
|
||||
####################
|
||||
@base.on_value_changed(
|
||||
socket_name={"Center", "Size"},
|
||||
input_sockets={"Center", "Size"},
|
||||
managed_objs={"structure_box"},
|
||||
socket_name={'Center', 'Size'},
|
||||
input_sockets={'Center', 'Size'},
|
||||
managed_objs={'structure_box'},
|
||||
)
|
||||
def on_value_changed__center_size(
|
||||
self,
|
||||
input_sockets: dict,
|
||||
managed_objs: dict[str, ct.schemas.ManagedObj],
|
||||
):
|
||||
_center = input_sockets["Center"]
|
||||
center = tuple([
|
||||
float(el)
|
||||
for el in spu.convert_to(_center, spu.um) / spu.um
|
||||
])
|
||||
|
||||
_size = input_sockets["Size"]
|
||||
size = tuple([
|
||||
float(el)
|
||||
for el in spu.convert_to(_size, spu.um) / spu.um
|
||||
])
|
||||
_center = input_sockets['Center']
|
||||
center = tuple(
|
||||
[float(el) for el in spu.convert_to(_center, spu.um) / spu.um]
|
||||
)
|
||||
|
||||
_size = input_sockets['Size']
|
||||
size = tuple(
|
||||
[float(el) for el in spu.convert_to(_size, spu.um) / spu.um]
|
||||
)
|
||||
## TODO: Preview unit system?? Presume um for now
|
||||
|
||||
|
||||
# Retrieve Hard-Coded GeoNodes and Analyze Input
|
||||
geo_nodes = bpy.data.node_groups[GEONODES_STRUCTURE_BOX]
|
||||
geonodes_interface = analyze_geonodes.interface(
|
||||
geo_nodes, direc="INPUT"
|
||||
geo_nodes, direc='INPUT'
|
||||
)
|
||||
|
||||
|
||||
# Sync Modifier Inputs
|
||||
managed_objs["structure_box"].sync_geonodes_modifier(
|
||||
managed_objs['structure_box'].sync_geonodes_modifier(
|
||||
geonodes_node_group=geo_nodes,
|
||||
geonodes_identifier_to_value={
|
||||
geonodes_interface["Size"].identifier: size,
|
||||
geonodes_interface['Size'].identifier: size,
|
||||
## TODO: Use 'bl_socket_map.value_to_bl`!
|
||||
## - This accounts for auto-conversion, unit systems, etc. .
|
||||
## - We could keep it in the node base class...
|
||||
## - ...But it needs aligning with Blender, too. Hmm.
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
# Sync Object Position
|
||||
managed_objs["structure_box"].bl_object("MESH").location = center
|
||||
|
||||
managed_objs['structure_box'].bl_object('MESH').location = center
|
||||
|
||||
####################
|
||||
# - Preview - Show Preview
|
||||
####################
|
||||
@base.on_show_preview(
|
||||
managed_objs={"structure_box"},
|
||||
managed_objs={'structure_box'},
|
||||
)
|
||||
def on_show_preview(
|
||||
self,
|
||||
managed_objs: dict[str, ct.schemas.ManagedObj],
|
||||
):
|
||||
managed_objs["structure_box"].show_preview("MESH")
|
||||
managed_objs['structure_box'].show_preview('MESH')
|
||||
self.on_value_changed__center_size()
|
||||
|
||||
|
||||
|
||||
####################
|
||||
# - Blender Registration
|
||||
####################
|
||||
|
|
|
@ -6,48 +6,49 @@ from .... import contracts
|
|||
from .... import sockets
|
||||
from ... import base
|
||||
|
||||
|
||||
class CylinderStructureNode(base.MaxwellSimTreeNode):
|
||||
node_type = contracts.NodeType.CylinderStructure
|
||||
bl_label = "Cylinder Structure"
|
||||
#bl_icon = ...
|
||||
|
||||
bl_label = 'Cylinder Structure'
|
||||
# bl_icon = ...
|
||||
|
||||
####################
|
||||
# - Sockets
|
||||
####################
|
||||
input_sockets = {
|
||||
"medium": sockets.MaxwellMediumSocketDef(
|
||||
label="Medium",
|
||||
'medium': sockets.MaxwellMediumSocketDef(
|
||||
label='Medium',
|
||||
),
|
||||
"center": sockets.PhysicalPoint3DSocketDef(
|
||||
label="Center",
|
||||
'center': sockets.PhysicalPoint3DSocketDef(
|
||||
label='Center',
|
||||
),
|
||||
"radius": sockets.PhysicalLengthSocketDef(
|
||||
label="Radius",
|
||||
'radius': sockets.PhysicalLengthSocketDef(
|
||||
label='Radius',
|
||||
),
|
||||
"height": sockets.PhysicalLengthSocketDef(
|
||||
label="Height",
|
||||
'height': sockets.PhysicalLengthSocketDef(
|
||||
label='Height',
|
||||
),
|
||||
}
|
||||
output_sockets = {
|
||||
"structure": sockets.MaxwellStructureSocketDef(
|
||||
label="Structure",
|
||||
'structure': sockets.MaxwellStructureSocketDef(
|
||||
label='Structure',
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
####################
|
||||
# - Output Socket Computation
|
||||
####################
|
||||
@base.computes_output_socket("structure")
|
||||
@base.computes_output_socket('structure')
|
||||
def compute_simulation(self: contracts.NodeTypeProtocol) -> td.Box:
|
||||
medium = self.compute_input("medium")
|
||||
_center = self.compute_input("center")
|
||||
_radius = self.compute_input("radius")
|
||||
_height = self.compute_input("height")
|
||||
|
||||
medium = self.compute_input('medium')
|
||||
_center = self.compute_input('center')
|
||||
_radius = self.compute_input('radius')
|
||||
_height = self.compute_input('height')
|
||||
|
||||
center = tuple(spu.convert_to(_center, spu.um) / spu.um)
|
||||
radius = spu.convert_to(_radius, spu.um) / spu.um
|
||||
height = spu.convert_to(_height, spu.um) / spu.um
|
||||
|
||||
|
||||
return td.Structure(
|
||||
geometry=td.Cylinder(
|
||||
radius=radius,
|
||||
|
@ -58,7 +59,6 @@ class CylinderStructureNode(base.MaxwellSimTreeNode):
|
|||
)
|
||||
|
||||
|
||||
|
||||
####################
|
||||
# - Blender Registration
|
||||
####################
|
||||
|
|
|
@ -10,48 +10,49 @@ from .... import sockets
|
|||
from .... import managed_objs
|
||||
from ... import base
|
||||
|
||||
GEONODES_STRUCTURE_SPHERE = "structure_sphere"
|
||||
GEONODES_STRUCTURE_SPHERE = 'structure_sphere'
|
||||
|
||||
|
||||
class SphereStructureNode(base.MaxwellSimNode):
|
||||
node_type = ct.NodeType.SphereStructure
|
||||
bl_label = "Sphere Structure"
|
||||
|
||||
bl_label = 'Sphere Structure'
|
||||
|
||||
####################
|
||||
# - Sockets
|
||||
####################
|
||||
input_sockets = {
|
||||
"Center": sockets.PhysicalPoint3DSocketDef(),
|
||||
"Radius": sockets.PhysicalLengthSocketDef(
|
||||
default_value=150*spu.nm,
|
||||
'Center': sockets.PhysicalPoint3DSocketDef(),
|
||||
'Radius': sockets.PhysicalLengthSocketDef(
|
||||
default_value=150 * spu.nm,
|
||||
),
|
||||
"Medium": sockets.MaxwellMediumSocketDef(),
|
||||
'Medium': sockets.MaxwellMediumSocketDef(),
|
||||
}
|
||||
output_sockets = {
|
||||
"Structure": sockets.MaxwellStructureSocketDef(),
|
||||
'Structure': sockets.MaxwellStructureSocketDef(),
|
||||
}
|
||||
|
||||
|
||||
managed_obj_defs = {
|
||||
"structure_sphere": ct.schemas.ManagedObjDef(
|
||||
'structure_sphere': ct.schemas.ManagedObjDef(
|
||||
mk=lambda name: managed_objs.ManagedBLObject(name),
|
||||
name_prefix="",
|
||||
name_prefix='',
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
####################
|
||||
# - Output Socket Computation
|
||||
####################
|
||||
@base.computes_output_socket(
|
||||
"Structure",
|
||||
input_sockets={"Center", "Radius", "Medium"},
|
||||
'Structure',
|
||||
input_sockets={'Center', 'Radius', 'Medium'},
|
||||
)
|
||||
def compute_structure(self, input_sockets: dict) -> td.Box:
|
||||
medium = input_sockets["Medium"]
|
||||
_center = input_sockets["Center"]
|
||||
_radius = input_sockets["Radius"]
|
||||
|
||||
medium = input_sockets['Medium']
|
||||
_center = input_sockets['Center']
|
||||
_radius = input_sockets['Radius']
|
||||
|
||||
center = tuple(spu.convert_to(_center, spu.um) / spu.um)
|
||||
radius = spu.convert_to(_radius, spu.um) / spu.um
|
||||
|
||||
|
||||
return td.Structure(
|
||||
geometry=td.Sphere(
|
||||
radius=radius,
|
||||
|
@ -59,66 +60,64 @@ class SphereStructureNode(base.MaxwellSimNode):
|
|||
),
|
||||
medium=medium,
|
||||
)
|
||||
|
||||
|
||||
####################
|
||||
# - Preview - Changes to Input Sockets
|
||||
####################
|
||||
@base.on_value_changed(
|
||||
socket_name={"Center", "Radius"},
|
||||
input_sockets={"Center", "Radius"},
|
||||
managed_objs={"structure_sphere"},
|
||||
socket_name={'Center', 'Radius'},
|
||||
input_sockets={'Center', 'Radius'},
|
||||
managed_objs={'structure_sphere'},
|
||||
)
|
||||
def on_value_changed__center_radius(
|
||||
self,
|
||||
input_sockets: dict,
|
||||
managed_objs: dict[str, ct.schemas.ManagedObj],
|
||||
):
|
||||
_center = input_sockets["Center"]
|
||||
center = tuple([
|
||||
float(el)
|
||||
for el in spu.convert_to(_center, spu.um) / spu.um
|
||||
])
|
||||
|
||||
_radius = input_sockets["Radius"]
|
||||
_center = input_sockets['Center']
|
||||
center = tuple(
|
||||
[float(el) for el in spu.convert_to(_center, spu.um) / spu.um]
|
||||
)
|
||||
|
||||
_radius = input_sockets['Radius']
|
||||
radius = float(spu.convert_to(_radius, spu.um) / spu.um)
|
||||
## TODO: Preview unit system?? Presume um for now
|
||||
|
||||
|
||||
# Retrieve Hard-Coded GeoNodes and Analyze Input
|
||||
geo_nodes = bpy.data.node_groups[GEONODES_STRUCTURE_SPHERE]
|
||||
geonodes_interface = analyze_geonodes.interface(
|
||||
geo_nodes, direc="INPUT"
|
||||
geo_nodes, direc='INPUT'
|
||||
)
|
||||
|
||||
|
||||
# Sync Modifier Inputs
|
||||
managed_objs["structure_sphere"].sync_geonodes_modifier(
|
||||
managed_objs['structure_sphere'].sync_geonodes_modifier(
|
||||
geonodes_node_group=geo_nodes,
|
||||
geonodes_identifier_to_value={
|
||||
geonodes_interface["Radius"].identifier: radius,
|
||||
geonodes_interface['Radius'].identifier: radius,
|
||||
## TODO: Use 'bl_socket_map.value_to_bl`!
|
||||
## - This accounts for auto-conversion, unit systems, etc. .
|
||||
## - We could keep it in the node base class...
|
||||
## - ...But it needs aligning with Blender, too. Hmm.
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
# Sync Object Position
|
||||
managed_objs["structure_sphere"].bl_object("MESH").location = center
|
||||
|
||||
managed_objs['structure_sphere'].bl_object('MESH').location = center
|
||||
|
||||
####################
|
||||
# - Preview - Show Preview
|
||||
####################
|
||||
@base.on_show_preview(
|
||||
managed_objs={"structure_sphere"},
|
||||
managed_objs={'structure_sphere'},
|
||||
)
|
||||
def on_show_preview(
|
||||
self,
|
||||
managed_objs: dict[str, ct.schemas.ManagedObj],
|
||||
):
|
||||
managed_objs["structure_sphere"].show_preview("MESH")
|
||||
managed_objs['structure_sphere'].show_preview('MESH')
|
||||
self.on_value_changed__center_radius()
|
||||
|
||||
|
||||
|
||||
####################
|
||||
# - Blender Registration
|
||||
####################
|
||||
|
|
|
@ -3,4 +3,3 @@
|
|||
####################
|
||||
BL_REGISTER = []
|
||||
BL_NODES = {}
|
||||
|
||||
|
|
|
@ -1,16 +1,14 @@
|
|||
#from . import math
|
||||
# from . import math
|
||||
from . import combine
|
||||
#from . import separate
|
||||
# from . import separate
|
||||
|
||||
BL_REGISTER = [
|
||||
# *math.BL_REGISTER,
|
||||
|
||||
# *math.BL_REGISTER,
|
||||
*combine.BL_REGISTER,
|
||||
#*separate.BL_REGISTER,
|
||||
# *separate.BL_REGISTER,
|
||||
]
|
||||
BL_NODES = {
|
||||
# **math.BL_NODES,
|
||||
|
||||
# **math.BL_NODES,
|
||||
**combine.BL_NODES,
|
||||
#**separate.BL_NODES,
|
||||
# **separate.BL_NODES,
|
||||
}
|
||||
|
|
|
@ -10,152 +10,143 @@ from .. import base
|
|||
|
||||
MAX_AMOUNT = 20
|
||||
|
||||
|
||||
class CombineNode(base.MaxwellSimNode):
|
||||
node_type = ct.NodeType.Combine
|
||||
bl_label = "Combine"
|
||||
#bl_icon = ...
|
||||
|
||||
bl_label = 'Combine'
|
||||
# bl_icon = ...
|
||||
|
||||
####################
|
||||
# - Sockets
|
||||
####################
|
||||
input_socket_sets = {
|
||||
"Maxwell Sources": {},
|
||||
"Maxwell Structures": {},
|
||||
"Maxwell Monitors": {},
|
||||
"Real 3D Vector": {
|
||||
f"x_{i}": sockets.RealNumberSocketDef()
|
||||
for i in range(3)
|
||||
'Maxwell Sources': {},
|
||||
'Maxwell Structures': {},
|
||||
'Maxwell Monitors': {},
|
||||
'Real 3D Vector': {
|
||||
f'x_{i}': sockets.RealNumberSocketDef() for i in range(3)
|
||||
},
|
||||
#"Point 3D": {
|
||||
# axis: sockets.PhysicalLengthSocketDef()
|
||||
# for i, axis in zip(
|
||||
# range(3),
|
||||
# ["x", "y", "z"]
|
||||
# )
|
||||
#},
|
||||
#"Size 3D": {
|
||||
# axis_key: sockets.PhysicalLengthSocketDef()
|
||||
# for i, axis_key, axis_label in zip(
|
||||
# range(3),
|
||||
# ["x_size", "y_size", "z_size"],
|
||||
# ["X Size", "Y Size", "Z Size"],
|
||||
# )
|
||||
#},
|
||||
# "Point 3D": {
|
||||
# axis: sockets.PhysicalLengthSocketDef()
|
||||
# for i, axis in zip(
|
||||
# range(3),
|
||||
# ["x", "y", "z"]
|
||||
# )
|
||||
# },
|
||||
# "Size 3D": {
|
||||
# axis_key: sockets.PhysicalLengthSocketDef()
|
||||
# for i, axis_key, axis_label in zip(
|
||||
# range(3),
|
||||
# ["x_size", "y_size", "z_size"],
|
||||
# ["X Size", "Y Size", "Z Size"],
|
||||
# )
|
||||
# },
|
||||
}
|
||||
output_socket_sets = {
|
||||
"Maxwell Sources": {
|
||||
"Sources": sockets.MaxwellSourceSocketDef(
|
||||
'Maxwell Sources': {
|
||||
'Sources': sockets.MaxwellSourceSocketDef(
|
||||
is_list=True,
|
||||
),
|
||||
},
|
||||
"Maxwell Structures": {
|
||||
"Structures": sockets.MaxwellStructureSocketDef(
|
||||
'Maxwell Structures': {
|
||||
'Structures': sockets.MaxwellStructureSocketDef(
|
||||
is_list=True,
|
||||
),
|
||||
},
|
||||
"Maxwell Monitors": {
|
||||
"Monitors": sockets.MaxwellMonitorSocketDef(
|
||||
'Maxwell Monitors': {
|
||||
'Monitors': sockets.MaxwellMonitorSocketDef(
|
||||
is_list=True,
|
||||
),
|
||||
},
|
||||
"Real 3D Vector": {
|
||||
"Real 3D Vector": sockets.Real3DVectorSocketDef(),
|
||||
'Real 3D Vector': {
|
||||
'Real 3D Vector': sockets.Real3DVectorSocketDef(),
|
||||
},
|
||||
#"Point 3D": {
|
||||
# "3D Point": sockets.PhysicalPoint3DSocketDef(),
|
||||
#},
|
||||
#"Size 3D": {
|
||||
# "3D Size": sockets.PhysicalSize3DSocketDef(),
|
||||
#},
|
||||
# "Point 3D": {
|
||||
# "3D Point": sockets.PhysicalPoint3DSocketDef(),
|
||||
# },
|
||||
# "Size 3D": {
|
||||
# "3D Size": sockets.PhysicalSize3DSocketDef(),
|
||||
# },
|
||||
}
|
||||
|
||||
|
||||
amount: bpy.props.IntProperty(
|
||||
name="# Objects to Combine",
|
||||
description="Amount of Objects to Combine",
|
||||
name='# Objects to Combine',
|
||||
description='Amount of Objects to Combine',
|
||||
default=1,
|
||||
min=1,
|
||||
max=MAX_AMOUNT,
|
||||
update=lambda self, context: self.sync_prop("amount", context)
|
||||
update=lambda self, context: self.sync_prop('amount', context),
|
||||
)
|
||||
|
||||
|
||||
####################
|
||||
# - Draw
|
||||
####################
|
||||
def draw_props(self, context, layout):
|
||||
layout.prop(self, "amount", text="#")
|
||||
|
||||
layout.prop(self, 'amount', text='#')
|
||||
|
||||
####################
|
||||
# - Output Socket Computation
|
||||
####################
|
||||
@base.computes_output_socket(
|
||||
"Real 3D Vector",
|
||||
input_sockets={"x_0", "x_1", "x_2"}
|
||||
'Real 3D Vector', input_sockets={'x_0', 'x_1', 'x_2'}
|
||||
)
|
||||
def compute_real_3d_vector(self, input_sockets) -> sp.Expr:
|
||||
return sp.Matrix([input_sockets[f"x_{i}"] for i in range(3)])
|
||||
|
||||
return sp.Matrix([input_sockets[f'x_{i}'] for i in range(3)])
|
||||
|
||||
@base.computes_output_socket(
|
||||
"Sources",
|
||||
input_sockets={f"Source #{i}" for i in range(MAX_AMOUNT)},
|
||||
props={"amount"},
|
||||
'Sources',
|
||||
input_sockets={f'Source #{i}' for i in range(MAX_AMOUNT)},
|
||||
props={'amount'},
|
||||
)
|
||||
def compute_sources(self, input_sockets, props) -> sp.Expr:
|
||||
return [
|
||||
input_sockets[f"Source #{i}"]
|
||||
for i in range(props["amount"])
|
||||
]
|
||||
|
||||
return [input_sockets[f'Source #{i}'] for i in range(props['amount'])]
|
||||
|
||||
@base.computes_output_socket(
|
||||
"Structures",
|
||||
input_sockets={f"Structure #{i}" for i in range(MAX_AMOUNT)},
|
||||
props={"amount"},
|
||||
'Structures',
|
||||
input_sockets={f'Structure #{i}' for i in range(MAX_AMOUNT)},
|
||||
props={'amount'},
|
||||
)
|
||||
def compute_structures(self, input_sockets, props) -> sp.Expr:
|
||||
return [
|
||||
input_sockets[f"Structure #{i}"]
|
||||
for i in range(props["amount"])
|
||||
input_sockets[f'Structure #{i}'] for i in range(props['amount'])
|
||||
]
|
||||
|
||||
|
||||
@base.computes_output_socket(
|
||||
"Monitors",
|
||||
input_sockets={f"Monitor #{i}" for i in range(MAX_AMOUNT)},
|
||||
props={"amount"},
|
||||
'Monitors',
|
||||
input_sockets={f'Monitor #{i}' for i in range(MAX_AMOUNT)},
|
||||
props={'amount'},
|
||||
)
|
||||
def compute_monitors(self, input_sockets, props) -> sp.Expr:
|
||||
return [
|
||||
input_sockets[f"Monitor #{i}"]
|
||||
for i in range(props["amount"])
|
||||
]
|
||||
|
||||
|
||||
return [input_sockets[f'Monitor #{i}'] for i in range(props['amount'])]
|
||||
|
||||
####################
|
||||
# - Input Socket Compilation
|
||||
####################
|
||||
@base.on_value_changed(
|
||||
prop_name="active_socket_set",
|
||||
props={"active_socket_set", "amount"},
|
||||
prop_name='active_socket_set',
|
||||
props={'active_socket_set', 'amount'},
|
||||
)
|
||||
def on_value_changed__active_socket_set(self, props):
|
||||
if props["active_socket_set"] == "Maxwell Sources":
|
||||
if props['active_socket_set'] == 'Maxwell Sources':
|
||||
self.loose_input_sockets = {
|
||||
f"Source #{i}": sockets.MaxwellSourceSocketDef()
|
||||
for i in range(props["amount"])
|
||||
f'Source #{i}': sockets.MaxwellSourceSocketDef()
|
||||
for i in range(props['amount'])
|
||||
}
|
||||
elif props["active_socket_set"] == "Maxwell Structures":
|
||||
elif props['active_socket_set'] == 'Maxwell Structures':
|
||||
self.loose_input_sockets = {
|
||||
f"Structure #{i}": sockets.MaxwellStructureSocketDef()
|
||||
for i in range(props["amount"])
|
||||
f'Structure #{i}': sockets.MaxwellStructureSocketDef()
|
||||
for i in range(props['amount'])
|
||||
}
|
||||
elif props["active_socket_set"] == "Maxwell Monitors":
|
||||
elif props['active_socket_set'] == 'Maxwell Monitors':
|
||||
self.loose_input_sockets = {
|
||||
f"Monitor #{i}": sockets.MaxwellMonitorSocketDef()
|
||||
for i in range(props["amount"])
|
||||
f'Monitor #{i}': sockets.MaxwellMonitorSocketDef()
|
||||
for i in range(props['amount'])
|
||||
}
|
||||
else:
|
||||
self.loose_input_sockets = {}
|
||||
|
||||
|
||||
@base.on_value_changed(
|
||||
prop_name="amount",
|
||||
prop_name='amount',
|
||||
)
|
||||
def on_value_changed__amount(self):
|
||||
self.on_value_changed__active_socket_set()
|
||||
|
@ -171,8 +162,4 @@ class CombineNode(base.MaxwellSimNode):
|
|||
BL_REGISTER = [
|
||||
CombineNode,
|
||||
]
|
||||
BL_NODES = {
|
||||
ct.NodeType.Combine: (
|
||||
ct.NodeCategory.MAXWELLSIM_UTILITIES
|
||||
)
|
||||
}
|
||||
BL_NODES = {ct.NodeType.Combine: (ct.NodeCategory.MAXWELLSIM_UTILITIES)}
|
||||
|
|
|
@ -7,68 +7,72 @@ from .... import contracts
|
|||
from .... import sockets
|
||||
from ... import base
|
||||
|
||||
|
||||
class WaveConverterNode(base.MaxwellSimTreeNode):
|
||||
node_type = contracts.NodeType.WaveConverter
|
||||
bl_label = "Wave Converter"
|
||||
#bl_icon = ...
|
||||
|
||||
bl_label = 'Wave Converter'
|
||||
# bl_icon = ...
|
||||
|
||||
####################
|
||||
# - Sockets
|
||||
####################
|
||||
input_sockets = {}
|
||||
input_socket_sets = {
|
||||
"freq_to_vacwl": {
|
||||
"freq": sockets.PhysicalFreqSocketDef(
|
||||
label="Freq",
|
||||
'freq_to_vacwl': {
|
||||
'freq': sockets.PhysicalFreqSocketDef(
|
||||
label='Freq',
|
||||
),
|
||||
},
|
||||
"vacwl_to_freq": {
|
||||
"vacwl": sockets.PhysicalVacWLSocketDef(
|
||||
label="Vac WL",
|
||||
'vacwl_to_freq': {
|
||||
'vacwl': sockets.PhysicalVacWLSocketDef(
|
||||
label='Vac WL',
|
||||
),
|
||||
},
|
||||
}
|
||||
output_sockets = {}
|
||||
output_socket_sets = {
|
||||
"freq_to_vacwl": {
|
||||
"vacwl": sockets.PhysicalVacWLSocketDef(
|
||||
label="Vac WL",
|
||||
'freq_to_vacwl': {
|
||||
'vacwl': sockets.PhysicalVacWLSocketDef(
|
||||
label='Vac WL',
|
||||
),
|
||||
},
|
||||
"vacwl_to_freq": {
|
||||
"freq": sockets.PhysicalFreqSocketDef(
|
||||
label="Freq",
|
||||
'vacwl_to_freq': {
|
||||
'freq': sockets.PhysicalFreqSocketDef(
|
||||
label='Freq',
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
####################
|
||||
# - Output Socket Computation
|
||||
####################
|
||||
@base.computes_output_socket("freq")
|
||||
@base.computes_output_socket('freq')
|
||||
def compute_freq(self: contracts.NodeTypeProtocol) -> sp.Expr:
|
||||
vac_speed_of_light = sc.constants.speed_of_light * spu.meter/spu.second
|
||||
|
||||
vacwl = self.compute_input("vacwl")
|
||||
|
||||
vac_speed_of_light = (
|
||||
sc.constants.speed_of_light * spu.meter / spu.second
|
||||
)
|
||||
|
||||
vacwl = self.compute_input('vacwl')
|
||||
|
||||
return spu.convert_to(
|
||||
vac_speed_of_light / vacwl,
|
||||
spu.hertz,
|
||||
)
|
||||
|
||||
@base.computes_output_socket("vacwl")
|
||||
|
||||
@base.computes_output_socket('vacwl')
|
||||
def compute_vacwl(self: contracts.NodeTypeProtocol) -> sp.Expr:
|
||||
vac_speed_of_light = sc.constants.speed_of_light * spu.meter/spu.second
|
||||
|
||||
freq = self.compute_input("freq")
|
||||
|
||||
vac_speed_of_light = (
|
||||
sc.constants.speed_of_light * spu.meter / spu.second
|
||||
)
|
||||
|
||||
freq = self.compute_input('freq')
|
||||
|
||||
return spu.convert_to(
|
||||
vac_speed_of_light / freq,
|
||||
spu.meter,
|
||||
)
|
||||
|
||||
|
||||
|
||||
####################
|
||||
# - Blender Registration
|
||||
####################
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue