Compare commits

..

10 Commits

199 changed files with 1936 additions and 3536 deletions

View File

@ -20,7 +20,7 @@ repos:
- repo: https://github.com/astral-sh/ruff-pre-commit - repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version. # Ruff version.
rev: v0.4.3 rev: v0.6.8
hooks: hooks:
# ruff lint # ruff lint
#- id: ruff #- id: ruff
@ -29,8 +29,18 @@ repos:
- id: ruff-format - id: ruff-format
- repo: https://github.com/commitizen-tools/commitizen - repo: https://github.com/commitizen-tools/commitizen
rev: master rev: v3.29.1
hooks: hooks:
- id: commitizen - id: commitizen
- id: commitizen-branch - id: commitizen-branch
stages: [push] stages: [push]
- repo: local
hooks:
- id: pytest
name: pytest
entry: ./.venv/bin/pytest tests
language: system
types: [python]
pass_filenames: false
always_run: true

View File

@ -14,221 +14,88 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# blender_maxwell """A visual DSL for electromagnetic simulation design and analysis implemented as a Blender node editor."""
# Copyright (C) 2024 blender_maxwell Project Contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""A Blender-based system for electromagnetic simulation design and analysis, with deep Tidy3D integration. from functools import reduce
# `bl_info` from . import contracts as ct
`bl_info` declares information about the addon to Blender.
However, it is not _dynamically_ read: Blender traverses it using `ast.parse`. # from . import node_trees
This makes it difficult to synchronize `bl_info` with the project's `pyproject.toml`. from . import operators, preferences, registration
As a workaround, **the addon zip-packer will replace `bl_info` entries**. from .utils import logger
The following `bl_info` entries are currently replaced when the ZIP is built: log = logger.get(__name__)
- `description`: To match the description in `pyproject.toml`.
- `version`: To match the version in `pyproject.toml`.
For more information, see `scripts.pack.BL_INFO_REPLACEMENTS`.
**NOTE**: The find/replace procedure is "dumb" (aka. no regex, no `ast` traversal, etc.).
This is surprisingly robust, so long as use of the deterministic code-formatter `ruff fmt` is enforced.
Still. Be careful around `bl_info`.
Attributes:
bl_info: Information about the addon declared to Blender.
BL_REGISTER_BEFORE_DEPS: Blender classes to register before dependencies are verified as installed.
BL_HOTKEYS: Blender keymap item defs to register before dependencies are verified as installed.
"""
from pathlib import Path
from .nodeps.utils import simple_logger
# Initialize Logging Defaults
## Initial logger settings (ex. log level) must be set somehow.
## The Addon ZIP-packer makes this decision, and packs it into files.
## AddonPreferences will, once loaded, override this.
_PATH_ADDON_ROOT = Path(__file__).resolve().parent
_PATH_BOOTSTRAP_LOG_LEVEL = _PATH_ADDON_ROOT / '.bootstrap_log_level'
with _PATH_BOOTSTRAP_LOG_LEVEL.open('r') as f:
_BOOTSTRAP_LOG_LEVEL = int(f.read().strip())
simple_logger.init_simple_logger_defaults(console_level=_BOOTSTRAP_LOG_LEVEL)
# Import Statements
import bpy # noqa: E402
from . import contracts as ct # noqa: E402
from . import preferences, registration # noqa: E402
from .nodeps import operators as nodeps_operators # noqa: E402
from .nodeps.utils import pydeps # noqa: E402
log = simple_logger.get(__name__)
#################### ####################
# - Load and Register Addon # - Load and Register Addon
#################### ####################
BL_REGISTER_BEFORE_DEPS: list[ct.BLClass] = [ BL_REGISTER: list[ct.BLClass] = [
*nodeps_operators.BL_REGISTER,
*preferences.BL_REGISTER,
]
## TODO: BL_HANDLERS and BL_SOCKET_DEFS
BL_HOTKEYS_BEFORE_DEPS: list[ct.KeymapItemDef] = [
*nodeps_operators.BL_HOTKEYS,
]
def load_main_blclasses(path_pydeps: Path) -> list[ct.BLClass]:
"""Imports all addon classes that rely on Python dependencies.
Notes:
`sys.path` is modified while executing this function.
Parameters:
path_pydeps: The path to the Python dependencies.
Returns:
An ordered list of Blender classes to register.
"""
with pydeps.importable_addon_deps(path_pydeps):
from . import assets, node_trees, operators
return [
*operators.BL_REGISTER, *operators.BL_REGISTER,
*assets.BL_REGISTER, # *node_trees.BL_REGISTER,
*node_trees.BL_REGISTER,
] ]
BL_HANDLERS: ct.BLHandlers = reduce(
lambda a, b: a + b,
[
operators.BL_HANDLERS,
# node_trees.BL_HANDLERS,
],
ct.BLHandlers(),
)
def load_main_blhotkeys(path_deps: Path) -> list[ct.KeymapItemDef]: BL_KEYMAP_ITEMS: list[ct.BLKeymapItem] = [
"""Imports all keymap item defs that rely on Python dependencies. *operators.BL_KEYMAP_ITEMS,
# node_trees.BL_KEYMAP_ITEMS,
Notes:
`sys.path` is modified while executing this function.
Parameters:
path_pydeps: The path to the Python dependencies.
Returns:
An ordered list of Blender keymap item defs to register.
"""
with pydeps.importable_addon_deps(path_deps):
from . import assets, operators
return [
*operators.BL_HOTKEYS,
*assets.BL_HOTKEYS,
] ]
#################### ####################
# - Registration # - Registration
#################### ####################
@bpy.app.handlers.persistent
def manage_pydeps(*_):
# ct.addon.operator(
# ct.OperatorType.ManagePyDeps,
# 'INVOKE_DEFAULT',
# path_addon_pydeps='',
# path_addon_reqs='',
# )
log.debug('PyDeps: Analyzing Post-File Load')
ct.addon.prefs().on_addon_pydeps_changed(show_popup_if_deps_invalid=True)
def register() -> None: def register() -> None:
"""Implements a multi-stage addon registration, which accounts for Python dependency management. """Implements addon registration in a way that respects the availability of addon preferences and loggers.
# Multi-Stage Registration
The trouble is that many classes in our addon might require Python dependencies.
## Stage 1: Barebones Addon
Many classes in our addon might require Python dependencies.
However, they may not yet be installed.
To solve this bootstrapping problem in a streamlined manner, we only **guarantee** the registration of a few key classes, including:
- `AddonPreferences`: The addon preferences provide an interface for the user to fix Python dependency problems, thereby triggering subsequent stages.
- `InstallPyDeps`: An operator that installs missing Python dependencies, using Blender's embeded `pip`.
- `UninstallPyDeps`: An operator that uninstalls Python dependencies.
**These classes provide just enough interface to help the user install the missing Python dependencies**.
## Stage 2: Declare Delayed Registration
We may not be able to register any classes that rely on Python dependencies.
However, we can use `registration.delay_registration()` to **delay the registration until it is determined that the Python dependencies are satisfied**.`
For now, we just pass a callback that will import + return a list of classes to register (`load_main_blclasses()`) when the time comes.
## Stage 3: Trigger "PyDeps Changed"
The addon preferences is responsible for storing (and exposing to the user) the path to the Python dependencies.
Thus, the addon preferences method `on_addon_pydeps_changed()` has the responsibility for checking when the dependencies are valid, and running the delayed registrations (and any other delayed setup) in response.
In general, `on_addon_pydeps_changed()` runs whenever the PyDeps path is changed, but it can also be run manually.
As the last part of this process, that's exactly what `register()` does: Runs `on_addon_pydeps_changed()` manually.
Depending on the addon preferences (which persist), one of two things can happen:
1. **Deps Satisfied**: The addon will load without issue: The just-declared "delayed registrations" will run immediately, and all is well.
2. **Deps Not Satisfied**: The user must take action to fix the conflicts due to Python dependencies, before the addon can load. **A popup will show to help the user do so.
Notes: Notes:
Called by Blender when enabling the addon. Called by Blender when enabling the addon.
Raises:
RuntimeError: If addon preferences fail to register.
""" """
log.info('Commencing Registration of Addon: %s', ct.addon.NAME) log.info('Registering Addon Preferences: %s', ct.addon.NAME)
bpy.app.handlers.load_post.append(manage_pydeps) registration.register_classes(preferences.BL_REGISTER)
# Register Barebones Addon addon_prefs = ct.addon.prefs()
## Contains all no-dependency BLClasses: if addon_prefs is not None:
## - Contains AddonPreferences. # Update Loggers
## Contains all BLClasses from 'nodeps'. # - This updates existing loggers to use settings defined by preferences.
registration.register_classes(BL_REGISTER_BEFORE_DEPS) addon_prefs.on_addon_logging_changed()
registration.register_hotkeys(BL_HOTKEYS_BEFORE_DEPS)
# Delay Complete Registration until DEPS_SATISFIED log.info('Registering Addon: %s', ct.addon.NAME)
registration.delay_registration_until(
registration.BLRegisterEvent.DepsSatisfied,
then_register_classes=load_main_blclasses,
then_register_hotkeys=load_main_blhotkeys,
)
# Trigger PyDeps Check registration.register_classes(BL_REGISTER)
## Deps ARE OK: Delayed registration will trigger. registration.register_handlers(BL_HANDLERS)
## Deps NOT OK: User must fix the pydeps, then trigger this method. registration.register_keymaps(BL_KEYMAP_ITEMS)
ct.addon.prefs().on_addon_pydeps_changed()
log.info('Finished Registration of Addon: %s', ct.addon.NAME)
else:
msg = 'Addon preferences did not register for addon {ct.addon.NAME} - something is very wrong!'
raise RuntimeError(msg)
def unregister() -> None: def unregister() -> None:
"""Unregisters anything that was registered by the addon. """Unregisters anything that was registered by the addon.
Notes: Notes:
Run by Blender when disabling the addon. Run by Blender when disabling and/or uninstalling the addon.
This doesn't clean `sys.modules`. This doesn't clean `sys.modules`.
To fully revert to Blender's state before the addon was in use (especially various import-related caches in the Python process), Blender must be restarted. We rely on the hope that Blender has extension-extension module isolation.
""" """
log.info('Starting %s Unregister', ct.addon.NAME) log.info('Starting %s Unregister', ct.addon.NAME)
registration.unregister_keymaps()
registration.unregister_handlers()
registration.unregister_classes() registration.unregister_classes()
registration.unregister_hotkeys()
registration.clear_delayed_registrations()
log.info('Finished %s Unregister', ct.addon.NAME) log.info('Finished %s Unregister', ct.addon.NAME)

BIN
oscillode/assets/converters/tdsphere_to_kp.blend (Stored with Git LFS) 100644

Binary file not shown.

BIN
oscillode/assets/primitives/arrow.blend (Stored with Git LFS) 100644

Binary file not shown.

View File

@ -14,21 +14,7 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# blender_maxwell """Independent constants and types, which represent a kind of 'social contract' governing communication between all components of the addon."""
# Copyright (C) 2024 blender_maxwell Project Contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from . import addon from . import addon
from .bl import ( from .bl import (
@ -36,24 +22,30 @@ from .bl import (
BLColorRGBA, BLColorRGBA,
BLEnumElement, BLEnumElement,
BLEnumID, BLEnumID,
BLEventType,
BLEventValue,
BLIcon, BLIcon,
BLIconSet, BLIconSet,
BLIDStruct, BLIDStruct,
BLImportMethod, BLImportMethod,
BLKeymapItem,
BLModifierType, BLModifierType,
BLNodeTreeInterfaceID, BLNodeTreeInterfaceID,
BLOperatorStatus, BLOperatorStatus,
BLPropFlag, BLPropFlag,
BLRegionType, BLRegionType,
BLSpaceType, BLSpaceType,
KeymapItemDef,
ManagedObjName, ManagedObjName,
PresetName, PresetName,
PropName, PropName,
SocketName, SocketName,
) )
from .bl_types import BLEnumStrEnum from .bl_handlers import BLHandlers
from .bl_keymap import BLKeymapItem
from .icons import Icon
from .mobj_types import ManagedObjType
from .node_tree_types import (
NodeTreeType,
)
from .operator_types import ( from .operator_types import (
OperatorType, OperatorType,
) )
@ -67,6 +59,8 @@ __all__ = [
'BLColorRGBA', 'BLColorRGBA',
'BLEnumElement', 'BLEnumElement',
'BLEnumID', 'BLEnumID',
'BLEventType',
'BLEventValue',
'BLIcon', 'BLIcon',
'BLIconSet', 'BLIconSet',
'BLIDStruct', 'BLIDStruct',
@ -80,12 +74,15 @@ __all__ = [
'BLSpaceType', 'BLSpaceType',
'KeymapItemDef', 'KeymapItemDef',
'ManagedObjName', 'ManagedObjName',
'ManagedObjType',
'PresetName', 'PresetName',
'PropName', 'PropName',
'SocketName', 'SocketName',
'BLEnumStrEnum', 'BLHandlers',
'Icon',
'BLInstance', 'BLInstance',
'InstanceID', 'InstanceID',
'NodeTreeType',
'OperatorType', 'OperatorType',
'PanelType', 'PanelType',
] ]

View File

@ -14,105 +14,82 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# blender_maxwell """Basic 'single-source-of-truth' static and dynamic information about this addon.
# Copyright (C) 2024 blender_maxwell Project Contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import sys Information is mined both from `bpy`, and from the addon's bundled manifest file.
"""
import os
import tomllib import tomllib
import typing as typ
from pathlib import Path from pathlib import Path
import bpy import bpy
import bpy_restrict_state
PATH_ADDON_ROOT = Path(__file__).resolve().parent.parent PATH_ADDON_ROOT = Path(__file__).resolve().parent.parent
with (PATH_ADDON_ROOT / 'pyproject.toml').open('rb') as f: PATH_MANIFEST = PATH_ADDON_ROOT / 'blender_manifest.toml'
PROJ_SPEC = tomllib.load(f)
## bl_info is filled with PROJ_SPEC when packing the .zip.
NAME = PROJ_SPEC['project']['name'] with PATH_MANIFEST.open('rb') as f:
VERSION = PROJ_SPEC['project']['version'] MANIFEST = tomllib.load(f)
NAME: str = MANIFEST['id']
VERSION: str = MANIFEST['version']
#################### ####################
# - Assets # - Assets
#################### ####################
PATH_ASSETS = PATH_ADDON_ROOT / 'assets' PATH_ASSETS: Path = PATH_ADDON_ROOT / 'assets'
#################### if 'RUNNING_BLEXT_TESTS' in os.environ:
# - PyDeps Info PATH_CACHE: Path = (
#################### PATH_ADDON_ROOT.parent / 'dev' / 'local'
PATH_REQS = PATH_ADDON_ROOT / 'requirements.lock' ) ## TODO: Consult init_settings
DEFAULT_PATH_DEPS = PATH_ADDON_ROOT / '.addon_dependencies' else:
DEFAULT_PATH_DEPS.mkdir(exist_ok=True) PATH_CACHE: Path = Path(bpy.utils.extension_path_user(__package__, create=True)) # type: ignore[no-redef]
## requirements.lock is written when packing the .zip.
## By default, the addon pydeps are kept in the addon dir.
ORIGINAL_SYS_PATH = sys.path.copy()
####################
# - Local Addon Cache
####################
DEFAULT_ADDON_CACHE = PATH_ADDON_ROOT / '.addon_cache'
DEFAULT_ADDON_CACHE.mkdir(exist_ok=True)
PIP_INSTALL_LOG = DEFAULT_ADDON_CACHE / 'pip_install.log'
#################### ####################
# - Dynamic Addon Information # - Dynamic Addon Information
#################### ####################
def is_loading() -> bool: def operator(
"""Checks whether the addon is currently loading. name: str, *operator_args: list[typ.Any], **operator_kwargs: dict[str, typ.Any]
) -> None:
"""Convenienve method to call an operator from this addon.
While an addon is loading, `bpy.context` is temporarily very limited. This avoids having to use `bpy.ops.<addon_name>.operator_name`, which isn't generic.
For example, operators can't run while the addon is loading.
By checking whether `bpy.context` is limited like this, we can determine whether the addon is currently loading. Only operators from this addon may be called using this method.
Notes: Raises:
Since `bpy_restrict_state._RestrictContext` is a very internal thing, this function may be prone to breakage on Blender updates. ValueError: If an addon from outside this operator is attempted to be used.
**Keep an eye out**!
Returns:
Whether the addon has been fully loaded, such that `bpy.context` is fully accessible.
""" """
return isinstance(bpy.context, bpy_restrict_state._RestrictContext)
def operator(name: str, *operator_args, **operator_kwargs) -> None:
# Parse Operator Name # Parse Operator Name
operator_namespace, operator_name = name.split('.') operator_namespace, operator_name = name.split('.')
if operator_namespace != NAME: if operator_namespace != NAME:
msg = f'Tried to call operator {operator_name}, but addon operators may only use the addon operator namespace "{operator_namespace}.<name>"' msg = f'Tried to call operator {operator_name}, but addon operators may only use the addon operator namespace "{operator_namespace}.<name>"'
raise RuntimeError(msg) raise ValueError(msg)
# Addon Not Loading: Run Operator # Run Operator
if not is_loading(): bl_operator = getattr(getattr(bpy.ops, NAME), operator_name)
operator = getattr(getattr(bpy.ops, NAME), operator_name) bl_operator(*operator_args, **operator_kwargs)
operator(*operator_args, **operator_kwargs) ## TODO: Can't we constrain 'name' to be an OperatorType somehow?
else:
msg = f'Tried to call operator "{operator_name}" while addon is loading'
raise RuntimeError(msg)
def prefs() -> bpy.types.AddonPreferences | None: def prefs() -> bpy.types.AddonPreferences | None:
if (addon := bpy.context.preferences.addons.get(NAME)) is None: """Retrieve the preferences of this addon, if they are available yet.
msg = 'Addon is not installed'
raise RuntimeError(msg)
Notes:
While registering the addon, one may wish to use the addon preferences.
This isn't possible - not even for default values.
Either a bad idea is at work, or `oscillode.utils.init_settings` should be consulted until the preferences are available.
Returns:
The addon preferences, if the addon is registered and loaded - otherwise None.
"""
addon = bpy.context.preferences.addons.get(NAME)
if addon is None:
return None
return addon.preferences return addon.preferences

View File

@ -14,21 +14,7 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# blender_maxwell """Explicit type annotations for Blender objects, making it easier to guarantee correctness in communications with Blender."""
# Copyright (C) 2024 blender_maxwell Project Contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import typing as typ import typing as typ
@ -109,7 +95,6 @@ BLIDStruct: typ.TypeAlias = (
| bpy.types.WorkSpace | bpy.types.WorkSpace
| bpy.types.World | bpy.types.World
) )
BLKeymapItem: typ.TypeAlias = typ.Any ## TODO: Better Type
BLPropFlag: typ.TypeAlias = typ.Literal[ BLPropFlag: typ.TypeAlias = typ.Literal[
'HIDDEN', 'HIDDEN',
'SKIP_SAVE', 'SKIP_SAVE',
@ -126,6 +111,24 @@ BLColorRGBA = tuple[float, float, float, float]
#################### ####################
# - Operators # - Operators
#################### ####################
BLRegionType: typ.TypeAlias = typ.Literal[
'WINDOW',
'HEADER',
'CHANNELS',
'TEMPORARY',
'UI',
'TOOLS',
'TOOL_PROPS',
'ASSET_SHELF',
'ASSET_SHELF_HEADER',
'PREVIEW',
'HUD',
'NAVIGATION_BAR',
'EXECUTE',
'FOOTER',
'TOOL_HEADER',
'XR',
]
BLSpaceType: typ.TypeAlias = typ.Literal[ BLSpaceType: typ.TypeAlias = typ.Literal[
'EMPTY', 'EMPTY',
'VIEW_3D', 'VIEW_3D',
@ -147,32 +150,151 @@ BLSpaceType: typ.TypeAlias = typ.Literal[
'SPREADSHEET', 'SPREADSHEET',
'PREFERENCES', 'PREFERENCES',
] ]
BLRegionType: typ.TypeAlias = typ.Literal[
'WINDOW',
'HEADER',
'CHANNELS',
'TEMPORARY',
'UI',
'TOOLS',
'TOOL_PROPS',
'ASSET_SHELF',
'ASSET_SHELF_HEADER',
'PREVIEW',
'HUD',
'NAVIGATION_BAR',
'EXECUTE',
'FOOTER',
'TOOL_HEADER',
'XR',
]
BLOperatorStatus: typ.TypeAlias = set[ BLOperatorStatus: typ.TypeAlias = set[
typ.Literal['RUNNING_MODAL', 'CANCELLED', 'FINISHED', 'PASS_THROUGH', 'INTERFACE'] typ.Literal['RUNNING_MODAL', 'CANCELLED', 'FINISHED', 'PASS_THROUGH', 'INTERFACE']
] ]
####################
# - Operators
####################
## TODO: Write the rest in.
BLEventType: typ.TypeAlias = typ.Literal[
'NONE',
'LEFTMOUSE',
'MIDDLEMOUSE',
'RIGHTMOUSE',
'BUTTON4MOUSE',
'BUTTON5MOUSE',
'BUTTON6MOUSE',
'BUTTON7MOUSE',
'PEN',
'ERASOR',
'MOUSEMOVE',
'INBETWEEN_MOUSEMOVE',
'TRACKPADPAN',
'TRACKPADZOOM',
'MOUSEROTATE',
'MOUSESMARTZOOM',
'WHEELUPMOUSE',
'WHEELDOWNMOUSE',
'WHEELINMOUSE',
'WHEELOUTMOUSE',
'A',
'B',
'C',
'D',
'E',
'F',
'G',
'H',
'I',
'J',
'K',
'L',
'M',
'N',
'O',
'P',
'Q',
'R',
'S',
'T',
'U',
'V',
'W',
'X',
'Y',
'Z',
'ZERO',
'ONE',
'TWO',
'THREE',
'FOUR',
'FIVE',
'SIX',
'SEVEN',
'EIGHT',
'NINE',
'LEFT_CTRL',
'LEFT_ALT',
'LEFT_SHIFT',
'RIGHT_ALT',
'RIGHT_CTRL',
'RIGHT_SHIFT',
'ESC',
'TAB',
'RET', ## Enter
'SPACE',
'LINE_FEED',
'BACK_SPACE',
'DEL',
'SEMI_COLON',
'PERIOD',
'COMMA',
'QUOTE',
'ACCENT_GRAVE',
'MINUS',
'PLUS',
'SLASH',
'BACK_SLASH',
'EQUAL',
'LEFT_BRACKET',
'RIGHT_BRACKET',
'LEFT_ARROW',
'DOWN_ARROW',
'RIGHT_ARROW',
'UP_ARROW',
'NUMPAD_0',
'NUMPAD_1',
'NUMPAD_2',
'NUMPAD_3',
'NUMPAD_4',
'NUMPAD_5',
'NUMPAD_6',
'NUMPAD_7',
'NUMPAD_8',
'NUMPAD_9',
'NUMPAD_PERIOD',
'NUMPAD_SLASH',
'NUMPAD_ASTERIX',
'NUMPAD_MINUS',
'NUMPAD_PLUS',
'NUMPAD_ENTER',
'F1',
'F2',
'F3',
'F4',
'F5',
'F6',
'F7',
'F8',
'F9',
'F10',
'F11',
'F12',
'PAUSE',
'INSERT',
'HOME',
'PAGE_UP',
'PAGE_DOWN',
'END',
'MEDIA_PLAY',
'MEDIA_STOP',
'MEDIA_FIRST',
'MEDIA_LAST',
]
BLEventValue: typ.TypeAlias = typ.Literal[
'ANY',
'PRESS',
'RELEASE',
'CLICK',
'DOUBLE_CLICK',
'CLICK_DRAG',
'NOTHING',
]
#################### ####################
# - Addon Types # - Addon Types
#################### ####################
KeymapItemDef: typ.TypeAlias = typ.Any
ManagedObjName = str ManagedObjName = str
#################### ####################

View File

@ -0,0 +1,125 @@
# oscillode
# Copyright (C) 2024 oscillode Project Contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""Declares types for working with `bpy.app.handlers` callbacks."""
import typing as typ
import bpy
import pydantic as pyd
from oscillode.utils.staticproperty import staticproperty
BLHandler = typ.Callable[[], None]
BLHandlerWithFile = typ.Callable[[str], None]
BLHandlerWithRenderStats = typ.Callable[[typ.Any], None]
class BLHandlers(pyd.BaseModel):
"""Contains lists of handlers associated with this addon."""
animation_playback_post: tuple[BLHandler, ...] = ()
animation_playback_pre: tuple[BLHandler, ...] = ()
annotation_post: tuple[BLHandler, ...] = ()
annotation_pre: tuple[BLHandler, ...] = ()
composite_cancel: tuple[BLHandler, ...] = ()
composite_post: tuple[BLHandler, ...] = ()
composite_pre: tuple[BLHandler, ...] = ()
depsgraph_update_post: tuple[BLHandler, ...] = ()
depsgraph_update_pre: tuple[BLHandler, ...] = ()
frame_change_post: tuple[BLHandler, ...] = ()
frame_change_pre: tuple[BLHandler, ...] = ()
load_factory_preferences_post: tuple[BLHandler, ...] = ()
load_factory_startup_post: tuple[BLHandler, ...] = ()
load_post: tuple[BLHandlerWithFile, ...] = ()
load_post_fail: tuple[BLHandlerWithFile, ...] = ()
load_pre: tuple[BLHandlerWithFile, ...] = ()
object_bake_cancel: tuple[BLHandler, ...] = ()
object_bake_complete: tuple[BLHandler, ...] = ()
object_bake_pre: tuple[BLHandler, ...] = ()
redo_post: tuple[BLHandler, ...] = ()
redo_pre: tuple[BLHandler, ...] = ()
render_cancel: tuple[BLHandler, ...] = ()
render_complete: tuple[BLHandler, ...] = ()
render_init: tuple[BLHandler, ...] = ()
render_post: tuple[BLHandler, ...] = ()
render_pre: tuple[BLHandler, ...] = ()
render_stats: tuple[BLHandler, ...] = ()
## TODO: Verify these type signatures.
## TODO: A validator to check that all handlers are decorated with bpy.app.handlers.persistent
####################
# - Properties
####################
@staticproperty # type: ignore[arg-type]
def handler_categories() -> tuple[str, ...]: # type: ignore[misc]
"""Returns an immutable string sequence of handler categories."""
return (
'animation_playback_post',
'animation_playback_pre',
'annotation_post',
'annotation_pre',
'composite_cancel',
'composite_post',
'composite_pre',
'depsgraph_update_post',
'depsgraph_update_pre',
'frame_change_post',
'frame_change_pre',
'load_factory_preferences_post',
'load_factory_startup_post',
'load_post',
'load_post_fail',
'load_pre',
'object_bake_cancel',
'object_bake_complete',
'object_bake_pre',
'redo_post',
'redo_pre',
'render_cancel',
'render_complete',
'render_init',
'render_post',
'render_pre',
'render_stats',
)
####################
# - Merging
####################
def __add__(self, other: typ.Self) -> typ.Self:
"""Concatenate the handlers of two `BLHandlers` objects."""
return BLHandlers(
**{
hndl_cat: getattr(self, hndl_cat) + getattr(self, hndl_cat)
for hndl_cat in self.handler_categories
}
)
def register(self) -> None:
"""Registers all handlers declared by-category."""
for handler_category in BLHandlers.handler_categories:
for handler in getattr(self, handler_category):
getattr(bpy.app.handlers, handler_category).append(handler)
def unregister(self) -> None:
"""Unregisters only this addon's handlers from bpy.app.handlers."""
for handler_category in BLHandlers.handler_categories:
for handler in getattr(self, handler_category):
bpy_handlers = getattr(bpy.app.handlers, handler_category)
if handler in bpy_handlers:
bpy_handlers.remove(handler)

