feat: We did it, GeoNodes node w/live update!
We also implemented the TriMesh node, and established a strong convention for updating nodes from sockets via. socket superclass method. `trigger_updates`. It should be triggered as the `update=` callback on **ALL PROPERTIES IN ALL SOCKETS**. This method in turn calls the nodal `update()` function, which in turn causes the node to chain-update all nodes linked to any output socket. By default, `update()` is `pass`, so performance shouldn't be a concern, but we should think about this deeper at some point. Because update-chaining is done, we're ready for preview toggles on node outputs. A lot of exciting things to do now!blender-plugin-mvp
parent
7344913c0e
commit
74d5a5daf8
|
@ -450,6 +450,41 @@ SocketType_to_color = {
|
||||||
SocketType.MaxwellSimGridAxis: (0.4, 0.3, 0.25, 1.0), # Darkest Gold
|
SocketType.MaxwellSimGridAxis: (0.4, 0.3, 0.25, 1.0), # Darkest Gold
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BLNodeSocket_to_SocketType = {
|
||||||
|
"NodeSocketBool": SocketType.Bool,
|
||||||
|
"NodeSocketCollection": SocketType.BlenderCollection,
|
||||||
|
"NodeSocketColor": SocketType.Real3DVector,
|
||||||
|
"NodeSocketFloat": SocketType.RealNumber,
|
||||||
|
"NodeSocketFloatAngle": SocketType.RealNumber,
|
||||||
|
"NodeSocketFloatDistance": SocketType.RealNumber,
|
||||||
|
"NodeSocketFloatFactor": SocketType.RealNumber,
|
||||||
|
"NodeSocketFloatPercentage": SocketType.RealNumber,
|
||||||
|
"NodeSocketFloatTime": SocketType.RealNumber,
|
||||||
|
"NodeSocketFloatTimeAbsolute": SocketType.RealNumber,
|
||||||
|
"NodeSocketFloatUnsigned": SocketType.RealNumber,
|
||||||
|
"NodeSocketGeometry": SocketType.Any,
|
||||||
|
"NodeSocketImage": SocketType.BlenderImage,
|
||||||
|
"NodeSocketInt": SocketType.IntegerNumber,
|
||||||
|
"NodeSocketIntFactor": SocketType.IntegerNumber,
|
||||||
|
"NodeSocketIntPercentage": SocketType.IntegerNumber,
|
||||||
|
"NodeSocketIntUnsigned": SocketType.IntegerNumber,
|
||||||
|
"NodeSocketMaterial": SocketType.Any,
|
||||||
|
"NodeSocketObject": SocketType.BlenderObject,
|
||||||
|
"NodeSocketRotation": SocketType.Real3DVector,
|
||||||
|
"NodeSocketShader": SocketType.Any,
|
||||||
|
"NodeSocketStandard": SocketType.Any,
|
||||||
|
"NodeSocketString": SocketType.Text,
|
||||||
|
"NodeSocketTexture": SocketType.Any,
|
||||||
|
"NodeSocketVector": SocketType.Real3DVector,
|
||||||
|
"NodeSocketVectorAcceleration": SocketType.Real3DVector,
|
||||||
|
"NodeSocketVectorDirection": SocketType.Real3DVector,
|
||||||
|
"NodeSocketVectorEuler": SocketType.Real3DVector,
|
||||||
|
"NodeSocketVectorTranslation": SocketType.Real3DVector,
|
||||||
|
"NodeSocketVectorVelocity": SocketType.Real3DVector,
|
||||||
|
"NodeSocketVectorXYZ": SocketType.Real3DVector,
|
||||||
|
"NodeSocketVirtual": SocketType.Any,
|
||||||
|
}
|
||||||
|
|
||||||
####################
|
####################
|
||||||
# - Node Types
|
# - Node Types
|
||||||
####################
|
####################
|
||||||
|
|
|
@ -155,11 +155,6 @@ class MaxwellSimTreeNode(bpy.types.Node):
|
||||||
|
|
||||||
# Declare Node Property: 'preset' EnumProperty
|
# Declare Node Property: 'preset' EnumProperty
|
||||||
if hasattr(cls, "input_socket_sets") or hasattr(cls, "output_socket_sets"):
|
if hasattr(cls, "input_socket_sets") or hasattr(cls, "output_socket_sets"):
|
||||||
if not hasattr(cls, "input_socket_sets"):
|
|
||||||
cls.input_socket_sets = {}
|
|
||||||
if not hasattr(cls, "output_socket_sets"):
|
|
||||||
cls.output_socket_sets = {}
|
|
||||||
|
|
||||||
socket_set_keys = [
|
socket_set_keys = [
|
||||||
input_socket_set_key
|
input_socket_set_key
|
||||||
for input_socket_set_key in cls.input_socket_sets.keys()
|
for input_socket_set_key in cls.input_socket_sets.keys()
|
||||||
|
@ -188,6 +183,11 @@ class MaxwellSimTreeNode(bpy.types.Node):
|
||||||
default=socket_set_keys[0]
|
default=socket_set_keys[0]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if not hasattr(cls, "input_socket_sets"):
|
||||||
|
cls.input_socket_sets = {}
|
||||||
|
if not hasattr(cls, "output_socket_sets"):
|
||||||
|
cls.output_socket_sets = {}
|
||||||
|
|
||||||
# Declare Node Property: 'preset' EnumProperty
|
# Declare Node Property: 'preset' EnumProperty
|
||||||
if hasattr(cls, "presets"):
|
if hasattr(cls, "presets"):
|
||||||
first_preset = list(cls.presets.keys())[0]
|
first_preset = list(cls.presets.keys())[0]
|
||||||
|
@ -292,6 +292,18 @@ class MaxwellSimTreeNode(bpy.types.Node):
|
||||||
|
|
||||||
return ntree.bl_idname == contracts.TreeType.MaxwellSim.value
|
return ntree.bl_idname == contracts.TreeType.MaxwellSim.value
|
||||||
|
|
||||||
|
def update(self) -> None:
|
||||||
|
"""Called when some node properties (ex. links) change,
|
||||||
|
and/or by custom code."""
|
||||||
|
if hasattr(self, "update_cb"):
|
||||||
|
self.update_cb()
|
||||||
|
|
||||||
|
for bl_socket in self.outputs:
|
||||||
|
if bl_socket.is_linked:
|
||||||
|
for node_link in bl_socket.links:
|
||||||
|
linked_node = node_link.to_node
|
||||||
|
linked_node.update()
|
||||||
|
|
||||||
def _update_socket(self):
|
def _update_socket(self):
|
||||||
if not hasattr(self, "socket_set"):
|
if not hasattr(self, "socket_set"):
|
||||||
raise ValueError("no socket")
|
raise ValueError("no socket")
|
||||||
|
@ -379,7 +391,7 @@ class MaxwellSimTreeNode(bpy.types.Node):
|
||||||
|
|
||||||
return self.inputs[self.input_sockets[input_socket_name].label]
|
return self.inputs[self.input_sockets[input_socket_name].label]
|
||||||
|
|
||||||
elif hasattr(self, "input_socket_sets"):
|
elif hasattr(self, "socket_set"):
|
||||||
# You're on your own, chump
|
# You're on your own, chump
|
||||||
|
|
||||||
return self.inputs[next(
|
return self.inputs[next(
|
||||||
|
@ -412,7 +424,7 @@ class MaxwellSimTreeNode(bpy.types.Node):
|
||||||
|
|
||||||
return self.outputs[self.output_sockets[output_socket_name].label]
|
return self.outputs[self.output_sockets[output_socket_name].label]
|
||||||
|
|
||||||
elif hasattr(self, "input_socket_sets"):
|
elif hasattr(self, "socket_set"):
|
||||||
return self.outputs[next(
|
return self.outputs[next(
|
||||||
socket_def.label
|
socket_def.label
|
||||||
for socket_set, socket_dict in self.input_socket_sets.items()
|
for socket_set, socket_dict in self.input_socket_sets.items()
|
||||||
|
@ -424,7 +436,7 @@ class MaxwellSimTreeNode(bpy.types.Node):
|
||||||
self,
|
self,
|
||||||
output_bl_socket_name: contracts.BLSocketName,
|
output_bl_socket_name: contracts.BLSocketName,
|
||||||
) -> contracts.SocketName:
|
) -> contracts.SocketName:
|
||||||
if hasattr(self, "output_socket_sets"):
|
if hasattr(self, "socket_set"):
|
||||||
return next(
|
return next(
|
||||||
socket_name
|
socket_name
|
||||||
for socket_set, socket_dict in self.output_socket_sets.items()
|
for socket_set, socket_dict in self.output_socket_sets.items()
|
||||||
|
|
|
@ -1,5 +1,177 @@
|
||||||
|
import tidy3d as td
|
||||||
|
import numpy as np
|
||||||
|
import sympy as sp
|
||||||
|
import sympy.physics.units as spu
|
||||||
|
|
||||||
|
import bpy
|
||||||
|
import bmesh
|
||||||
|
|
||||||
|
from ... import contracts
|
||||||
|
from ... import sockets
|
||||||
|
from .. import base
|
||||||
|
|
||||||
|
GEONODES_MODIFIER_NAME = "BLMaxwell_GeoNodes"
|
||||||
|
|
||||||
|
class GeoNodesStructureNode(base.MaxwellSimTreeNode):
|
||||||
|
node_type = contracts.NodeType.GeoNodesStructure
|
||||||
|
bl_label = "GeoNodes Structure"
|
||||||
|
#bl_icon = ...
|
||||||
|
|
||||||
|
####################
|
||||||
|
# - Sockets
|
||||||
|
####################
|
||||||
|
input_sockets = {
|
||||||
|
"medium": sockets.MaxwellMediumSocketDef(
|
||||||
|
label="Medium",
|
||||||
|
),
|
||||||
|
"object": sockets.BlenderObjectSocketDef(
|
||||||
|
label="Object",
|
||||||
|
),
|
||||||
|
"geo_nodes": sockets.BlenderGeoNodesSocketDef(
|
||||||
|
label="GeoNodes",
|
||||||
|
),
|
||||||
|
}
|
||||||
|
output_sockets = {
|
||||||
|
"structure": sockets.MaxwellStructureSocketDef(
|
||||||
|
label="Structure",
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
####################
|
||||||
|
# - Output Socket Computation
|
||||||
|
####################
|
||||||
|
@base.computes_output_socket("structure")
|
||||||
|
def compute_simulation(self: contracts.NodeTypeProtocol) -> td.TriangleMesh:
|
||||||
|
# Extract the Blender Object
|
||||||
|
bl_object = self.compute_input("object")
|
||||||
|
|
||||||
|
# Ensure Updated Geometry
|
||||||
|
bpy.context.view_layer.update()
|
||||||
|
|
||||||
|
# Triangulate Object Mesh
|
||||||
|
bmesh_mesh = bmesh.new()
|
||||||
|
bmesh_mesh.from_mesh(bl_object.data)
|
||||||
|
bmesh.ops.triangulate(bmesh_mesh, faces=bmesh_mesh.faces)
|
||||||
|
|
||||||
|
mesh = bpy.data.meshes.new(name="TriangulatedMesh")
|
||||||
|
bmesh_mesh.to_mesh(mesh)
|
||||||
|
bmesh_mesh.free()
|
||||||
|
|
||||||
|
# Extract Vertices and Faces
|
||||||
|
vertices = np.array([vert.co for vert in mesh.vertices])
|
||||||
|
faces = np.array([
|
||||||
|
[vert for vert in poly.vertices]
|
||||||
|
for poly in mesh.polygons
|
||||||
|
])
|
||||||
|
|
||||||
|
# Remove Temporary Mesh
|
||||||
|
bpy.data.meshes.remove(mesh)
|
||||||
|
|
||||||
|
return td.Structure(
|
||||||
|
geometry=td.TriangleMesh.from_vertices_faces(vertices, faces),
|
||||||
|
medium=self.compute_input("medium")
|
||||||
|
)
|
||||||
|
|
||||||
|
####################
|
||||||
|
# - Update Function
|
||||||
|
####################
|
||||||
|
def update_cb(self) -> None:
|
||||||
|
bl_object = self.compute_input("object")
|
||||||
|
if bl_object is None: return
|
||||||
|
|
||||||
|
geo_nodes = self.compute_input("geo_nodes")
|
||||||
|
if geo_nodes is None: return
|
||||||
|
|
||||||
|
bl_modifier = bl_object.modifiers.get(GEONODES_MODIFIER_NAME)
|
||||||
|
if bl_modifier is None: return
|
||||||
|
|
||||||
|
# Set GeoNodes Modifier Attributes
|
||||||
|
for idx, interface_item in enumerate(
|
||||||
|
geo_nodes.interface.items_tree.values()
|
||||||
|
):
|
||||||
|
if idx == 0: continue ## Always-on "Geometry" Input (from Object)
|
||||||
|
|
||||||
|
bl_socket = self.inputs[
|
||||||
|
interface_item.name
|
||||||
|
]
|
||||||
|
if bl_socket.is_linked:
|
||||||
|
linked_bl_socket = bl_socket.links[0].from_socket
|
||||||
|
linked_bl_node = bl_socket.links[0].from_node
|
||||||
|
val = linked_bl_node.compute_output(
|
||||||
|
linked_bl_node.g_output_socket_name(
|
||||||
|
linked_bl_socket.name
|
||||||
|
)
|
||||||
|
) ## What a bunch of spaghetti
|
||||||
|
else:
|
||||||
|
val = self.inputs[
|
||||||
|
interface_item.name
|
||||||
|
].default_value
|
||||||
|
|
||||||
|
# Conservatively Set Differing Values
|
||||||
|
if bl_modifier[interface_item.identifier] != val:
|
||||||
|
bl_modifier[interface_item.identifier] = val
|
||||||
|
|
||||||
|
# Update DepGraph
|
||||||
|
bl_object.data.update()
|
||||||
|
|
||||||
|
def update_sockets_from_geonodes(self) -> None:
|
||||||
|
# Remove All "Loose" Sockets
|
||||||
|
socket_labels = {
|
||||||
|
socket_def.label
|
||||||
|
for socket_def in self.input_sockets.values()
|
||||||
|
} | {
|
||||||
|
socket_def.label
|
||||||
|
for socket_set_name, socket_set in self.input_socket_sets.items()
|
||||||
|
for socket_name, socket_def in socket_set.items()
|
||||||
|
}
|
||||||
|
bl_sockets_to_remove = {
|
||||||
|
bl_socket
|
||||||
|
for bl_socket_name, bl_socket in self.inputs.items()
|
||||||
|
if bl_socket_name not in socket_labels
|
||||||
|
}
|
||||||
|
|
||||||
|
for bl_socket in bl_sockets_to_remove:
|
||||||
|
self.inputs.remove(bl_socket)
|
||||||
|
|
||||||
|
# Query for Blender Object / Geo Nodes
|
||||||
|
bl_object = self.compute_input("object")
|
||||||
|
if bl_object is None: return
|
||||||
|
## TODO: Make object? Gray out geonodes if object not defined?
|
||||||
|
|
||||||
|
geo_nodes = self.compute_input("geo_nodes")
|
||||||
|
if geo_nodes is None: return
|
||||||
|
|
||||||
|
|
||||||
|
# Add Non-Static Sockets from GeoNodes
|
||||||
|
for bl_socket_name, bl_socket in geo_nodes.interface.items_tree.items():
|
||||||
|
# For now, don't allow Geometry inputs.
|
||||||
|
if bl_socket.socket_type == "NodeSocketGeometry": continue
|
||||||
|
|
||||||
|
self.inputs.new(
|
||||||
|
contracts.BLNodeSocket_to_SocketType[bl_socket.socket_type],
|
||||||
|
bl_socket_name,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create New GeoNodes Modifier
|
||||||
|
if GEONODES_MODIFIER_NAME not in bl_object.modifiers:
|
||||||
|
modifier = bl_object.modifiers.new(
|
||||||
|
name=GEONODES_MODIFIER_NAME,
|
||||||
|
type="NODES",
|
||||||
|
)
|
||||||
|
modifier.node_group = geo_nodes
|
||||||
|
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
####################
|
####################
|
||||||
# - Blender Registration
|
# - Blender Registration
|
||||||
####################
|
####################
|
||||||
BL_REGISTER = []
|
BL_REGISTER = [
|
||||||
BL_NODES = {}
|
GeoNodesStructureNode,
|
||||||
|
]
|
||||||
|
BL_NODES = {
|
||||||
|
contracts.NodeType.GeoNodesStructure: (
|
||||||
|
contracts.NodeCategory.MAXWELLSIM_STRUCTURES
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,83 @@
|
||||||
|
import tidy3d as td
|
||||||
|
import numpy as np
|
||||||
|
import sympy as sp
|
||||||
|
import sympy.physics.units as spu
|
||||||
|
|
||||||
|
import bpy
|
||||||
|
import bmesh
|
||||||
|
|
||||||
|
from ... import contracts
|
||||||
|
from ... import sockets
|
||||||
|
from .. import base
|
||||||
|
|
||||||
|
class ObjectStructureNode(base.MaxwellSimTreeNode):
|
||||||
|
node_type = contracts.NodeType.ObjectStructure
|
||||||
|
bl_label = "Object Structure"
|
||||||
|
#bl_icon = ...
|
||||||
|
|
||||||
|
####################
|
||||||
|
# - Sockets
|
||||||
|
####################
|
||||||
|
input_sockets = {
|
||||||
|
"medium": sockets.MaxwellMediumSocketDef(
|
||||||
|
label="Medium",
|
||||||
|
),
|
||||||
|
"object": sockets.BlenderObjectSocketDef(
|
||||||
|
label="Object",
|
||||||
|
),
|
||||||
|
}
|
||||||
|
output_sockets = {
|
||||||
|
"structure": sockets.MaxwellStructureSocketDef(
|
||||||
|
label="Structure",
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
####################
|
||||||
|
# - Output Socket Computation
|
||||||
|
####################
|
||||||
|
@base.computes_output_socket("structure")
|
||||||
|
def compute_structure(self: contracts.NodeTypeProtocol) -> td.Structure:
|
||||||
|
# Extract the Blender Object
|
||||||
|
bl_object = self.compute_input("object")
|
||||||
|
|
||||||
|
# Ensure Updated Geometry
|
||||||
|
bpy.context.view_layer.update()
|
||||||
|
|
||||||
|
# Triangulate Object Mesh
|
||||||
|
bmesh_mesh = bmesh.new()
|
||||||
|
bmesh_mesh.from_mesh(bl_object.data)
|
||||||
|
bmesh.ops.triangulate(bmesh_mesh, faces=bmesh_mesh.faces)
|
||||||
|
|
||||||
|
mesh = bpy.data.meshes.new(name="TriangulatedMesh")
|
||||||
|
bmesh_mesh.to_mesh(mesh)
|
||||||
|
bmesh_mesh.free()
|
||||||
|
|
||||||
|
# Extract Vertices and Faces
|
||||||
|
vertices = np.array([vert.co for vert in mesh.vertices])
|
||||||
|
faces = np.array([
|
||||||
|
[vert for vert in poly.vertices]
|
||||||
|
for poly in mesh.polygons
|
||||||
|
])
|
||||||
|
|
||||||
|
# Remove Temporary Mesh
|
||||||
|
bpy.data.meshes.remove(mesh)
|
||||||
|
|
||||||
|
print(vertices)
|
||||||
|
return td.Structure(
|
||||||
|
geometry=td.TriangleMesh.from_vertices_faces(vertices, faces),
|
||||||
|
medium=self.compute_input("medium")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
####################
|
####################
|
||||||
# - Blender Registration
|
# - Blender Registration
|
||||||
####################
|
####################
|
||||||
BL_REGISTER = []
|
BL_REGISTER = [
|
||||||
BL_NODES = {}
|
ObjectStructureNode,
|
||||||
|
]
|
||||||
|
BL_NODES = {
|
||||||
|
contracts.NodeType.ObjectStructure: (
|
||||||
|
contracts.NodeCategory.MAXWELLSIM_STRUCTURES
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -140,6 +140,13 @@ class BLSocket(bpy.types.NodeSocket):
|
||||||
|
|
||||||
self._unit_previous = self.unit
|
self._unit_previous = self.unit
|
||||||
|
|
||||||
|
####################
|
||||||
|
# - Callback Dispatcher
|
||||||
|
####################
|
||||||
|
def trigger_updates(self) -> None:
|
||||||
|
if not self.is_output:
|
||||||
|
self.node.update()
|
||||||
|
|
||||||
####################
|
####################
|
||||||
# - Methods
|
# - Methods
|
||||||
####################
|
####################
|
||||||
|
|
|
@ -25,6 +25,7 @@ class BoolBLSocket(base.BLSocket):
|
||||||
name="Boolean",
|
name="Boolean",
|
||||||
description="Represents a boolean",
|
description="Represents a boolean",
|
||||||
default=False,
|
default=False,
|
||||||
|
update=(lambda self, context: self.trigger_updates()),
|
||||||
)
|
)
|
||||||
|
|
||||||
####################
|
####################
|
||||||
|
|
|
@ -27,6 +27,7 @@ class FilePathBLSocket(base.BLSocket):
|
||||||
description="Represents the path to a file",
|
description="Represents the path to a file",
|
||||||
#default="",
|
#default="",
|
||||||
subtype="FILE_PATH",
|
subtype="FILE_PATH",
|
||||||
|
update=(lambda self, context: self.trigger_updates()),
|
||||||
)
|
)
|
||||||
|
|
||||||
####################
|
####################
|
||||||
|
|
|
@ -27,6 +27,7 @@ class TextBLSocket(base.BLSocket):
|
||||||
name="Text",
|
name="Text",
|
||||||
description="Represents some text",
|
description="Represents some text",
|
||||||
default="",
|
default="",
|
||||||
|
update=(lambda self, context: self.trigger_updates()),
|
||||||
)
|
)
|
||||||
|
|
||||||
####################
|
####################
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import typing as typ
|
import typing as typ
|
||||||
|
|
||||||
|
import bpy
|
||||||
import pydantic as pyd
|
import pydantic as pyd
|
||||||
|
|
||||||
from .. import base
|
from .. import base
|
||||||
|
@ -12,16 +13,26 @@ class BlenderCollectionBLSocket(base.BLSocket):
|
||||||
socket_type = contracts.SocketType.BlenderCollection
|
socket_type = contracts.SocketType.BlenderCollection
|
||||||
bl_label = "BlenderCollection"
|
bl_label = "BlenderCollection"
|
||||||
|
|
||||||
|
####################
|
||||||
|
# - Properties
|
||||||
|
####################
|
||||||
|
raw_value: bpy.props.PointerProperty(
|
||||||
|
name="Blender Collection",
|
||||||
|
description="Represents a Blender collection",
|
||||||
|
type=bpy.types.Collection,
|
||||||
|
update=(lambda self, context: self.trigger_updates()),
|
||||||
|
)
|
||||||
|
|
||||||
####################
|
####################
|
||||||
# - Default Value
|
# - Default Value
|
||||||
####################
|
####################
|
||||||
@property
|
@property
|
||||||
def default_value(self) -> None:
|
def default_value(self) -> bpy.types.Collection | None:
|
||||||
pass
|
return self.raw_value
|
||||||
|
|
||||||
@default_value.setter
|
@default_value.setter
|
||||||
def default_value(self, value: typ.Any) -> None:
|
def default_value(self, value: bpy.types.Collection) -> None:
|
||||||
pass
|
self.raw_value = value
|
||||||
|
|
||||||
####################
|
####################
|
||||||
# - Socket Configuration
|
# - Socket Configuration
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import typing as typ
|
import typing as typ
|
||||||
|
|
||||||
|
import bpy
|
||||||
import pydantic as pyd
|
import pydantic as pyd
|
||||||
|
|
||||||
from .. import base
|
from .. import base
|
||||||
|
@ -12,16 +13,36 @@ class BlenderGeoNodesBLSocket(base.BLSocket):
|
||||||
socket_type = contracts.SocketType.BlenderGeoNodes
|
socket_type = contracts.SocketType.BlenderGeoNodes
|
||||||
bl_label = "BlenderGeoNodes"
|
bl_label = "BlenderGeoNodes"
|
||||||
|
|
||||||
|
####################
|
||||||
|
# - Properties
|
||||||
|
####################
|
||||||
|
def update_geonodes_node(self):
|
||||||
|
if hasattr(self.node, "update_sockets_from_geonodes"):
|
||||||
|
self.node.update_sockets_from_geonodes()
|
||||||
|
else:
|
||||||
|
raise ValueError("Node doesn't have GeoNodes socket update method.")
|
||||||
|
|
||||||
|
# Run the Usual Updates
|
||||||
|
self.trigger_updates()
|
||||||
|
|
||||||
|
raw_value: bpy.props.PointerProperty(
|
||||||
|
name="Blender GeoNodes Tree",
|
||||||
|
description="Represents a Blender GeoNodes Tree",
|
||||||
|
type=bpy.types.NodeTree,
|
||||||
|
poll=(lambda self, obj: obj.bl_idname == 'GeometryNodeTree'),
|
||||||
|
update=(lambda self, context: self.update_geonodes_node()),
|
||||||
|
)
|
||||||
|
|
||||||
####################
|
####################
|
||||||
# - Default Value
|
# - Default Value
|
||||||
####################
|
####################
|
||||||
@property
|
@property
|
||||||
def default_value(self) -> None:
|
def default_value(self) -> bpy.types.Object | None:
|
||||||
pass
|
return self.raw_value
|
||||||
|
|
||||||
@default_value.setter
|
@default_value.setter
|
||||||
def default_value(self, value: typ.Any) -> None:
|
def default_value(self, value: bpy.types.Object) -> None:
|
||||||
pass
|
self.raw_value = value
|
||||||
|
|
||||||
####################
|
####################
|
||||||
# - Socket Configuration
|
# - Socket Configuration
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import typing as typ
|
import typing as typ
|
||||||
|
|
||||||
|
import bpy
|
||||||
import pydantic as pyd
|
import pydantic as pyd
|
||||||
|
|
||||||
from .. import base
|
from .. import base
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import typing as typ
|
import typing as typ
|
||||||
|
|
||||||
|
import bpy
|
||||||
import pydantic as pyd
|
import pydantic as pyd
|
||||||
|
|
||||||
from .. import base
|
from .. import base
|
||||||
|
@ -12,16 +13,26 @@ class BlenderObjectBLSocket(base.BLSocket):
|
||||||
socket_type = contracts.SocketType.BlenderObject
|
socket_type = contracts.SocketType.BlenderObject
|
||||||
bl_label = "BlenderObject"
|
bl_label = "BlenderObject"
|
||||||
|
|
||||||
|
####################
|
||||||
|
# - Properties
|
||||||
|
####################
|
||||||
|
raw_value: bpy.props.PointerProperty(
|
||||||
|
name="Blender Object",
|
||||||
|
description="Represents a Blender object",
|
||||||
|
type=bpy.types.Object,
|
||||||
|
update=(lambda self, context: self.trigger_updates()),
|
||||||
|
)
|
||||||
|
|
||||||
####################
|
####################
|
||||||
# - Default Value
|
# - Default Value
|
||||||
####################
|
####################
|
||||||
@property
|
@property
|
||||||
def default_value(self) -> None:
|
def default_value(self) -> bpy.types.Object | None:
|
||||||
pass
|
return self.raw_value
|
||||||
|
|
||||||
@default_value.setter
|
@default_value.setter
|
||||||
def default_value(self, value: typ.Any) -> None:
|
def default_value(self, value: bpy.types.Object) -> None:
|
||||||
pass
|
self.raw_value = value
|
||||||
|
|
||||||
####################
|
####################
|
||||||
# - Socket Configuration
|
# - Socket Configuration
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import typing as typ
|
import typing as typ
|
||||||
|
|
||||||
|
import bpy
|
||||||
import pydantic as pyd
|
import pydantic as pyd
|
||||||
|
|
||||||
from .. import base
|
from .. import base
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import typing as typ
|
import typing as typ
|
||||||
|
|
||||||
|
import bpy
|
||||||
import pydantic as pyd
|
import pydantic as pyd
|
||||||
|
|
||||||
from .. import base
|
from .. import base
|
||||||
|
|
|
@ -23,6 +23,7 @@ class MaxwellMediumBLSocket(base.BLSocket):
|
||||||
description="Represents a simple, real permittivity.",
|
description="Represents a simple, real permittivity.",
|
||||||
default=0.0,
|
default=0.0,
|
||||||
precision=4,
|
precision=4,
|
||||||
|
update=(lambda self, context: self.trigger_updates()),
|
||||||
)
|
)
|
||||||
|
|
||||||
####################
|
####################
|
||||||
|
|
|
@ -30,7 +30,8 @@ class ComplexNumberBLSocket(base.BLSocket):
|
||||||
description="Represents a complex number (real, imaginary)",
|
description="Represents a complex number (real, imaginary)",
|
||||||
size=2,
|
size=2,
|
||||||
default=(0.0, 0.0),
|
default=(0.0, 0.0),
|
||||||
subtype='NONE'
|
subtype='NONE',
|
||||||
|
update=(lambda self, context: self.trigger_updates()),
|
||||||
)
|
)
|
||||||
coord_sys: bpy.props.EnumProperty(
|
coord_sys: bpy.props.EnumProperty(
|
||||||
name="Coordinate System",
|
name="Coordinate System",
|
||||||
|
@ -136,6 +137,8 @@ class ComplexNumberBLSocket(base.BLSocket):
|
||||||
sp.arg(cart_value) if y != 0 else 0,
|
sp.arg(cart_value) if y != 0 else 0,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.trigger_updates()
|
||||||
|
|
||||||
####################
|
####################
|
||||||
# - Socket Configuration
|
# - Socket Configuration
|
||||||
####################
|
####################
|
||||||
|
|
|
@ -30,6 +30,7 @@ class RealNumberBLSocket(base.BLSocket):
|
||||||
description="Represents a real number",
|
description="Represents a real number",
|
||||||
default=0.0,
|
default=0.0,
|
||||||
precision=6,
|
precision=6,
|
||||||
|
update=(lambda self, context: self.trigger_updates()),
|
||||||
)
|
)
|
||||||
|
|
||||||
####################
|
####################
|
||||||
|
|
|
@ -22,6 +22,7 @@ class PhysicalAccelScalarBLSocket(base.BLSocket):
|
||||||
description="Represents the unitless part of the acceleration",
|
description="Represents the unitless part of the acceleration",
|
||||||
default=0.0,
|
default=0.0,
|
||||||
precision=6,
|
precision=6,
|
||||||
|
update=(lambda self, context: self.trigger_updates()),
|
||||||
)
|
)
|
||||||
|
|
||||||
####################
|
####################
|
||||||
|
|
|
@ -22,6 +22,7 @@ class PhysicalAngleBLSocket(base.BLSocket):
|
||||||
description="Represents the unitless part of the acceleration",
|
description="Represents the unitless part of the acceleration",
|
||||||
default=0.0,
|
default=0.0,
|
||||||
precision=4,
|
precision=4,
|
||||||
|
update=(lambda self, context: self.trigger_updates()),
|
||||||
)
|
)
|
||||||
|
|
||||||
####################
|
####################
|
||||||
|
|
|
@ -32,6 +32,7 @@ class PhysicalAreaBLSocket(base.BLSocket):
|
||||||
description="Represents the unitless part of the area",
|
description="Represents the unitless part of the area",
|
||||||
default=0.0,
|
default=0.0,
|
||||||
precision=6,
|
precision=6,
|
||||||
|
update=(lambda self, context: self.trigger_updates()),
|
||||||
)
|
)
|
||||||
|
|
||||||
####################
|
####################
|
||||||
|
|
|
@ -22,6 +22,7 @@ class PhysicalForceScalarBLSocket(base.BLSocket):
|
||||||
description="Represents the unitless part of the force",
|
description="Represents the unitless part of the force",
|
||||||
default=0.0,
|
default=0.0,
|
||||||
precision=6,
|
precision=6,
|
||||||
|
update=(lambda self, context: self.trigger_updates()),
|
||||||
)
|
)
|
||||||
|
|
||||||
####################
|
####################
|
||||||
|
|
|
@ -22,6 +22,7 @@ class PhysicalFreqBLSocket(base.BLSocket):
|
||||||
description="Represents the unitless part of the frequency",
|
description="Represents the unitless part of the frequency",
|
||||||
default=0.0,
|
default=0.0,
|
||||||
precision=6,
|
precision=6,
|
||||||
|
update=(lambda self, context: self.trigger_updates()),
|
||||||
)
|
)
|
||||||
|
|
||||||
####################
|
####################
|
||||||
|
|
|
@ -22,6 +22,7 @@ class PhysicalLengthBLSocket(base.BLSocket):
|
||||||
description="Represents the unitless part of the force",
|
description="Represents the unitless part of the force",
|
||||||
default=0.0,
|
default=0.0,
|
||||||
precision=6,
|
precision=6,
|
||||||
|
update=(lambda self, context: self.trigger_updates()),
|
||||||
)
|
)
|
||||||
|
|
||||||
####################
|
####################
|
||||||
|
|
|
@ -33,6 +33,7 @@ class PhysicalPoint3DBLSocket(base.BLSocket):
|
||||||
size=3,
|
size=3,
|
||||||
default=(0.0, 0.0, 0.0),
|
default=(0.0, 0.0, 0.0),
|
||||||
precision=4,
|
precision=4,
|
||||||
|
update=(lambda self, context: self.trigger_updates()),
|
||||||
)
|
)
|
||||||
|
|
||||||
####################
|
####################
|
||||||
|
|
|
@ -33,6 +33,7 @@ class PhysicalSize3DBLSocket(base.BLSocket):
|
||||||
size=3,
|
size=3,
|
||||||
default=(1.0, 1.0, 1.0),
|
default=(1.0, 1.0, 1.0),
|
||||||
precision=4,
|
precision=4,
|
||||||
|
update=(lambda self, context: self.trigger_updates()),
|
||||||
)
|
)
|
||||||
|
|
||||||
####################
|
####################
|
||||||
|
|
|
@ -22,6 +22,7 @@ class PhysicalTimeBLSocket(base.BLSocket):
|
||||||
description="Represents the unitless part of the force",
|
description="Represents the unitless part of the force",
|
||||||
default=0.0,
|
default=0.0,
|
||||||
precision=6,
|
precision=6,
|
||||||
|
update=(lambda self, context: self.trigger_updates()),
|
||||||
)
|
)
|
||||||
|
|
||||||
####################
|
####################
|
||||||
|
|
|
@ -32,6 +32,7 @@ class PhysicalVolumeBLSocket(base.BLSocket):
|
||||||
description="Represents the unitless part of the area",
|
description="Represents the unitless part of the area",
|
||||||
default=0.0,
|
default=0.0,
|
||||||
precision=6,
|
precision=6,
|
||||||
|
update=(lambda self, context: self.trigger_updates()),
|
||||||
)
|
)
|
||||||
|
|
||||||
####################
|
####################
|
||||||
|
|
|
@ -2,3 +2,4 @@ tidy3d==2.5.2
|
||||||
pydantic==2.6.0
|
pydantic==2.6.0
|
||||||
sympy==1.12
|
sympy==1.12
|
||||||
scipy==1.12.0
|
scipy==1.12.0
|
||||||
|
trimesh==4.1.4
|
||||||
|
|
BIN
code/demo.blend (Stored with Git LFS)
BIN
code/demo.blend (Stored with Git LFS)
Binary file not shown.
Loading…
Reference in New Issue