Skip to content

Commit 66ade0a

Browse files
committed
Support prefixes for InputGroups
1 parent 852823a commit 66ade0a

File tree

4 files changed

+83
-34
lines changed

4 files changed

+83
-34
lines changed

api/node_mapper.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import bl_ui
33
import itertools
44
import enum
5+
import re
56
from .state import State
67
from .types import *
78
from .static.input_group import InputGroup
@@ -299,6 +300,8 @@ def type_symbol(t):
299300
def enum_namespace(k):
300301
return f"""class {k}:
301302
{newline.join(enums[k])}"""
303+
def add_self_arg(x):
304+
return re.sub('\(', '(self, ', x, 1)
302305
contents = f"""from typing import *
303306
import enum
304307
def tree(builder):
@@ -327,7 +330,7 @@ def __ge__(self, other) -> Type: return self
327330
y = Type()
328331
z = Type()
329332
def capture(self, attribute: Type, **kwargs) -> Callable[[], Type]: return transfer_attribute
330-
{(newline + ' ').join(map(lambda x: x.replace('(', '(self, '), filter(lambda x: x.startswith('def'), symbols)))}
333+
{(newline + ' ').join(map(add_self_arg, filter(lambda x: x.startswith('def'), symbols)))}
331334
332335
{newline.join(map(type_symbol, Type.__subclasses__()))}
333336
{newline.join(map(enum_namespace, enums.keys()))}

api/static/input_group.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
1-
class InputGroup:
1+
class _InputGroupMeta(type):
2+
def __getitem__(cls, args):
3+
if isinstance(args, str):
4+
class PrefixedInputGroup(InputGroup):
5+
prefix = args
6+
PrefixedInputGroup.__annotations__ = cls.__annotations__
7+
return PrefixedInputGroup
8+
return cls
9+
10+
class InputGroup(metaclass=_InputGroupMeta):
211
"""
312
A group of inputs that will be expanded in the node tree.
413

api/tree.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,12 @@ def validate_param(param):
4747
raise Exception(f"Type of tree input '{param.name}' is not a valid 'Type' subclass.")
4848
for param in signature.parameters.values():
4949
if issubclass(param.annotation, InputGroup):
50+
prefix = (param.annotation.prefix + "_") if hasattr(param.annotation, "prefix") else ""
5051
for group_param, annotation in param.annotation.__annotations__.items():
51-
inputs[group_param] = (annotation, inspect.Parameter.empty, param.name)
52+
inputs[prefix + group_param] = (annotation, inspect.Parameter.empty, param.name, prefix)
5253
else:
5354
validate_param(param)
54-
inputs[param.name] = (param.annotation, param.default, None)
55+
inputs[param.name] = (param.annotation, param.default, None, None)
5556

5657
# Create the input sockets and collect input values.
5758
for i, node_input in enumerate(node_group.inputs):
@@ -72,7 +73,7 @@ def validate_param(param):
7273
if arg[1][2] is not None:
7374
if arg[1][2] not in builder_inputs:
7475
builder_inputs[arg[1][2]] = signature.parameters[arg[1][2]].annotation()
75-
setattr(builder_inputs[arg[1][2]], arg[0], arg[1][0](group_input_node.outputs[i]))
76+
setattr(builder_inputs[arg[1][2]], arg[0].replace(arg[1][3], ''), arg[1][0](group_input_node.outputs[i]))
7677
else:
7778
builder_inputs[arg[0]] = arg[1][0](group_input_node.outputs[i])
7879

@@ -132,7 +133,10 @@ def group_reference(*args, **kwargs):
132133
group_outputs = []
133134
for group_output in result._socket.node.outputs:
134135
group_outputs.append(Type(group_output))
135-
return tuple(group_outputs)
136+
if len(group_outputs) == 1:
137+
return group_outputs[0]
138+
else:
139+
return tuple(group_outputs)
136140
return group_reference
137141
if isinstance(name, str):
138142
return build_tree

api/types.py

Lines changed: 61 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from bpy.types import NodeSocketStandard
33
import nodeitems_utils
44
from .state import State
5+
import geometry_script
56

67
def map_case_name(i):
78
return ('_' if not i.identifier[0].isalpha() else '') + i.identifier.replace(' ', '_').upper()
@@ -16,7 +17,16 @@ def socket_type_to_data_type(socket_type):
1617
return socket_type
1718

1819
# The base class all exposed socket types conform to.
19-
class Type:
20+
class _TypeMeta(type):
21+
def __getitem__(self, args):
22+
for s in filter(lambda x: isinstance(x, slice), args):
23+
if (isinstance(s.start, float) or isinstance(s.start, int)) and (isinstance(s.stop, float) or isinstance(s.stop, int)):
24+
print(f"minmax: ({s.start}, {s.stop})")
25+
elif isinstance(s.start, str):
26+
print(f"{s.start} = {s.stop}")
27+
return self
28+
29+
class Type(metaclass=_TypeMeta):
2030
socket_type: str
2131