View File

@ -0,0 +1,60 @@
# oscillode
# Copyright (C) 2024 oscillode Project Contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""Declares types for working with `bpy.types.KeyMap`s."""
import bpy
import pydantic as pyd
from .bl import BLEventType, BLEventValue, BLSpaceType
from .operator_types import OperatorType
class BLKeymapItem(pyd.BaseModel):
"""Contains lists of handlers associated with this addon."""
operator: OperatorType
event_type: BLEventType
event_value: BLEventValue
shift: bool = False
ctrl: bool = False
alt: bool = False
key_modifier: BLEventType = 'NONE'
space_type: BLSpaceType = 'EMPTY'
def register(self, addon_keymap: bpy.types.KeyMap) -> bpy.types.KeyMapItem:
"""Registers this hotkey with an addon keymap.
Raises:
ValueError: If the `space_type` constraint of the addon keymap does not match the `space_type` constraint of this `BLKeymapItem`.
"""
if self.space_type == addon_keymap.space_type:
addon_keymap.keymap_items.new(
self.operator,
self.event_type,
self.event_value,
shift=self.shift,
ctrl=self.ctrl,
alt=self.alt,
key_modifier=self.key_modifier,
)
msg = f'Addon keymap space type {addon_keymap.space_type} does not match space_type of BLKeymapItem to register: {self}'
raise ValueError(msg)
## TODO: Check if space_type matches?

