kopia lustrzana https://github.com/carson-katri/geometry-script
Add enum generation
rodzic
f2d6280024
commit
828fb8b6da
|
@ -1,5 +1,7 @@
|
||||||
import bpy
|
import bpy
|
||||||
import bl_ui
|
import bl_ui
|
||||||
|
import itertools
|
||||||
|
import enum
|
||||||
from .state import State
|
from .state import State
|
||||||
from .types import *
|
from .types import *
|
||||||
from ..absolute_path import absolute_path
|
from ..absolute_path import absolute_path
|
||||||
|
@ -17,21 +19,39 @@ def build_node(node_type):
|
||||||
for prop in node.bl_rna.properties:
|
for prop in node.bl_rna.properties:
|
||||||
argname = prop.identifier.lower().replace(' ', '_')
|
argname = prop.identifier.lower().replace(' ', '_')
|
||||||
if argname in kwargs:
|
if argname in kwargs:
|
||||||
setattr(node, prop.identifier, kwargs[argname])
|
value = kwargs[argname]
|
||||||
|
if isinstance(value, enum.Enum):
|
||||||
|
value = value.value
|
||||||
|
setattr(node, prop.identifier, value)
|
||||||
for node_input in (node.inputs[1:] if _primary_arg is not None else node.inputs):
|
for node_input in (node.inputs[1:] if _primary_arg is not None else node.inputs):
|
||||||
argname = node_input.name.lower().replace(' ', '_')
|
argname = node_input.name.lower().replace(' ', '_')
|
||||||
|
all_with_name = []
|
||||||
|
for node_input2 in (node.inputs[1:] if _primary_arg is not None else node.inputs):
|
||||||
|
if node_input2.name.lower().replace(' ', '_') == argname and node_input2.type == node_input.type:
|
||||||
|
all_with_name.append(node_input2)
|
||||||
if argname in kwargs:
|
if argname in kwargs:
|
||||||
if node_input.is_multi_input and hasattr(kwargs[argname], '__iter__') and len(kwargs[argname]) > 0 and issubclass(type(next(iter(kwargs[argname]))), Type):
|
def set_or_create_link(x, node_input):
|
||||||
for x in kwargs[argname]:
|
if issubclass(type(x), Type):
|
||||||
State.current_node_tree.links.new(x._socket, node_input)
|
State.current_node_tree.links.new(x._socket, node_input)
|
||||||
elif issubclass(type(kwargs[argname]), Type):
|
else:
|
||||||
State.current_node_tree.links.new(kwargs[argname]._socket, node_input)
|
try:
|
||||||
|
node_input.default_value = x
|
||||||
|
except:
|
||||||
|
constant = Type(value=x)
|
||||||
|
State.current_node_tree.links.new(constant._socket, node_input)
|
||||||
|
value = kwargs[argname]
|
||||||
|
if isinstance(value, enum.Enum):
|
||||||
|
value = value.value
|
||||||
|
if node_input.is_multi_input and hasattr(value, '__iter__') and len() > 0 and issubclass(type(next(iter(value))), Type):
|
||||||
|
for x in value:
|
||||||
|
for node_input in all_with_name:
|
||||||
|
State.current_node_tree.links.new(x._socket, node_input)
|
||||||
|
elif len(all_with_name) > 1 and issubclass(type(value), tuple) and len(value) > 0:
|
||||||
|
for i, x in enumerate(value):
|
||||||
|
set_or_create_link(x, all_with_name[i])
|
||||||
else:
|
else:
|
||||||
try:
|
for node_input in all_with_name:
|
||||||
node_input.default_value = kwargs[argname]
|
set_or_create_link(value, node_input)
|
||||||
except:
|
|
||||||
constant = Type(value=kwargs[argname])
|
|
||||||
State.current_node_tree.links.new(constant._socket, node_input)
|
|
||||||
outputs = {}
|
outputs = {}
|
||||||
for node_output in node.outputs:
|
for node_output in node.outputs:
|
||||||
if not node_output.enabled:
|
if not node_output.enabled:
|
||||||
|
@ -49,6 +69,7 @@ def register_node(node_type, category_path=None):
|
||||||
if node_type in registered_nodes:
|
if node_type in registered_nodes:
|
||||||
return
|
return
|
||||||
snake_case_name = node_type.bl_rna.name.lower().replace(' ', '_')
|
snake_case_name = node_type.bl_rna.name.lower().replace(' ', '_')
|
||||||
|
node_namespace_name = snake_case_name.replace('_', ' ').title().replace(' ', '')
|
||||||
globals()[snake_case_name] = build_node(node_type)
|
globals()[snake_case_name] = build_node(node_type)
|
||||||
globals()[snake_case_name].bl_category_path = category_path
|
globals()[snake_case_name].bl_category_path = category_path
|
||||||
globals()[snake_case_name].bl_node_type = node_type
|
globals()[snake_case_name].bl_node_type = node_type
|
||||||
|
@ -58,6 +79,16 @@ def register_node(node_type, category_path=None):
|
||||||
return build_node(node_type)(self, *args, **kwargs)
|
return build_node(node_type)(self, *args, **kwargs)
|
||||||
return build
|
return build
|
||||||
setattr(Type, snake_case_name, build_node_method(node_type))
|
setattr(Type, snake_case_name, build_node_method(node_type))
|
||||||
|
parent_props = [prop.identifier for base in node_type.__bases__ for prop in base.bl_rna.properties]
|
||||||
|
for prop in node_type.bl_rna.properties:
|
||||||
|
if not prop.identifier in parent_props and prop.type == 'ENUM':
|
||||||
|
if node_namespace_name not in globals():
|
||||||
|
class NodeNamespace: pass
|
||||||
|
NodeNamespace.__name__ = node_namespace_name
|
||||||
|
globals()[node_namespace_name] = NodeNamespace
|
||||||
|
enum_type_name = prop.identifier.replace('_', ' ').title().replace(' ', '')
|
||||||
|
enum_type = enum.Enum(enum_type_name, { map_case_name(i): i.identifier for i in prop.enum_items })
|
||||||
|
setattr(globals()[node_namespace_name], enum_type_name, enum_type)
|
||||||
registered_nodes.add(node_type)
|
registered_nodes.add(node_type)
|
||||||
for category_name in list(filter(lambda x: x.startswith('NODE_MT_category_GEO_'), dir(bpy.types))):
|
for category_name in list(filter(lambda x: x.startswith('NODE_MT_category_GEO_'), dir(bpy.types))):
|
||||||
category = getattr(bpy.types, category_name)
|
category = getattr(bpy.types, category_name)
|
||||||
|
@ -108,6 +139,7 @@ def create_documentation():
|
||||||
default_color = '#A1A1A1'
|
default_color = '#A1A1A1'
|
||||||
docstrings = []
|
docstrings = []
|
||||||
symbols = []
|
symbols = []
|
||||||
|
enums = {}
|
||||||
for func in sorted(documentation.keys()):
|
for func in sorted(documentation.keys()):
|
||||||
try:
|
try:
|
||||||
method = documentation[func]
|
method = documentation[func]
|
||||||
|
@ -117,15 +149,21 @@ def create_documentation():
|
||||||
props_inputs = {}
|
props_inputs = {}
|
||||||
symbol_inputs = {}
|
symbol_inputs = {}
|
||||||
parent_props = [prop.identifier for base in method.bl_node_type.__bases__ for prop in base.bl_rna.properties]
|
parent_props = [prop.identifier for base in method.bl_node_type.__bases__ for prop in base.bl_rna.properties]
|
||||||
|
node_namespace_name = func.replace('_', ' ').title().replace(' ', '')
|
||||||
for prop in method.bl_node_type.bl_rna.properties:
|
for prop in method.bl_node_type.bl_rna.properties:
|
||||||
if not prop.identifier in parent_props:
|
if not prop.identifier in parent_props:
|
||||||
if prop.type == 'ENUM':
|
if prop.type == 'ENUM':
|
||||||
enum_items = 'Literal[' + ', '.join(map(lambda i: f"'{i.identifier}'", prop.enum_items)) + ']'
|
enum_name = prop.identifier.replace('_', ' ').title().replace(' ', '')
|
||||||
props_inputs[prop.identifier] = f"<span style=\"color: {color_mappings['STRING']};\">{enum_items}</span>"
|
enum_cases = '\n '.join(map(lambda i: f"{map_case_name(i)} = '{i.identifier}'", prop.enum_items))
|
||||||
symbol_inputs[prop.identifier] = enum_items
|
if node_namespace_name not in enums:
|
||||||
|
enums[node_namespace_name] = []
|
||||||
|
enums[node_namespace_name].append(f""" class {enum_name}(enum.Enum):
|
||||||
|
{enum_cases}""")
|
||||||
|
props_inputs[prop.identifier] = {f"<span style=\"color: {color_mappings['STRING']};\">{node_namespace_name}.{enum_name}</span>":1}
|
||||||
|
symbol_inputs[prop.identifier] = {f"{node_namespace_name}.{enum_name}": 1}
|
||||||
else:
|
else:
|
||||||
props_inputs[prop.identifier] = f"<span style=\"color: {color_mappings.get(prop.type, default_color)};\">{prop.type.title()}</span>"
|
props_inputs[prop.identifier] = {f"<span style=\"color: {color_mappings.get(prop.type, default_color)};\">{prop.type.title()}</span>":1}
|
||||||
symbol_inputs[prop.identifier] = prop.type.title()
|
symbol_inputs[prop.identifier] = {prop.type.title(): 1}
|
||||||
primary_arg = None
|
primary_arg = None
|
||||||
for node_input in node_instance.inputs:
|
for node_input in node_instance.inputs:
|
||||||
name = node_input.name.lower().replace(' ', '_')
|
name = node_input.name.lower().replace(' ', '_')
|
||||||
|
@ -134,13 +172,32 @@ def create_documentation():
|
||||||
typename = f"List[{typename}]"
|
typename = f"List[{typename}]"
|
||||||
type_str = f"<span style=\"color: {color_mappings.get(node_input.type, default_color)};\">{typename}</span>"
|
type_str = f"<span style=\"color: {color_mappings.get(node_input.type, default_color)};\">{typename}</span>"
|
||||||
if name in props_inputs:
|
if name in props_inputs:
|
||||||
props_inputs[name] = props_inputs[name] + f' | {type_str}'
|
if type_str in props_inputs[name]:
|
||||||
symbol_inputs[name] = symbol_inputs[name] + f' | {typename}'
|
props_inputs[name][type_str] += 1
|
||||||
|
symbol_inputs[name][typename] += 1
|
||||||
|
else:
|
||||||
|
props_inputs[name][type_str] = 1
|
||||||
|
symbol_inputs[name][typename] = 1
|
||||||
else:
|
else:
|
||||||
props_inputs[name] = type_str
|
props_inputs[name] = {type_str: 1}
|
||||||
symbol_inputs[name] = typename
|
symbol_inputs[name] = {typename: 1}
|
||||||
if primary_arg is None:
|
if primary_arg is None:
|
||||||
primary_arg = (name, props_inputs[name])
|
primary_arg = (name, list(props_inputs[name].keys())[0])
|
||||||
|
def collapse_inputs(inputs):
|
||||||
|
for k, v in inputs.items():
|
||||||
|
values = []
|
||||||
|
for t, c in v.items():
|
||||||
|
for c in range(1, c + 1):
|
||||||
|
value = ""
|
||||||
|
if c > 1:
|
||||||
|
value += "Tuple["
|
||||||
|
value += ', '.join(itertools.repeat(t, c))
|
||||||
|
if c > 1:
|
||||||
|
value += "]"
|
||||||
|
values.append(value)
|
||||||
|
inputs[k] = ' | '.join(values)
|
||||||
|
collapse_inputs(props_inputs)
|
||||||
|
collapse_inputs(symbol_inputs)
|
||||||
arg_docs = []
|
arg_docs = []
|
||||||
symbol_args = []
|
symbol_args = []
|
||||||
for name, value in props_inputs.items():
|
for name, value in props_inputs.items():
|
||||||
|
@ -181,14 +238,20 @@ def create_documentation():
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
""")
|
""")
|
||||||
output_symbol_separator = '\n '
|
output_symbol_separator = '\n '
|
||||||
symbol_return_type = f"_{func}_result"
|
|
||||||
if len(output_symbols) > 1:
|
if len(output_symbols) > 1:
|
||||||
symbols.append(f"""class {symbol_return_type}:
|
if node_namespace_name not in enums:
|
||||||
{output_symbol_separator.join(output_symbols)}""")
|
enums[node_namespace_name] = []
|
||||||
return_type_hint = list(symbol_outputs.values())[0] if len(output_symbols) == 1 else symbol_return_type
|
enums[node_namespace_name].append(f""" class Result:
|
||||||
|
{output_symbol_separator.join(output_symbols)}""")
|
||||||
|
return_type_hint = list(symbol_outputs.values())[0] if len(output_symbols) == 1 else f"{node_namespace_name}.Result"
|
||||||
symbols.append(f"""def {func}({', '.join(symbol_args)}) -> {return_type_hint}: \"\"\"\"\"\"""")
|
symbols.append(f"""def {func}({', '.join(symbol_args)}) -> {return_type_hint}: \"\"\"\"\"\"""")
|
||||||
except:
|
except Exception as e:
|
||||||
|
import os, sys
|
||||||
|
print(e)
|
||||||
|
exc_type, exc_obj, exc_tb = sys.exc_info()
|
||||||
|
fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
|
||||||
|
print(exc_type, fname, exc_tb.tb_lineno)
|
||||||
continue
|
continue
|
||||||
bpy.data.node_groups.remove(temp_node_group)
|
bpy.data.node_groups.remove(temp_node_group)
|
||||||
html = f"""
|
html = f"""
|
||||||
|
@ -228,15 +291,40 @@ def create_documentation():
|
||||||
newline = '\n'
|
newline = '\n'
|
||||||
def type_symbol(t):
|
def type_symbol(t):
|
||||||
return f"class {t.__name__}(Type): pass"
|
return f"class {t.__name__}(Type): pass"
|
||||||
|
def enum_namespace(k):
|
||||||
|
return f"""class {k}:
|
||||||
|
{newline.join(enums[k])}"""
|
||||||
contents = f"""from typing import *
|
contents = f"""from typing import *
|
||||||
|
import enum
|
||||||
def tree(builder):
|
def tree(builder):
|
||||||
\"\"\"
|
\"\"\"
|
||||||
Marks a function as a node tree.
|
Marks a function as a node tree.
|
||||||
\"\"\"
|
\"\"\"
|
||||||
pass
|
pass
|
||||||
class Type:
|
class Type:
|
||||||
{(newline + ' ').join(filter(lambda x: x.startswith('def'), symbols))}
|
def __add__(self, other) -> Type: return self
|
||||||
|
def __radd__(self, other) -> Type: return self
|
||||||
|
def __sub__(self, other) -> Type: return self
|
||||||
|
def __rsub__(self, other) -> Type: return self
|
||||||
|
def __mul__(self, other) -> Type: return self
|
||||||
|
def __rmul__(self, other) -> Type: return self
|
||||||
|
def __truediv__(self, other) -> Type: return self
|
||||||
|
def __rtruediv__(self, other) -> Type: return self
|
||||||
|
def __mod__(self, other) -> Type: return self
|
||||||
|
def __rmod__(self, other) -> Type: return self
|
||||||
|
def __eq__(self, other) -> Type: return self
|
||||||
|
def __ne__(self, other) -> Type: return self
|
||||||
|
def __lt__(self, other) -> Type: return self
|
||||||
|
def __le__(self, other) -> Type: return self
|
||||||
|
def __gt__(self, other) -> Type: return self
|
||||||
|
def __ge__(self, other) -> Type: return self
|
||||||
|
x = Type()
|
||||||
|
y = Type()
|
||||||
|
z = Type()
|
||||||
|
{(newline + ' ').join(map(lambda x: x.replace('(', '(self, '), filter(lambda x: x.startswith('def'), symbols)))}
|
||||||
|
|
||||||
{newline.join(map(type_symbol, Type.__subclasses__()))}
|
{newline.join(map(type_symbol, Type.__subclasses__()))}
|
||||||
|
{newline.join(map(enum_namespace, enums.keys()))}
|
||||||
{newline.join(symbols)}"""
|
{newline.join(symbols)}"""
|
||||||
fpyi.write(contents)
|
fpyi.write(contents)
|
||||||
fpy.write(contents)
|
fpy.write(contents)
|
||||||
|
|
32
api/types.py
32
api/types.py
|
@ -3,6 +3,15 @@ from bpy.types import NodeSocketStandard
|
||||||
import nodeitems_utils
|
import nodeitems_utils
|
||||||
from .state import State
|
from .state import State
|
||||||
|
|
||||||
|
def map_case_name(i):
|
||||||
|
r = i.identifier.replace('_', ' ').title().replace(' ', '')
|
||||||
|
if r == 'None':
|
||||||
|
return 'NONE'
|
||||||
|
elif not r[0].isalpha():
|
||||||
|
return f'_{r}'
|
||||||
|
else:
|
||||||
|
return r
|
||||||
|
|
||||||
# The base class all exposed socket types conform to.
|
# The base class all exposed socket types conform to.
|
||||||
class Type:
|
class Type:
|
||||||
socket_type: str
|
socket_type: str
|
||||||
|
@ -28,31 +37,46 @@ class Type:
|
||||||
self._socket = socket
|
self._socket = socket
|
||||||
self.socket_type = type(socket).__name__
|
self.socket_type = type(socket).__name__
|
||||||
|
|
||||||
def _math(self, other, operation):
|
def _math(self, other, operation, reverse=False):
|
||||||
math_node = State.current_node_tree.nodes.new('ShaderNodeVectorMath' if self._socket.type == 'VECTOR' else 'ShaderNodeMath')
|
math_node = State.current_node_tree.nodes.new('ShaderNodeVectorMath' if self._socket.type == 'VECTOR' else 'ShaderNodeMath')
|
||||||
math_node.operation = operation
|
math_node.operation = operation
|
||||||
State.current_node_tree.links.new(self._socket, math_node.inputs[0])
|
State.current_node_tree.links.new(self._socket, math_node.inputs[1 if reverse else 0])
|
||||||
if issubclass(type(other), Type):
|
if issubclass(type(other), Type):
|
||||||
State.current_node_tree.links.new(other._socket, math_node.inputs[1])
|
State.current_node_tree.links.new(other._socket, math_node.inputs[0 if reverse else 1])
|
||||||
else:
|
else:
|
||||||
math_node.inputs[1].default_value = other
|
math_node.inputs[0 if reverse else 1].default_value = other
|
||||||
return Type(math_node.outputs[0])
|
return Type(math_node.outputs[0])
|
||||||
|
|
||||||
def __add__(self, other):
|
def __add__(self, other):
|
||||||
return self._math(other, 'ADD')
|
return self._math(other, 'ADD')
|
||||||
|
|
||||||
|
def __radd__(self, other):
|
||||||
|
return self._math(other, 'ADD', True)
|
||||||
|
|
||||||
def __sub__(self, other):
|
def __sub__(self, other):
|
||||||
return self._math(other, 'SUBTRACT')
|
return self._math(other, 'SUBTRACT')
|
||||||
|
|
||||||
|
def __rsub__(self, other):
|
||||||
|
return self._math(other, 'SUBTRACT', True)
|
||||||
|
|
||||||
def __mul__(self, other):
|
def __mul__(self, other):
|
||||||
return self._math(other, 'MULTIPLY')
|
return self._math(other, 'MULTIPLY')
|
||||||
|
|
||||||
|
def __rmul__(self, other):
|
||||||
|
return self._math(other, 'MULTIPLY', True)
|
||||||
|
|
||||||
def __truediv__(self, other):
|
def __truediv__(self, other):
|
||||||
return self._math(other, 'DIVIDE')
|
return self._math(other, 'DIVIDE')
|
||||||
|
|
||||||
|
def __rtruediv__(self, other):
|
||||||
|
return self._math(other, 'DIVIDE', True)
|
||||||
|
|
||||||
def __mod__(self, other):
|
def __mod__(self, other):
|
||||||
return self._math(other, 'MODULO')
|
return self._math(other, 'MODULO')
|
||||||
|
|
||||||
|
def __rmod__(self, other):
|
||||||
|
return self._math(other, 'MODULO', True)
|
||||||
|
|
||||||
def _compare(self, other, operation):
|
def _compare(self, other, operation):
|
||||||
compare_node = State.current_node_tree.nodes.new('FunctionNodeCompare')
|
compare_node = State.current_node_tree.nodes.new('FunctionNodeCompare')
|
||||||
compare_node.data_type = 'FLOAT' if self._socket.type == 'VALUE' else self._socket.type
|
compare_node.data_type = 'FLOAT' if self._socket.type == 'VALUE' else self._socket.type
|
||||||
|
|
|
@ -4,3 +4,8 @@ language = "en"
|
||||||
multilingual = false
|
multilingual = false
|
||||||
src = "src"
|
src = "src"
|
||||||
title = "Geometry Script"
|
title = "Geometry Script"
|
||||||
|
|
||||||
|
[output.html]
|
||||||
|
default-theme = "coal"
|
||||||
|
preferred-dark-theme = "coal"
|
||||||
|
additional-css = ["style.css"]
|
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 25 KiB |
|
@ -121,4 +121,30 @@ The same script without chaining calls is written more verbosely as:
|
||||||
@tree("Cube Tree")
|
@tree("Cube Tree")
|
||||||
def cube_tree(size: Vector):
|
def cube_tree(size: Vector):
|
||||||
return mesh_to_volume(mesh=cube(size=size))
|
return mesh_to_volume(mesh=cube(size=size))
|
||||||
|
```
|
||||||
|
|
||||||
|
### Spanning Multiple Lines
|
||||||
|
|
||||||
|
Often times you want each chained calls to be on a separate line. There are a few ways to do this in Python:
|
||||||
|
|
||||||
|
1. Newlines around arguments
|
||||||
|
|
||||||
|
```python
|
||||||
|
cube(
|
||||||
|
size=size
|
||||||
|
).mesh_to_volume()
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Parentheses
|
||||||
|
|
||||||
|
```python
|
||||||
|
(cube(size=size)
|
||||||
|
.mesh_to_volume())
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Line continuation
|
||||||
|
|
||||||
|
```python
|
||||||
|
cube(size=size) \
|
||||||
|
.mesh_to_volume()
|
||||||
```
|
```
|
|
@ -20,7 +20,47 @@ The general process is:
|
||||||
|
|
||||||
> Properties and inputs are different types of argument. A property is a value that cannot be connected to a socket. These are typically enums (displayed in the UI as a dropdown), with specific string values expected. Check the documentation for a node to see what the possible values are for a property.
|
> Properties and inputs are different types of argument. A property is a value that cannot be connected to a socket. These are typically enums (displayed in the UI as a dropdown), with specific string values expected. Check the documentation for a node to see what the possible values are for a property.
|
||||||
|
|
||||||
Let's take a look at two nodes as an example.
|
## Enum Properties
|
||||||
|
|
||||||
|
Many nodes have enum properties. For example, the math node lets you choose which operation to perform. You can pass a string to specify the enum case to use. But a safer way to set these values is with the autogenerated enum types. The enums are namespaced to the name of the node in PascalCase:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Access it by Node.Enum Name.Case
|
||||||
|
math(operation=Math.Operation.Add)
|
||||||
|
math(operation=Math.Operation.Subtract)
|
||||||
|
math(operation='MULTIPLY') # Or manually pass a string
|
||||||
|
```
|
||||||
|
|
||||||
|
Internally, this type is generated as:
|
||||||
|
|
||||||
|
```python
|
||||||
|
import enum
|
||||||
|
class Math:
|
||||||
|
class Operation(enum.Enum):
|
||||||
|
Add = 'ADD'
|
||||||
|
Subtract = 'SUBTRACT'
|
||||||
|
Multiply = 'MULTIPLY'
|
||||||
|
...
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
The cases will appear in code completion if you setup an [external editor](../../setup/external-editing.md).
|
||||||
|
|
||||||
|
## Duplicate Names
|
||||||
|
|
||||||
|
Some nodes use the same input name multiple times. For example, the *Math* node has three inputs named `value`. To specify each value, pass a tuple for the input:
|
||||||
|
|
||||||
|
```python
|
||||||
|
math(operation=Math.Operation.Wrap, value=(0.5, 1, 0)) # Pass all 3
|
||||||
|
math(operation=Math.Operation.Wrap, value=(0.5, 1)) # Only pass 2/3
|
||||||
|
math(operation=Math.Operation.Wrap, value=0.5) # Only pass 1/3
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
Here are two examples to show how a node maps to a function.
|
||||||
|
|
||||||
### Cube
|
### Cube
|
||||||
|
|
||||||
|
@ -72,8 +112,8 @@ size.cube(...)
|
||||||
1. Name `Capture Attribute` -> `capture_attribute`
|
1. Name `Capture Attribute` -> `capture_attribute`
|
||||||
2. Keyword Arguments
|
2. Keyword Arguments
|
||||||
* Properties
|
* Properties
|
||||||
* `data_type: Literal['FLOAT', 'INT', 'FLOAT_VECTOR', 'FLOAT_COLOR', 'BYTE_COLOR', 'STRING', 'BOOLEAN', 'FLOAT2', 'INT8']`
|
* `data_type: CaptureAttribute.DataType`
|
||||||
* `domain: Literal['POINT', 'EDGE', 'FACE', 'CORNER', 'CURVE', 'INSTANCE']`
|
* `domain: CaptureAttribute.Domain`
|
||||||
* Inputs
|
* Inputs
|
||||||
* `geometry: Geometry`
|
* `geometry: Geometry`
|
||||||
* `value: Vector | Float | Color | Bool | Int`
|
* `value: Vector | Float | Color | Bool | Int`
|
||||||
|
@ -82,7 +122,7 @@ size.cube(...)
|
||||||
The node can now be used as a function:
|
The node can now be used as a function:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
result = capture_attribute(data_type='BOOLEAN', geometry=cube_geo) # Specify a property and an input
|
result = capture_attribute(data_type=CaptureAttribute.DataType.Boolean, geometry=cube_geo) # Specify a property and an input
|
||||||
result.geometry # Access the geometry
|
result.geometry # Access the geometry
|
||||||
result.attribute # Access the attribute
|
result.attribute # Access the attribute
|
||||||
```
|
```
|
||||||
|
@ -92,8 +132,8 @@ The generated documentation will show the signature, result type, and [chain syn
|
||||||
#### Signature
|
#### Signature
|
||||||
```python
|
```python
|
||||||
capture_attribute(
|
capture_attribute(
|
||||||
data_type: Literal['FLOAT', 'INT', 'FLOAT_VECTOR', 'FLOAT_COLOR', 'BYTE_COLOR', 'STRING', 'BOOLEAN', 'FLOAT2', 'INT8'],
|
data_type: CaptureAttribute.DataType,
|
||||||
domain: Literal['POINT', 'EDGE', 'FACE', 'CORNER', 'CURVE', 'INSTANCE'],
|
domain: CaptureAttribute.Domain,
|
||||||
geometry: Geometry,
|
geometry: Geometry,
|
||||||
value: Vector | Float | Color | Bool | Int
|
value: Vector | Float | Color | Bool | Int
|
||||||
)
|
)
|
||||||
|
|
|
@ -3,9 +3,9 @@
|
||||||
Blender's *Text Editor* leaves a lot to be desired. Writing scripts without code completion can be tough.
|
Blender's *Text Editor* leaves a lot to be desired. Writing scripts without code completion can be tough.
|
||||||
Using an external code editor is one way to improve the editing experience.
|
Using an external code editor is one way to improve the editing experience.
|
||||||
|
|
||||||
This guide will show how to setup [Visual Studio Code](https://code.visualstudio.com/) to edit Geometry Scripts. However, the same concepts apply to IDEs.
|
This guide will show how to setup [Visual Studio Code](https://code.visualstudio.com/) to edit Geometry Scripts. However, the same concepts apply to other IDEs.
|
||||||
|
|
||||||
> This guide assumes you have already installed Visual Studio Code and setup the [Python extension](https://marketplace.visualstudio.com/items?itemName=ms-python.python). If not, please follow the setup guides for those tools before continuing.
|
> This guide assumes you have already installed Visual Studio Code and setup the [Python extension](https://marketplace.visualstudio.com/items?itemName=ms-python.python). If not, please setup those tools before continuing.
|
||||||
|
|
||||||
## Code Completion
|
## Code Completion
|
||||||
When the Geometry Script add-on starts, it generates a Python typeshed file that can be used to provide code completion.
|
When the Geometry Script add-on starts, it generates a Python typeshed file that can be used to provide code completion.
|
||||||
|
|
|
@ -26,7 +26,7 @@ def city_builder(
|
||||||
return geometry
|
return geometry
|
||||||
```
|
```
|
||||||
|
|
||||||
Run the script to create the tree, then add a *Geometry Nodes* modifier to your curve object and select the *City Builger* node group.
|
Run the script to create the tree, then add a *Geometry Nodes* modifier to your curve object and select the *City Builder* node group.
|
||||||
|
|
||||||
## Buildings
|
## Buildings
|
||||||
Let's start with the buildings. We'll distribute points on a grid with `size_x` and `size_y`.
|
Let's start with the buildings. We'll distribute points on a grid with `size_x` and `size_y`.
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
.coal {
|
||||||
|
--bg: #1C1C1C !important;
|
||||||
|
}
|
Ładowanie…
Reference in New Issue