2232
def __init__(self, socket: bpy.types.NodeSocket = None, value = None):
@@ -41,14 +51,10 @@ def __init__(self, socket: bpy.types.NodeSocket = None, value = None):
4151
self.socket_type = type(socket).__name__
4252

4353
def _math(self, other, operation, reverse=False):
44-
math_node = State.current_node_tree.nodes.new('ShaderNodeVectorMath' if self._socket.type == 'VECTOR' else 'ShaderNodeMath')
45-
math_node.operation = operation
46-
State.current_node_tree.links.new(self._socket, math_node.inputs[1 if reverse else 0])
47-
if issubclass(type(other), Type):
48-
State.current_node_tree.links.new(other._socket, math_node.inputs[0 if reverse else 1])
54+
if self._socket.type == 'VECTOR':
55+
return geometry_script.vector_math(operation=operation, vector=(other, self) if reverse else (self, other))
4956
else:
50-
math_node.inputs[0 if reverse else 1].default_value = other
51-
return Type(math_node.outputs[0])
57+
return geometry_script.math(operation=operation, value=(other, self) if reverse else (self, other))
5258

5359
def __add__(self, other):
5460
return self._math(other, 'ADD')
@@ -81,30 +87,19 @@ def __rmod__(self, other):
8187
return self._math(other, 'MODULO', True)
8288

8389
def _compare(self, other, operation):
84-
compare_node = State.current_node_tree.nodes.new('FunctionNodeCompare')
85-
compare_node.data_type = 'FLOAT' if self._socket.type == 'VALUE' else self._socket.type
86-
compare_node.operation = operation
87-
a = None
88-
b = None
89-
for node_input in compare_node.inputs:
90-
if not node_input.enabled:
91-
continue
92-
elif a is None:
93-
a = node_input
94-
else:
95-
b = node_input
96-
State.current_node_tree.links.new(self._socket, a)
97-
if issubclass(type(other), Type):
98-
State.current_node_tree.links.new(other._socket, b)
99-
else:
100-
b.default_value = other
101-
return Type(compare_node.outputs[0])
90+
return geometry_script.compare(operation=operation, a=self, b=other)
10291

10392
def __eq__(self, other):
104-
return self._compare(other, 'EQUAL')
93+
if self._socket.type == 'BOOLEAN':
94+
return self._boolean_math(other, 'XNOR')
95+
else:
96+
return self._compare(other, 'EQUAL')
10597

10698
def __ne__(self, other):
107-
return self._compare(other, 'NOT_EQUAL')
99+
if self._socket.type == 'BOOLEAN':
100+
return self._boolean_math(other, 'XOR')
101+
else:
102+
return self._compare(other, 'NOT_EQUAL')
108103

109104
def __lt__(self, other):
110105
return self._compare(other, 'LESS_THAN')
@@ -118,6 +113,44 @@ def __gt__(self, other):
118113
def __ge__(self, other):
119114
return self._compare(other, 'GREATER_EQUAL')
120115

116+
def _boolean_math(self, other, operation, reverse=False):
117+
boolean_math_node = State.current_node_tree.nodes.new('FunctionNodeBooleanMath')
118+
boolean_math_node.operation = operation
119+
a = None
120+
b = None
121+
for node_input in boolean_math_node.inputs:
122+
if not node_input.enabled:
123+
continue
124+
elif a is None:
125+
a = node_input
126+
else:
127+
b = node_input
128+
State.current_node_tree.links.new(self._socket, a)
129+
if other is not None:
130+
if issubclass(type(other), Type):
131+
State.current_node_tree.links.new(other._socket, b)
132+
else:
133+
b.default_value = other
134+
return Type(boolean_math_node.outputs[0])
135+
136+
def __and__(self, other):
137+
return self._boolean_math(other, 'AND')
138+
139+
def __rand__(self, other):
140+
return self._boolean_math(other, 'AND', reverse=True)
141+
142+
def __or__(self, other):
143+
return self._boolean_math(other, 'OR')
144+
145+
def __ror__(self, other):
146+
return self._boolean_math(other, 'OR', reverse=True)
147+
148+
def __invert__(self):
149+
if self._socket.type == 'BOOLEAN':
150+
return self._boolean_math(None, 'NOT')
151+
else:
152+
return self._math(-1, 'MULTIPLY')
153+
121154
def _get_xyz_component(self, component):
122155
if self._socket.type != 'VECTOR':
123156
raise Exception("`x`, `y`, `z` properties are not available on non-Vector types.")

0 commit comments

Comments
 (0)