View File

@ -1,44 +0,0 @@
# oscillode
# Copyright (C) 2024 oscillode Project Contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# blender_maxwell
# Copyright (C) 2024 blender_maxwell Project Contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import typing as typ
####################
# - Blender Enum (w/EnumProperty support)
####################
class BLEnumStrEnum(typ.Protocol):
@staticmethod
def to_name(value: typ.Self) -> str: ...
@staticmethod
def to_icon(value: typ.Self) -> str: ...

View File

@ -14,26 +14,14 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# blender_maxwell """Provides an enum that semantically constrains the use of icons throughout the addon."""
# Copyright (C) 2024 blender_maxwell Project Contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import enum import enum
class Icon(enum.StrEnum): class Icon(enum.StrEnum):
"""Identifiers for icons used throughout this addon."""
# Node Tree # Node Tree
SimNodeEditor = 'MOD_SIMPLEDEFORM' SimNodeEditor = 'MOD_SIMPLEDEFORM'

View File

@ -14,28 +14,16 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# blender_maxwell """Provides identifiers for objects in Blender that are managed on behalf of the user, for use by the addon, in order to provide a safe and streamlined integration of more complex data representations that flow between the addon and Blender."""
# Copyright (C) 2024 blender_maxwell Project Contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import enum import enum
from blender_maxwell.utils import blender_type_enum from oscillode.utils import blender_type_enum
class ManagedObjType(blender_type_enum.BlenderTypeEnum): class ManagedObjType(blender_type_enum.BlenderTypeEnum):
"""Identifiers for 'managed objects', which encapsulates a particular Blender object and provides alternative semantics more suited to the needs of this addon."""
ManagedBLImage = enum.auto() ManagedBLImage = enum.auto()
ManagedBLCollection = enum.auto() ManagedBLCollection = enum.auto()

View File

@ -0,0 +1,28 @@
# oscillode
# Copyright (C) 2024 oscillode Project Contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""Provides identifiers for node trees defined by this addon."""
import enum
from oscillode.utils import blender_type_enum
@blender_type_enum.append_cls_name_to_values
class NodeTreeType(blender_type_enum.BlenderTypeEnum):
"""Identifiers for addon-defined node trees."""
MaxwellSim = enum.auto()

View File

@ -14,27 +14,12 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# blender_maxwell """Provides identifiers for Blender operators defined by this addon."""
# Copyright (C) 2024 blender_maxwell Project Contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""Defines Operator Types as an enum, making it easy for any part of the addon to refer to any operator."""
import enum import enum
from ..nodeps.utils import blender_type_enum from oscillode.utils import blender_type_enum
from .addon import NAME as ADDON_NAME from .addon import NAME as ADDON_NAME

View File

@ -14,27 +14,11 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# blender_maxwell
# Copyright (C) 2024 blender_maxwell Project Contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""Defines Panel Types as an enum, making it easy for any part of the addon to refer to any panel.""" """Defines Panel Types as an enum, making it easy for any part of the addon to refer to any panel."""
import enum import enum
from blender_maxwell.nodeps.utils import blender_type_enum from oscillode.utils import blender_type_enum
from .addon import NAME as ADDON_NAME from .addon import NAME as ADDON_NAME

View File

@ -14,29 +14,10 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# blender_maxwell """Provides various useful managed objects, which enables the clean and safe use of objects external to this addon."""
# Copyright (C) 2024 blender_maxwell Project Contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from .base import ManagedObj from .base import ManagedObj
# from .managed_bl_empty import ManagedBLEmpty
from .managed_bl_image import ManagedBLImage from .managed_bl_image import ManagedBLImage
# from .managed_bl_collection import ManagedBLCollection
# from .managed_bl_object import ManagedBLObject
from .managed_bl_mesh import ManagedBLMesh from .managed_bl_mesh import ManagedBLMesh
# from .managed_bl_volume import ManagedBLVolume # from .managed_bl_volume import ManagedBLVolume
@ -44,13 +25,8 @@ from .managed_bl_modifier import ManagedBLModifier
__all__ = [ __all__ = [
'ManagedObj', 'ManagedObj',
#'ManagedBLEmpty',
'ManagedBLImage', 'ManagedBLImage',
#'ManagedBLCollection',
#'ManagedBLObject',
'ManagedBLMesh', 'ManagedBLMesh',
#'ManagedBLVolume', #'ManagedBLVolume',
'ManagedBLModifier', 'ManagedBLModifier',
] ]
## REMEMBER: Add the appropriate entry to the bl_cache.DECODER

View File

@ -0,0 +1,125 @@
# oscillode
# Copyright (C) 2024 oscillode Project Contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""Provides the base protocol on which all ManagedObjs are derived, as well as explicit protocols describing common features supported by some `ManagedObjs`."""
import abc
import typing as typ
from blender_maxwell.utils import logger, serialize
from .. import contracts as ct
log = logger.get(__name__)
class ManagedObj(typ.Protocol):
"""A weak name-based reference to some kind of object external to this software.
While the object doesn't have to come from Blender's `bpy.types`, that is the driving motivation for this class: To encapsulate access to the powerful visual tools granted by Blender's 3D viewport, image editor, and UI.
Through extensive trial-and-error, this transparently-cached immediate-mode interface has emerged as the best compromise.
While not suited to all use cases, the `ManagedObj` paradigm is ideal for the common situation of 'node ownership of external logic'.
For example, a plotting node needs access to its own image buffer; however, for it to be displayable, that image buffer must be owned by Blender.
This is where `ManagedObj` provides the ability of the plotting node to "loosely own" a particular Blender image, which both allows the user to see/interact with the image as a typical Blender image, but also allows the node to drive ex. lifecycle (like removing the image plot when the node is deleted).
This approach of loose name-coupling is not perfect. To name a few examples:
- A particular challenge is that of identifying and namespacing managed datablocks. The user may, for example, change the name of an object - and what do we do then? Is generating new data under the now-vacant name a feature? Or is it a systematic memory leak backed by dangling fake-owned datablocks?
- The precise definition of "loose ownership" is itself a matter of taste. Stronger ownership presumptions allow for stronger guarantees and simpler caching - but at the cost of more brittle breaks when the underlying objects are manipulated in ways we don't expect.
- `.blend` persistance is not the default when it comes to certain Blender objects, which raises deeper UX questions about how opinionated we should be about where users put data.
- Solving this doesn't help actually deal with data. There's a lot of very specific nuance in every single data pipeline, and that complexity is only added to watever this approach incurs.
This abstract base class serves to provide a few of the most basic of commonly-available operations.
In particular, implementations of `dump_as_msgspec`/`parse_as_msgspec` are enforced, for use with `oscillode.utils.serialize`.
This way, ex. opening a `.blend` file will allow a newly-deserialized `ManagedObj` to re-attach transparently to the also-persisted underlying datablock.
Parameters:
managed_obj_type: Enum identifier indicating which of the `ct.ManagedObjType` the instance should declare itself as.
"""
managed_obj_type: ct.ManagedObjType
@abc.abstractmethod
def __init__(self, name: ct.ManagedObjName, prev_name: str | None = None):
"""Initializes the managed object with a unique name.
Use `prev_name` to indicate that the managed object will initially be avaiable under `prev_name`, but that it should be renamed to `name`.
"""
####################
# - Properties
####################
@property
@abc.abstractmethod
def name(self) -> str:
"""Retrieve the name of the managed object."""
@name.setter
@abc.abstractmethod
def name(self, value: str) -> None:
"""Retrieve the name of the managed object."""
####################
# - Methods
####################
@abc.abstractmethod
def free(self) -> None:
"""Cleanup the resources managed by the managed object."""
@abc.abstractmethod
def hide_preview(self) -> None:
"""Hide any active preview of the managed object, if it exists, and if such an operation makes sense."""
####################
# - Serialization
####################
def dump_as_msgspec(self) -> serialize.NaiveRepresentation:
"""Bijectively transform this managed object into a 'naive representation' that uses only basic Python types, which may be serialized cleanly."""
return [
serialize.TypeID.ManagedObj,
self.__class__.__name__,
self.name,
]
@staticmethod
def parse_as_msgspec(obj: serialize.NaiveRepresentation) -> typ.Self:
"""Bijectively construct an instance of this managed object from the 'naive representation', which may have been deserialized into."""
return next(
subclass(obj[2])
for subclass in ManagedObj.__subclasses__()
if subclass.__name__ == obj[1]
)
####################
# - Support Flags
####################
class SupportsBlenderSelect(typ.Protocol):
"""Protocol guaranteeing the ability to select the object in Blender."""
def bl_select(self) -> None:
"""Select the managed object in Blender."""
class Supportsreview(typ.Protocol):
"""Protocol guaranteeing support for previewing the object."""
def show_preview(self) -> None:
"""Shows a preview of the managed object."""
def hide_preview(self) -> None:
"""Hide any active preview of the managed object, if it exists."""

View File

@ -32,12 +32,12 @@
import bpy import bpy
from blender_maxwell.utils import logger from oscillode.utils import logger
log = logger.get(__name__) log = logger.get(__name__)
MANAGED_COLLECTION_NAME = 'BLMaxwell' MANAGED_COLLECTION_NAME = 'Oscillode'
PREVIEW_COLLECTION_NAME = 'BLMaxwell Visible' PREVIEW_COLLECTION_NAME = 'Oscillode Visible'
#################### ####################

View File

@ -14,23 +14,7 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# blender_maxwell """Declares an object encapsulating access to a Blender image datablock from this addon."""
# Copyright (C) 2024 blender_maxwell Project Contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""Declares `ManagedBLImage`."""
import time import time
import typing as typ import typing as typ
@ -39,15 +23,17 @@ import bpy
import matplotlib.axis as mpl_ax import matplotlib.axis as mpl_ax
import numpy as np import numpy as np
from blender_maxwell.utils import image_ops, logger from oscillode.utils import image_ops, logger
from .. import contracts as ct from .. import contracts as ct
from . import base from . import base
log = logger.get(__name__) log = logger.get(__name__)
AREA_TYPE = 'IMAGE_EDITOR' AREA_TYPE: ct.BLSpaceType = (
SPACE_TYPE = 'IMAGE_EDITOR' 'IMAGE_EDITOR' ##TODO: Is SpaceType different than AreaType?
)
SPACE_TYPE: ct.BLSpaceType = 'IMAGE_EDITOR'
#################### ####################
@ -60,7 +46,7 @@ class ManagedBLImage(base.ManagedObj):
name: The name of the image. name: The name of the image.
""" """
managed_obj_type = ct.ManagedObjType.ManagedBLImage managed_obj_type: str = ct.ManagedObjType.ManagedBLImage
_bl_image_name: str _bl_image_name: str
def __init__(self, name: str, prev_name: str | None = None): def __init__(self, name: str, prev_name: str | None = None):
@ -186,6 +172,7 @@ class ManagedBLImage(base.ManagedObj):
#################### ####################
# - Methods # - Methods
#################### ####################
## TODO: Rename to show_preview()
def bl_select(self) -> None: def bl_select(self) -> None:
"""Selects the image by loading it into an on-screen UI area/space. """Selects the image by loading it into an on-screen UI area/space.

View File

@ -14,22 +14,6 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# blender_maxwell
# Copyright (C) 2024 blender_maxwell Project Contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import contextlib import contextlib
import bmesh import bmesh
@ -37,7 +21,7 @@ import bpy
import jax import jax
import numpy as np import numpy as np
from blender_maxwell.utils import logger from oscillode.utils import logger
from .. import contracts as ct from .. import contracts as ct
from . import base from . import base

View File

@ -14,31 +14,17 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# blender_maxwell
# Copyright (C) 2024 blender_maxwell Project Contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""A managed Blender modifier, associated with some Blender object.""" """A managed Blender modifier, associated with some Blender object."""
## TODO: Make a common core for modifier manipulation, then specify each modifier as a seperate ManagedObj.
import typing as typ import typing as typ
import bpy import bpy
import jax import jax
import numpy as np import numpy as np
from blender_maxwell.utils import logger from oscillode.utils import logger
from .. import bl_socket_map from .. import bl_socket_map
from .. import contracts as ct from .. import contracts as ct
@ -140,7 +126,7 @@ def write_modifier_geonodes(
modifier_altered = True modifier_altered = True
## TODO: More fine-grained alterations? ## TODO: More fine-grained alterations?
return modifier_altered # noqa: RET504 return modifier_altered
#################### ####################
@ -170,6 +156,8 @@ def write_modifier(
# - ManagedObj # - ManagedObj
#################### ####################
class ManagedBLModifier(base.ManagedObj): class ManagedBLModifier(base.ManagedObj):
"""Manages a particular modifier attached to a Blender mesh also managed by this construction."""
managed_obj_type = ct.ManagedObjType.ManagedBLModifier managed_obj_type = ct.ManagedObjType.ManagedBLModifier
_modifier_name: str | None = None _modifier_name: str | None = None
twin_bl_mesh: ManagedBLMesh | None = None twin_bl_mesh: ManagedBLMesh | None = None

View File

@ -14,32 +14,13 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# blender_maxwell
# Copyright (C) 2024 blender_maxwell Project Contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""Declares `ManagedBLText`.""" """Declares `ManagedBLText`."""
import time
import typing as typ import typing as typ
import bpy import bpy
import matplotlib.axis as mpl_ax
import numpy as np
from blender_maxwell.utils import image_ops, logger from oscillode.utils import logger
from .. import contracts as ct from .. import contracts as ct
from . import base from . import base
@ -54,10 +35,10 @@ SPACE_TYPE = 'IMAGE_EDITOR'
# - Managed BL Image # - Managed BL Image
#################### ####################
class ManagedBLText(base.ManagedObj): class ManagedBLText(base.ManagedObj):
"""Represents a Blender Image datablock, encapsulating various useful interactions with it. """Represents a Blender text datablock, encapsulating various useful interactions with it.
Attributes: Attributes:
name: The name of the image. name: The name of the text datablock.
""" """
managed_obj_type = ct.ManagedObjType.ManagedBLText managed_obj_type = ct.ManagedObjType.ManagedBLText

View File

@ -14,33 +14,32 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# blender_maxwell """Internal assets for use via the Python API and/or directly by the user as an asset library."""
# Copyright (C) 2024 blender_maxwell Project Contributors
# from functools import reduce
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by import oscillode.contracts as ct
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from . import geonodes from . import geonodes
BL_REGISTER = [ BL_REGISTER: list[ct.BLClass] = [
*geonodes.BL_REGISTER, *geonodes.BL_REGISTER,
] ]
BL_HOTKEYS = [ BL_HANDLERS: ct.BLHandlers = reduce(
*geonodes.BL_HOTKEYS, lambda a, b: a + b,
[
geonodes.BL_HANDLERS,
],
ct.BLHandlers(),
)
BL_KEYMAP_ITEMS: list[ct.BLKeymapItem] = [
*geonodes.BL_KEYMAP_ITEMS,
] ]
__all__ = [ __all__ = [
'BL_REGISTER', 'BL_REGISTER',
'BL_HANDLERS',
'BL_HOTKEYS', 'BL_HOTKEYS',
] ]

View File

@ -14,22 +14,6 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# blender_maxwell
# Copyright (C) 2024 blender_maxwell Project Contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""Provides for the linking and/or appending of geometry nodes trees from vendored libraries included in Blender maxwell.""" """Provides for the linking and/or appending of geometry nodes trees from vendored libraries included in Blender maxwell."""
import enum import enum
@ -37,8 +21,8 @@ from pathlib import Path
import bpy import bpy
from blender_maxwell import contracts as ct from oscillode import contracts as ct
from blender_maxwell.utils import logger from oscillode.utils import logger
log = logger.get(__name__) log = logger.get(__name__)
@ -48,17 +32,17 @@ log = logger.get(__name__)
#################### ####################
# GeoNodes Paths # GeoNodes Paths
## Internal ## Internal
GN_INTERNAL_PATH = ct.addon.PATH_ASSETS / 'internal' GN_INTERNAL_PATH: Path = ct.addon.PATH_ASSETS / 'internal'
GN_INTERNAL_INPUTS_PATH = GN_INTERNAL_PATH / 'input' GN_INTERNAL_INPUTS_PATH: Path = GN_INTERNAL_PATH / 'input'
GN_INTERNAL_SOURCES_PATH = GN_INTERNAL_PATH / 'source' GN_INTERNAL_SOURCES_PATH: Path = GN_INTERNAL_PATH / 'source'
GN_INTERNAL_STRUCTURES_PATH = GN_INTERNAL_PATH / 'structure' GN_INTERNAL_STRUCTURES_PATH: Path = GN_INTERNAL_PATH / 'structure'
GN_INTERNAL_MONITORS_PATH = GN_INTERNAL_PATH / 'monitor' GN_INTERNAL_MONITORS_PATH: Path = GN_INTERNAL_PATH / 'monitor'
GN_INTERNAL_SIMULATIONS_PATH = GN_INTERNAL_PATH / 'simulation' GN_INTERNAL_SIMULATIONS_PATH: Path = GN_INTERNAL_PATH / 'simulation'
## Structures ## Structures
GN_STRUCTURES_PATH = ct.addon.PATH_ASSETS / 'structures' GN_STRUCTURES_PATH: Path = ct.addon.PATH_ASSETS / 'structures'
GN_STRUCTURES_PRIMITIVES_PATH = GN_STRUCTURES_PATH / 'primitives' GN_STRUCTURES_PRIMITIVES_PATH: Path = GN_STRUCTURES_PATH / 'primitives'
GN_STRUCTURES_ARRAYS_PATH = GN_STRUCTURES_PATH / 'arrays' GN_STRUCTURES_ARRAYS_PATH: Path = GN_STRUCTURES_PATH / 'arrays'
class GeoNodes(enum.StrEnum): class GeoNodes(enum.StrEnum):
@ -121,7 +105,7 @@ class GeoNodes(enum.StrEnum):
ArrayRing = 'array_ring' ArrayRing = 'array_ring'
@property @property
def dedicated_node_type(self) -> ct.BLImportMethod: def dedicated_node_type(self) -> str: ## TODO: How to correlate to node_tree?
"""Deduces the denode type that implements a vendored GeoNodes tree (usually just "GeoNodes Structure"). """Deduces the denode type that implements a vendored GeoNodes tree (usually just "GeoNodes Structure").
Generally, "GeoNodes Structure' is the generic triangle-mesh node that can do everything. Generally, "GeoNodes Structure' is the generic triangle-mesh node that can do everything.
@ -401,7 +385,7 @@ class GeoNodesToStructureNode(bpy.types.Operator):
#################### ####################
def invoke( def invoke(
self, context: bpy.types.Context, _: bpy.types.Event self, context: bpy.types.Context, _: bpy.types.Event
) -> set[ct.BLOperatorStatus]: ) -> ct.BLOperatorStatus:
"""Commences the drag-and-drop of a GeoNodes asset. """Commences the drag-and-drop of a GeoNodes asset.
- Starts the modal timer, which listens for a mouse-release event. - Starts the modal timer, which listens for a mouse-release event.
@ -490,10 +474,10 @@ class GeoNodesToStructureNode(bpy.types.Operator):
## 3. We compute it manually, to avoid the jank. ## 3. We compute it manually, to avoid the jank.
node_location = get_view_location( node_location = get_view_location(
editor_region, editor_region,
[ (
event.mouse_x - editor_region.x, event.mouse_x - editor_region.x,
event.mouse_y - editor_region.y, event.mouse_y - editor_region.y,
], ),
context.preferences.system.ui_scale, context.preferences.system.ui_scale,
) )
@ -542,8 +526,8 @@ ASSET_LIB_SPECS: dict[str, Path] = {
} }
@bpy.app.handlers.persistent @bpy.app.handlers.persistent # type: ignore[misc]
def initialize_asset_libraries(_: bpy.types.Scene): def initialize_asset_libraries(_: bpy.types.Scene) -> None:
"""Before loading a `.blend` file, ensure that the WindowManager properties relied on by NodeAssetPanel are available. """Before loading a `.blend` file, ensure that the WindowManager properties relied on by NodeAssetPanel are available.
- Several asset libraries, defined under the global `ASSET_LIB_SPECS`, are added/replaced such that the name:path map is respected. - Several asset libraries, defined under the global `ASSET_LIB_SPECS`, are added/replaced such that the name:path map is respected.
@ -596,11 +580,11 @@ def initialize_asset_libraries(_: bpy.types.Scene):
) )
bpy.app.handlers.load_pre.append(initialize_asset_libraries)
BL_REGISTER = [ BL_REGISTER = [
NodeAssetPanel, NodeAssetPanel,
GeoNodesToStructureNode, GeoNodesToStructureNode,
] ]
BL_HOTKEYS = [] BL_HANDLERS: ct.BLHandlers = ct.BLHandlers(load_pre=(initialize_asset_libraries,))
BL_KEYMAP_ITEMS: list[ct.BLKeymapItem] = []

View File

@ -38,7 +38,6 @@ Attributes:
""" """
import bpy import bpy
from blender_maxwell.utils import logger as _logger from blender_maxwell.utils import logger as _logger
from . import contracts as ct from . import contracts as ct
@ -46,6 +45,8 @@ from . import sockets
log = _logger.get(__name__) log = _logger.get(__name__)
## TODO: Coordinate w/refactor of managed modifiers; for example, the contents of this file would be ideally specified as the implementation of a Protocol, for the particular case of this node tree.
#################### ####################
# - Blender -> Socket Def(s) # - Blender -> Socket Def(s)

View File

@ -38,6 +38,9 @@ import nodeitems_utils
from . import contracts as ct from . import contracts as ct
from .nodes import BL_NODES from .nodes import BL_NODES
## TODO: Completely refactor this file, and rename it to reflect that it doesn't define or implement any categories - it simply, dumbly, implements the menu.
## - Actually; we could delete this file by refactoring it, implementing the logic in a utils/ module, then running the dangling registration as a rote-registration matter in __init__.py.
DYNAMIC_SUBMENU_REGISTRATIONS = [] DYNAMIC_SUBMENU_REGISTRATIONS = []
@ -117,7 +120,7 @@ BL_REGISTER = [*DYNAMIC_SUBMENU_REGISTRATIONS] ## Must be run after, right now.
def menu_draw(self, context): def menu_draw(self, context):
if context.space_data.tree_type == ct.TreeType.MaxwellSim.value: if context.space_data.tree_type == ct.NodeTreeType.MaxwellSim.value:
for nodeitem_or_submenu in BL_NODE_CATEGORIES: for nodeitem_or_submenu in BL_NODE_CATEGORIES:
if isinstance(nodeitem_or_submenu, str): if isinstance(nodeitem_or_submenu, str):
submenu_id = nodeitem_or_submenu submenu_id = nodeitem_or_submenu

View File

@ -31,6 +31,8 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from blender_maxwell.contracts import ( from blender_maxwell.contracts import (
UNITS_BLENDER,
UNITS_TIDY3D,
BLClass, BLClass,
BLColorRGBA, BLColorRGBA,
BLEnumElement, BLEnumElement,
@ -45,8 +47,11 @@ from blender_maxwell.contracts import (
BLPropFlag, BLPropFlag,
BLRegionType, BLRegionType,
BLSpaceType, BLSpaceType,
Icon,
KeymapItemDef, KeymapItemDef,
ManagedObjName, ManagedObjName,
ManagedObjType,
NodeTreeType,
OperatorType, OperatorType,
PanelType, PanelType,
PresetName, PresetName,
@ -72,8 +77,6 @@ from .flow_kinds import (
ValueFlow, ValueFlow,
) )
from .flow_signals import FlowSignal from .flow_signals import FlowSignal
from .icons import Icon
from .mobj_types import ManagedObjType
from .node_types import NodeType from .node_types import NodeType
from .sim_types import ( from .sim_types import (
BoundCondType, BoundCondType,
@ -90,8 +93,6 @@ from .sim_types import (
) )
from .socket_colors import SOCKET_COLORS from .socket_colors import SOCKET_COLORS
from .socket_types import SocketType from .socket_types import SocketType
from .tree_types import TreeType
from .unit_systems import UNITS_BLENDER, UNITS_TIDY3D
__all__ = [ __all__ = [
'BLClass', 'BLClass',
@ -108,16 +109,17 @@ __all__ = [
'BLPropFlag', 'BLPropFlag',
'BLRegionType', 'BLRegionType',
'BLSpaceType', 'BLSpaceType',
'Icon',
'KeymapItemDef', 'KeymapItemDef',
'ManagedObjName', 'ManagedObjName',
'ManagedObjType',
'NodeTreeType',
'OperatorType', 'OperatorType',
'PanelType', 'PanelType',
'PresetName', 'PresetName',
'PropName', 'PropName',
'SocketName', 'SocketName',
'addon', 'addon',
'Icon',
'TreeType',
'SocketType', 'SocketType',
'SOCKET_COLORS', 'SOCKET_COLORS',
'SOCKET_SHAPES', 'SOCKET_SHAPES',
@ -139,7 +141,6 @@ __all__ = [
'manual_amp_time', 'manual_amp_time',
'NodeCategory', 'NodeCategory',
'NODE_CAT_LABELS', 'NODE_CAT_LABELS',
'ManagedObjType',
'FlowEvent', 'FlowEvent',
'ArrayFlow', 'ArrayFlow',
'CapabilitiesFlow', 'CapabilitiesFlow',

View File

@ -30,6 +30,8 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
## TODO: Unify w/category_types.py
from .category_types import NodeCategory as NC # noqa: N817 from .category_types import NodeCategory as NC # noqa: N817
NODE_CAT_LABELS = { NODE_CAT_LABELS = {

View File

@ -14,22 +14,6 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# blender_maxwell
# Copyright (C) 2024 blender_maxwell Project Contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import enum import enum
from blender_maxwell.utils import blender_type_enum from blender_maxwell.utils import blender_type_enum

View File

@ -14,23 +14,9 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# blender_maxwell """Socket base colors for Maxwell Sim sockets."""
# Copyright (C) 2024 blender_maxwell Project Contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from .socket_types import SocketType as ST from .socket_types import SocketType as ST # noqa: N817
## TODO: Don't just presume sRGB. ## TODO: Don't just presume sRGB.
SOCKET_COLORS = { SOCKET_COLORS = {

View File

@ -14,22 +14,6 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# blender_maxwell
# Copyright (C) 2024 blender_maxwell Project Contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import enum import enum
from blender_maxwell.utils import blender_type_enum from blender_maxwell.utils import blender_type_enum
@ -37,6 +21,8 @@ from blender_maxwell.utils import blender_type_enum
@blender_type_enum.append_cls_name_to_values @blender_type_enum.append_cls_name_to_values
class SocketType(blender_type_enum.BlenderTypeEnum): class SocketType(blender_type_enum.BlenderTypeEnum):
"""Identifiers for valid sockets in Maxwell Sim node trees."""
Expr = enum.auto() Expr = enum.auto()
# Base # Base

View File

@ -1,40 +0,0 @@
# oscillode
# Copyright (C) 2024 oscillode Project Contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# blender_maxwell
# Copyright (C) 2024 blender_maxwell Project Contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import enum
from blender_maxwell.utils import blender_type_enum
@blender_type_enum.append_cls_name_to_values
class TreeType(blender_type_enum.BlenderTypeEnum):
MaxwellSim = enum.auto()

View File

@ -1,111 +0,0 @@
# oscillode
# Copyright (C) 2024 oscillode Project Contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# blender_maxwell
# Copyright (C) 2024 blender_maxwell Project Contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import abc
import typing as typ
from blender_maxwell.utils import logger, serialize
from .. import contracts as ct
log = logger.get(__name__)
class ManagedObj(abc.ABC):
"""A weak name-based reference to some kind of object external to this software.
While the object doesn't have to come from Blender's `bpy.types`, that is admittedly the driving motivation for this class: To encapsulate access to the powerful visual tools granted by Blender's 3D viewport, image editor, and UI.
Through extensive testing, the functionality of an implicitly-cached, semi-strictly immediate-mode interface, demanding only a weakly-referenced name as persistance, has emerged (with all of the associated tradeoffs).
While not suited to all use cases, the `ManagedObj` paradigm is perfect for many situations where a node needs to "loosely own" something external and non-trivial.
Intriguingly, the precise definition of "loose" has grown to vary greatly between subclasses, as it ends of demonstrating itself to be a matter of taste more than determinism.
This abstract base class serves to provide a few of the most basic of commonly-available - especially the `dump_as_msgspec`/`parse_as_msgspec` methods that allow it to be persisted using `blender_maxwell.utils.serialize`.
Parameters:
managed_obj_type: Enum identifier indicating which of the `ct.ManagedObjType` the instance should declare itself as.
"""
managed_obj_type: ct.ManagedObjType
@abc.abstractmethod
def __init__(self, name: ct.ManagedObjName, prev_name: str | None = None):
"""Initializes the managed object with a unique name.
Use `prev_name` to indicate that the managed object will initially be avaiable under `prev_name`, but that it should be renamed to `name`.
"""
####################
# - Properties
####################
@property
@abc.abstractmethod
def name(self) -> str:
"""Retrieve the name of the managed object."""
@name.setter
@abc.abstractmethod
def name(self, value: str) -> None:
"""Retrieve the name of the managed object."""
####################
# - Methods
####################
@abc.abstractmethod
def free(self) -> None:
"""Cleanup the resources managed by the managed object."""
@abc.abstractmethod
def bl_select(self) -> None:
"""Select the managed object in Blender, if such an operation makes sense."""
@abc.abstractmethod
def hide_preview(self) -> None:
"""Hide any active preview of the managed object, if it exists, and if such an operation makes sense."""
####################
# - Serialization
####################
def dump_as_msgspec(self) -> serialize.NaiveRepresentation:
return [
serialize.TypeID.ManagedObj,
self.__class__.__name__,
self.name,
]
@staticmethod
def parse_as_msgspec(obj: serialize.NaiveRepresentation) -> typ.Self:
return next(
subclass(obj[2])
for subclass in ManagedObj.__subclasses__()
if subclass.__name__ == obj[1]
)

View File

@ -1,32 +0,0 @@
# oscillode
# Copyright (C) 2024 oscillode Project Contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# blender_maxwell
# Copyright (C) 2024 blender_maxwell Project Contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

View File

@ -14,33 +14,17 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# blender_maxwell # TODO: Factor this stuff into a dedicated utils/ module, so that this particular node tree becomes simple (aka. the deathly complicated logic is factored out, and can be ex. unit tested all on its own).
# Copyright (C) 2024 blender_maxwell Project Contributors ## - Then this file can focus on what is special about a Maxwell Sim node tree, as opposed to the lower-level details of how we've chosen to structure our node trees in general.
# ## - Right now there may not be a distinction. And there may never be. But it's a healthy way to think about the problem.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import contextlib
import functools
import queue import queue
import typing as typ import typing as typ
import bpy import bpy
from blender_maxwell.utils import logger
from blender_maxwell.utils import logger, serialize
from . import contracts as ct from . import contracts as ct
from .managed_objs.managed_bl_image import ManagedBLImage
log = logger.get(__name__) log = logger.get(__name__)
@ -320,7 +304,7 @@ class MaxwellSimTree(bpy.types.NodeTree):
In general, only one `MaxwellSimTree` should be active at a time. In general, only one `MaxwellSimTree` should be active at a time.
""" """
bl_idname = ct.TreeType.MaxwellSim.value bl_idname = ct.NodeTreeType.MaxwellSim.value
bl_label = 'Maxwell Sim Editor' bl_label = 'Maxwell Sim Editor'
bl_icon = ct.Icon.SimNodeEditor bl_icon = ct.Icon.SimNodeEditor
@ -328,6 +312,8 @@ class MaxwellSimTree(bpy.types.NodeTree):
default=True, default=True,
) )
viewer_node_type: ct.NodeType = ct.NodeType.Viewer
#################### ####################
# - Init Methods # - Init Methods
#################### ####################
@ -552,7 +538,8 @@ def populate_missing_persistence(_) -> None:
for node_tree in [ for node_tree in [
_node_tree _node_tree
for _node_tree in bpy.data.node_groups for _node_tree in bpy.data.node_groups
if _node_tree.bl_idname == ct.TreeType.MaxwellSim.value and _node_tree.is_active if _node_tree.bl_idname == ct.NodeTreeType.MaxwellSim.value
and _node_tree.is_active
]: ]:
log.debug( log.debug(
'%s: Regenerating Dynamic Field Persistance for NodeTree nodes/sockets', '%s: Regenerating Dynamic Field Persistance for NodeTree nodes/sockets',

View File

@ -14,22 +14,6 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# blender_maxwell
# Copyright (C) 2024 blender_maxwell Project Contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from . import ( from . import (
analysis, analysis,
inputs, inputs,

View File

@ -41,7 +41,6 @@ import jax.numpy as jnp
import sympy.physics.units as spu import sympy.physics.units as spu
import tidy3d as td import tidy3d as td
import xarray import xarray
from blender_maxwell.utils import bl_cache, logger, sim_symbols from blender_maxwell.utils import bl_cache, logger, sim_symbols
from ... import contracts as ct from ... import contracts as ct

View File

@ -37,7 +37,6 @@ import typing as typ
import bpy import bpy
import sympy as sp import sympy as sp
from blender_maxwell.utils import bl_cache, logger, sim_symbols from blender_maxwell.utils import bl_cache, logger, sim_symbols
from blender_maxwell.utils import sympy_extra as spux from blender_maxwell.utils import sympy_extra as spux

View File

@ -35,7 +35,6 @@
import typing as typ import typing as typ
import bpy import bpy
from blender_maxwell.utils import bl_cache, logger from blender_maxwell.utils import bl_cache, logger
from blender_maxwell.utils import sympy_extra as spux from blender_maxwell.utils import sympy_extra as spux

View File

@ -38,7 +38,6 @@ See `blender_maxwell.maxwell_sim_nodes.math_system` for the actual mathematics i
import typing as typ import typing as typ
import bpy import bpy
from blender_maxwell.utils import bl_cache, logger from blender_maxwell.utils import bl_cache, logger
from blender_maxwell.utils import sympy_extra as spux from blender_maxwell.utils import sympy_extra as spux

View File

@ -39,7 +39,6 @@ import bpy
import jax import jax
import jax.numpy as jnp import jax.numpy as jnp
import numpy as np import numpy as np
from blender_maxwell.utils import bl_cache, logger, sim_symbols from blender_maxwell.utils import bl_cache, logger, sim_symbols
from .... import contracts as ct from .... import contracts as ct

View File

@ -37,7 +37,6 @@ import typing as typ
import bpy import bpy
import sympy as sp import sympy as sp
from blender_maxwell.utils import bl_cache, logger, sim_symbols from blender_maxwell.utils import bl_cache, logger, sim_symbols
from blender_maxwell.utils import sympy_extra as spux from blender_maxwell.utils import sympy_extra as spux

View File

@ -38,10 +38,9 @@ import jaxtyping as jtyp
import matplotlib.axis as mpl_ax import matplotlib.axis as mpl_ax
import sympy as sp import sympy as sp
import sympy.physics.units as spu import sympy.physics.units as spu
from frozendict import frozendict
from blender_maxwell.utils import bl_cache, image_ops, logger, sim_symbols from blender_maxwell.utils import bl_cache, image_ops, logger, sim_symbols
from blender_maxwell.utils import sympy_extra as spux from blender_maxwell.utils import sympy_extra as spux
from frozendict import frozendict
from ... import contracts as ct from ... import contracts as ct
from ... import managed_objs, sockets from ... import managed_objs, sockets

View File

@ -43,7 +43,6 @@ from collections import defaultdict
from types import MappingProxyType from types import MappingProxyType
import bpy import bpy
from blender_maxwell.utils import bl_cache, bl_instance, logger from blender_maxwell.utils import bl_cache, bl_instance, logger
from blender_maxwell.utils import sympy_extra as spux from blender_maxwell.utils import sympy_extra as spux
@ -1196,7 +1195,7 @@ class MaxwellSimNode(bpy.types.Node, bl_instance.BLInstance):
Returns: Returns:
Whether or not the node can be instantiated within the given node tree. Whether or not the node can be instantiated within the given node tree.
""" """
return node_tree.bl_idname == ct.TreeType.MaxwellSim.value return node_tree.bl_idname == ct.NodeTreeType.MaxwellSim
def init(self, _: bpy.types.Context) -> None: def init(self, _: bpy.types.Context) -> None:
"""Initialize the node instance, including ID, name, socket, presets, and the execution of any `on_value_changed` methods with the `run_on_init` keyword set. """Initialize the node instance, including ID, name, socket, presets, and the execution of any `on_value_changed` methods with the `run_on_init` keyword set.

View File

@ -36,14 +36,11 @@ import typing as typ
import uuid import uuid
from collections import defaultdict from collections import defaultdict
from fractions import Fraction from fractions import Fraction
from types import MappingProxyType
import bpy import bpy
import jax import jax
import numpy as np import numpy as np
import pydantic as pyd import pydantic as pyd
import sympy as sp
from blender_maxwell.utils import logger from blender_maxwell.utils import logger
from blender_maxwell.utils import sympy_extra as spux from blender_maxwell.utils import sympy_extra as spux
from blender_maxwell.utils.frozendict import FrozenDict, frozendict from blender_maxwell.utils.frozendict import FrozenDict, frozendict

View File

@ -36,10 +36,9 @@ import typing as typ
import bpy import bpy
import sympy as sp import sympy as sp
from frozendict import frozendict
from blender_maxwell.utils import bl_cache, sci_constants, sim_symbols from blender_maxwell.utils import bl_cache, sci_constants, sim_symbols
from blender_maxwell.utils import sympy_extra as spux from blender_maxwell.utils import sympy_extra as spux
from frozendict import frozendict
from .... import contracts as ct from .... import contracts as ct
from .... import sockets from .... import sockets

View File

@ -38,7 +38,6 @@ from fractions import Fraction
import bpy import bpy
import sympy as sp import sympy as sp
from blender_maxwell.utils import bl_cache, logger, sim_symbols from blender_maxwell.utils import bl_cache, logger, sim_symbols
from blender_maxwell.utils import sympy_extra as spux from blender_maxwell.utils import sympy_extra as spux

View File

@ -39,7 +39,6 @@ from pathlib import Path
import bpy import bpy
import sympy as sp import sympy as sp
import tidy3d as td import tidy3d as td
from blender_maxwell.utils import bl_cache, logger, sim_symbols from blender_maxwell.utils import bl_cache, logger, sim_symbols
from blender_maxwell.utils import sympy_extra as spux from blender_maxwell.utils import sympy_extra as spux

View File

@ -37,7 +37,6 @@ from pathlib import Path
import bpy import bpy
import tidy3d as td import tidy3d as td
from blender_maxwell.utils import bl_cache, logger from blender_maxwell.utils import bl_cache, logger
from .... import contracts as ct from .... import contracts as ct

View File

@ -37,7 +37,6 @@ import typing as typ
import bpy import bpy
import sympy as sp import sympy as sp
import sympy.physics.units as spu import sympy.physics.units as spu
from blender_maxwell.utils import bl_cache, logger from blender_maxwell.utils import bl_cache, logger
from blender_maxwell.utils import sympy_extra as spux from blender_maxwell.utils import sympy_extra as spux
@ -146,7 +145,8 @@ def update_scene_node_after_frame_changed(
for node_tree in [ for node_tree in [
_node_tree _node_tree
for _node_tree in bpy.data.node_groups for _node_tree in bpy.data.node_groups
if _node_tree.bl_idname == ct.TreeType.MaxwellSim.value and _node_tree.is_active if _node_tree.bl_idname == ct.NodeTreeType.MaxwellSim.value
and _node_tree.is_active
]: ]:
for node in [ for node in [
_node _node

View File

@ -36,7 +36,6 @@ import typing as typ
import bpy import bpy
import tidy3d as td import tidy3d as td
from blender_maxwell.services import tdcloud from blender_maxwell.services import tdcloud
from blender_maxwell.utils import bl_cache, logger, sim_symbols from blender_maxwell.utils import bl_cache, logger, sim_symbols
from blender_maxwell.utils import sympy_extra as spux from blender_maxwell.utils import sympy_extra as spux

View File

@ -40,11 +40,10 @@ import bpy
import sympy as sp import sympy as sp
import sympy.physics.units as spu import sympy.physics.units as spu
import tidy3d as td import tidy3d as td
from tidy3d.material_library.material_library import MaterialItem as Tidy3DMediumItem
from tidy3d.material_library.material_library import VariantItem as Tidy3DMediumVariant
from blender_maxwell.utils import bl_cache, logger, sci_constants from blender_maxwell.utils import bl_cache, logger, sci_constants
from blender_maxwell.utils import sympy_extra as spux from blender_maxwell.utils import sympy_extra as spux
from tidy3d.material_library.material_library import MaterialItem as Tidy3DMediumItem
from tidy3d.material_library.material_library import VariantItem as Tidy3DMediumVariant
from ... import contracts as ct from ... import contracts as ct
from ... import managed_objs, sockets from ... import managed_objs, sockets

View File

@ -36,7 +36,6 @@ import functools
import typing as typ import typing as typ
import tidy3d as td import tidy3d as td
from blender_maxwell.utils import logger from blender_maxwell.utils import logger
from blender_maxwell.utils import sympy_extra as spux from blender_maxwell.utils import sympy_extra as spux

View File

@ -36,7 +36,6 @@ import typing as typ
import bpy import bpy
import tidy3d as td import tidy3d as td
from blender_maxwell.utils import logger from blender_maxwell.utils import logger
from blender_maxwell.utils import sympy_extra as spux from blender_maxwell.utils import sympy_extra as spux

View File

@ -36,7 +36,6 @@ import typing as typ
import bpy import bpy
import tidy3d as td import tidy3d as td
from blender_maxwell.utils import logger from blender_maxwell.utils import logger
from blender_maxwell.utils import sympy_extra as spux from blender_maxwell.utils import sympy_extra as spux

View File

@ -36,7 +36,6 @@ import bpy
import sympy.physics.units as spu import sympy.physics.units as spu
import tidy3d as td import tidy3d as td
import tidy3d.plugins.dispersion as td_dispersion import tidy3d.plugins.dispersion as td_dispersion
from blender_maxwell.utils import bl_cache, logger from blender_maxwell.utils import bl_cache, logger
from blender_maxwell.utils import sympy_extra as spux from blender_maxwell.utils import sympy_extra as spux

View File

@ -36,7 +36,6 @@ import bpy
import sympy as sp import sympy as sp
import sympy.physics.units as spu import sympy.physics.units as spu
import tidy3d as td import tidy3d as td
from blender_maxwell.assets.geonodes import GeoNodes, import_geonodes from blender_maxwell.assets.geonodes import GeoNodes, import_geonodes
from blender_maxwell.utils import bl_cache, logger from blender_maxwell.utils import bl_cache, logger
from blender_maxwell.utils import sympy_extra as spux from blender_maxwell.utils import sympy_extra as spux

View File

@ -39,7 +39,6 @@ import bpy
import sympy as sp import sympy as sp
import sympy.physics.units as spu import sympy.physics.units as spu
import tidy3d as td import tidy3d as td
from blender_maxwell.assets.geonodes import GeoNodes, import_geonodes from blender_maxwell.assets.geonodes import GeoNodes, import_geonodes
from blender_maxwell.utils import bl_cache, logger from blender_maxwell.utils import bl_cache, logger
from blender_maxwell.utils import sympy_extra as spux from blender_maxwell.utils import sympy_extra as spux

View File

@ -34,7 +34,6 @@ import typing as typ
import sympy as sp import sympy as sp
import tidy3d as td import tidy3d as td
from blender_maxwell.assets.geonodes import GeoNodes, import_geonodes from blender_maxwell.assets.geonodes import GeoNodes, import_geonodes
from blender_maxwell.utils import logger from blender_maxwell.utils import logger
from blender_maxwell.utils import sympy_extra as spux from blender_maxwell.utils import sympy_extra as spux

View File

@ -34,7 +34,6 @@ import typing as typ
from pathlib import Path from pathlib import Path
import bpy import bpy
from blender_maxwell.utils import bl_cache, logger from blender_maxwell.utils import bl_cache, logger
from blender_maxwell.utils import sympy_extra as spux from blender_maxwell.utils import sympy_extra as spux

View File

@ -115,10 +115,9 @@ class JSONFileExporterNode(base.MaxwellSimNode):
return data.json() return data.json()
# Pydantic Models: Call .model_dump_json() # Pydantic Models: Call .model_dump_json()
elif isinstance(data, pyd.BaseModel): if isinstance(data, pyd.BaseModel):
return data.model_dump_json() return data.model_dump_json()
else:
json.dumps(data) json.dumps(data)

View File

@ -35,7 +35,6 @@ import typing as typ
import bpy import bpy
import sympy as sp import sympy as sp
import tidy3d as td import tidy3d as td
from blender_maxwell.utils import bl_cache, logger from blender_maxwell.utils import bl_cache, logger
from blender_maxwell.utils import sympy_extra as spux from blender_maxwell.utils import sympy_extra as spux

View File

@ -36,7 +36,6 @@ import typing as typ
import bpy import bpy
import tidy3d as td import tidy3d as td
from blender_maxwell.services import tdcloud from blender_maxwell.services import tdcloud
from blender_maxwell.utils import bl_cache, logger, sim_symbols from blender_maxwell.utils import bl_cache, logger, sim_symbols

View File

@ -37,7 +37,6 @@ import typing as typ
import bpy import bpy
import sympy as sp import sympy as sp
import tidy3d as td import tidy3d as td
from blender_maxwell.utils import logger from blender_maxwell.utils import logger
from blender_maxwell.utils import sympy_extra as spux from blender_maxwell.utils import sympy_extra as spux

View File

@ -36,7 +36,6 @@ import typing as typ
import bpy import bpy
import tidy3d as td import tidy3d as td
from blender_maxwell.utils import bl_cache, logger from blender_maxwell.utils import bl_cache, logger
from .... import contracts as ct from .... import contracts as ct

View File

@ -37,7 +37,6 @@ import typing as typ
import bpy import bpy
import sympy as sp import sympy as sp
import tidy3d as td import tidy3d as td
from blender_maxwell.utils import logger from blender_maxwell.utils import logger
from blender_maxwell.utils import sympy_extra as spux from blender_maxwell.utils import sympy_extra as spux

View File

@ -35,7 +35,6 @@
import typing as typ import typing as typ
import tidy3d as td import tidy3d as td
from blender_maxwell.utils import logger from blender_maxwell.utils import logger
from ... import contracts as ct from ... import contracts as ct

View File

@ -36,12 +36,9 @@ import itertools
import typing as typ import typing as typ
import bpy import bpy
import jax
import numpy as np
import sympy as sp import sympy as sp
import sympy.physics.units as spu import sympy.physics.units as spu
import tidy3d as td import tidy3d as td
from blender_maxwell.utils import bl_cache, logger, sim_symbols from blender_maxwell.utils import bl_cache, logger, sim_symbols
from blender_maxwell.utils import sympy_extra as spux from blender_maxwell.utils import sympy_extra as spux
from blender_maxwell.utils.frozendict import frozendict from blender_maxwell.utils.frozendict import frozendict

View File

@ -36,7 +36,6 @@ import typing as typ
import sympy as sp import sympy as sp
import sympy.physics.units as spu import sympy.physics.units as spu
from blender_maxwell.assets.geonodes import GeoNodes, import_geonodes from blender_maxwell.assets.geonodes import GeoNodes, import_geonodes
from blender_maxwell.utils import logger from blender_maxwell.utils import logger
from blender_maxwell.utils import sympy_extra as spux from blender_maxwell.utils import sympy_extra as spux

View File

@ -36,7 +36,6 @@ import typing as typ
import sympy.physics.units as spu import sympy.physics.units as spu
import tidy3d as td import tidy3d as td
from blender_maxwell.utils import logger from blender_maxwell.utils import logger
from ... import contracts as ct from ... import contracts as ct

View File

@ -36,7 +36,6 @@ import typing as typ
import sympy.physics.units as spu import sympy.physics.units as spu
import tidy3d as td import tidy3d as td
from blender_maxwell.utils import logger from blender_maxwell.utils import logger
from .... import contracts as ct from .... import contracts as ct

View File

@ -36,7 +36,6 @@ import typing as typ
import sympy.physics.units as spu import sympy.physics.units as spu
import tidy3d as td import tidy3d as td
from blender_maxwell.utils import logger from blender_maxwell.utils import logger
from .... import contracts as ct from .... import contracts as ct

View File

@ -35,7 +35,6 @@
import typing as typ import typing as typ
import bpy import bpy
from blender_maxwell.services import tdcloud from blender_maxwell.services import tdcloud
from blender_maxwell.utils import bl_cache, logger from blender_maxwell.utils import bl_cache, logger
from blender_maxwell.utils import sympy_extra as spux from blender_maxwell.utils import sympy_extra as spux

View File

@ -38,7 +38,6 @@ import typing as typ
import bpy import bpy
import sympy as sp import sympy as sp
import tidy3d as td import tidy3d as td
from blender_maxwell.utils import bl_cache, logger from blender_maxwell.utils import bl_cache, logger
from blender_maxwell.utils import sympy_extra as spux from blender_maxwell.utils import sympy_extra as spux
from blender_maxwell.utils.frozendict import frozendict from blender_maxwell.utils.frozendict import frozendict

View File

@ -37,7 +37,6 @@ import typing as typ
import bpy import bpy
import sympy as sp import sympy as sp
import tidy3d as td import tidy3d as td
from blender_maxwell.assets.geonodes import GeoNodes, import_geonodes from blender_maxwell.assets.geonodes import GeoNodes, import_geonodes
from blender_maxwell.utils import bl_cache, logger from blender_maxwell.utils import bl_cache, logger
from blender_maxwell.utils import sympy_extra as spux from blender_maxwell.utils import sympy_extra as spux

View File

@ -35,7 +35,6 @@
import typing as typ import typing as typ
import tidy3d as td import tidy3d as td
from blender_maxwell.utils import bl_cache, logger from blender_maxwell.utils import bl_cache, logger
from blender_maxwell.utils import sympy_extra as spux from blender_maxwell.utils import sympy_extra as spux
from blender_maxwell.utils.frozendict import frozendict from blender_maxwell.utils.frozendict import frozendict

View File

@ -37,7 +37,6 @@ import typing as typ
import bpy import bpy
import sympy as sp import sympy as sp
import tidy3d as td import tidy3d as td
from blender_maxwell.assets.geonodes import GeoNodes, import_geonodes from blender_maxwell.assets.geonodes import GeoNodes, import_geonodes
from blender_maxwell.utils import bl_cache, logger from blender_maxwell.utils import bl_cache, logger
from blender_maxwell.utils import sympy_extra as spux from blender_maxwell.utils import sympy_extra as spux

View File

@ -37,7 +37,6 @@ import typing as typ
import bpy import bpy
import sympy as sp import sympy as sp
import tidy3d as td import tidy3d as td
from blender_maxwell.assets.geonodes import GeoNodes, import_geonodes from blender_maxwell.assets.geonodes import GeoNodes, import_geonodes
from blender_maxwell.utils import bl_cache, logger from blender_maxwell.utils import bl_cache, logger
from blender_maxwell.utils import sympy_extra as spux from blender_maxwell.utils import sympy_extra as spux

View File

@ -40,14 +40,13 @@ import numpy as np
import sympy as sp import sympy as sp
import sympy.physics.units as spu import sympy.physics.units as spu
import tidy3d as td import tidy3d as td
from blender_maxwell.utils import bl_cache, logger, sim_symbols
from blender_maxwell.utils import sympy_extra as spux
from tidy3d.components.data.data_array import TimeDataArray as td_TimeDataArray from tidy3d.components.data.data_array import TimeDataArray as td_TimeDataArray
from tidy3d.components.data.dataset import TimeDataset as td_TimeDataset from tidy3d.components.data.dataset import TimeDataset as td_TimeDataset
from blender_maxwell.utils import bl_cache, logger, sim_symbols
from blender_maxwell.utils import sympy_extra as spux
from ... import contracts as ct from ... import contracts as ct
from ... import managed_objs, sockets from ... import sockets
from .. import base, events from .. import base, events
log = logger.get(__name__) log = logger.get(__name__)

View File

@ -38,7 +38,6 @@ import typing as typ
import sympy as sp import sympy as sp
import sympy.physics.units as spu import sympy.physics.units as spu
import tidy3d as td import tidy3d as td
from blender_maxwell.utils import logger from blender_maxwell.utils import logger
from blender_maxwell.utils import sympy_extra as spux from blender_maxwell.utils import sympy_extra as spux

View File

@ -37,7 +37,6 @@ import typing as typ
import sympy as sp import sympy as sp
import sympy.physics.units as spu import sympy.physics.units as spu
import tidy3d as td import tidy3d as td
from blender_maxwell.assets.geonodes import GeoNodes, import_geonodes from blender_maxwell.assets.geonodes import GeoNodes, import_geonodes
from blender_maxwell.utils import logger from blender_maxwell.utils import logger
from blender_maxwell.utils import sympy_extra as spux from blender_maxwell.utils import sympy_extra as spux

View File

@ -37,7 +37,6 @@ import typing as typ
import sympy as sp import sympy as sp
import sympy.physics.units as spu import sympy.physics.units as spu
import tidy3d as td import tidy3d as td
from blender_maxwell.assets.geonodes import GeoNodes, import_geonodes from blender_maxwell.assets.geonodes import GeoNodes, import_geonodes
from blender_maxwell.utils import logger from blender_maxwell.utils import logger
from blender_maxwell.utils import sympy_extra as spux from blender_maxwell.utils import sympy_extra as spux

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