forked from LeenkxTeam/LNXSDK
Update Files
This commit is contained in:
25
leenkx/blender/lnx/logicnode/logic/LN_alternate_output.py
Normal file
25
leenkx/blender/lnx/logicnode/logic/LN_alternate_output.py
Normal file
@ -0,0 +1,25 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class AlternateNode(LnxLogicTreeNode):
|
||||
"""Activates the outputs alternating every time it is active."""
|
||||
bl_idname = 'LNAlternateNode'
|
||||
bl_label = 'Alternate Output'
|
||||
lnx_section = 'flow'
|
||||
lnx_version = 2
|
||||
|
||||
def __init__(self):
|
||||
super(AlternateNode, self).__init__()
|
||||
array_nodes[str(id(self))] = self
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
row = layout.row(align=True)
|
||||
|
||||
op = row.operator('lnx.node_add_output', text='New', icon='PLUS', emboss=True)
|
||||
op.node_index = str(id(self))
|
||||
op.socket_type = 'LnxNodeSocketAction'
|
||||
op2 = row.operator('lnx.node_remove_output', text='', icon='X', emboss=True)
|
||||
op2.node_index = str(id(self))
|
||||
|
16
leenkx/blender/lnx/logicnode/logic/LN_branch.py
Normal file
16
leenkx/blender/lnx/logicnode/logic/LN_branch.py
Normal file
@ -0,0 +1,16 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
|
||||
class BranchNode(LnxLogicTreeNode):
|
||||
"""Activates its `true` or `false` output, according
|
||||
to the state of the plugged-in boolean."""
|
||||
bl_idname = 'LNBranchNode'
|
||||
bl_label = 'Branch'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
self.add_input('LnxBoolSocket', 'Bool')
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'True')
|
||||
self.add_output('LnxNodeSocketAction', 'False')
|
42
leenkx/blender/lnx/logicnode/logic/LN_call_function.py
Normal file
42
leenkx/blender/lnx/logicnode/logic/LN_call_function.py
Normal file
@ -0,0 +1,42 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
|
||||
class CallFunctionNode(LnxLogicTreeNode):
|
||||
"""Calls the given function that was created by the [Function](#function) node."""
|
||||
bl_idname = 'LNCallFunctionNode'
|
||||
bl_label = 'Call Function'
|
||||
bl_description = 'Calls a function that was created by the Function node.'
|
||||
lnx_section = 'function'
|
||||
lnx_version = 2
|
||||
min_inputs = 3
|
||||
|
||||
def __init__(self):
|
||||
super(CallFunctionNode, self).__init__()
|
||||
array_nodes[str(id(self))] = self
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
self.add_input('LnxDynamicSocket', 'Trait/Any')
|
||||
self.add_input('LnxStringSocket', 'Function')
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Out')
|
||||
self.add_output('LnxDynamicSocket', 'Result')
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
row = layout.row(align=True)
|
||||
op = row.operator('lnx.node_add_input', text='Add Arg', icon='PLUS', emboss=True)
|
||||
op.node_index = str(id(self))
|
||||
op.socket_type = 'LnxDynamicSocket'
|
||||
op.name_format = "Arg {0}"
|
||||
op.index_name_offset = -2
|
||||
column = row.column(align=True)
|
||||
op = column.operator('lnx.node_remove_input', text='', icon='X', emboss=True)
|
||||
op.node_index = str(id(self))
|
||||
if len(self.inputs) == self.min_inputs:
|
||||
column.enabled = False
|
||||
|
||||
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
|
||||
if self.lnx_version not in (0, 1):
|
||||
raise LookupError()
|
||||
|
||||
return NodeReplacement.Identity(self)
|
60
leenkx/blender/lnx/logicnode/logic/LN_case_index.py
Normal file
60
leenkx/blender/lnx/logicnode/logic/LN_case_index.py
Normal file
@ -0,0 +1,60 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
|
||||
class CaseIndexNode(LnxLogicTreeNode):
|
||||
"""Compare the given `Compare` value with the other inputs for equality
|
||||
and return the index of the first match. This is particularly helpful
|
||||
in combination with the `Select` node.
|
||||
|
||||
@seeNode Select
|
||||
|
||||
@input Compare: the value to be compared
|
||||
@input Value: values for the dynamic comparison
|
||||
|
||||
@output Index: the index of the first equal value, or `null` if no
|
||||
equal value was found.
|
||||
"""
|
||||
bl_idname = 'LNCaseIndexNode'
|
||||
bl_label = 'Case Index'
|
||||
lnx_version = 1
|
||||
min_inputs = 2
|
||||
|
||||
num_choices: IntProperty(default=0, min=0)
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
array_nodes[str(id(self))] = self
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxDynamicSocket', 'Compare')
|
||||
self.add_input_func()
|
||||
|
||||
self.add_output('LnxIntSocket', 'Index')
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
row = layout.row(align=True)
|
||||
op = row.operator('lnx.node_call_func', text='New', icon='PLUS', emboss=True)
|
||||
op.node_index = str(id(self))
|
||||
op.callback_name = 'add_input_func'
|
||||
|
||||
column = row.column(align=True)
|
||||
op = column.operator('lnx.node_call_func', text='', icon='X', emboss=True)
|
||||
op.node_index = str(id(self))
|
||||
op.callback_name = 'remove_input_func'
|
||||
if len(self.inputs) == self.min_inputs:
|
||||
column.enabled = False
|
||||
|
||||
def add_input_func(self):
|
||||
self.add_input('LnxDynamicSocket', f'Value {self.num_choices}')
|
||||
self.num_choices += 1
|
||||
|
||||
def remove_input_func(self):
|
||||
if len(self.inputs) > self.min_inputs:
|
||||
self.inputs.remove(self.inputs[-1])
|
||||
self.num_choices -= 1
|
||||
|
||||
def draw_label(self) -> str:
|
||||
if self.num_choices == 0:
|
||||
return self.bl_label
|
||||
|
||||
return f'{self.bl_label}: [{self.num_choices}]'
|
42
leenkx/blender/lnx/logicnode/logic/LN_function.py
Normal file
42
leenkx/blender/lnx/logicnode/logic/LN_function.py
Normal file
@ -0,0 +1,42 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
|
||||
class FunctionNode(LnxLogicTreeNode):
|
||||
"""Creates a reusable function that can be called by the
|
||||
[Call Function](#call-function) node."""
|
||||
bl_idname = 'LNFunctionNode'
|
||||
bl_label = 'Function'
|
||||
bl_description = 'Creates a reusable function that can be called by the Call Function node'
|
||||
lnx_section = 'function'
|
||||
lnx_version = 2
|
||||
min_outputs = 1
|
||||
|
||||
def __init__(self):
|
||||
super(FunctionNode, self).__init__()
|
||||
array_nodes[str(id(self))] = self
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_output('LnxNodeSocketAction', 'Out')
|
||||
|
||||
function_name: StringProperty(name="Name")
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
row = layout.row(align=True)
|
||||
row.prop(self, 'function_name')
|
||||
row = layout.row(align=True)
|
||||
op = row.operator('lnx.node_add_output', text='Add Arg', icon='PLUS', emboss=True)
|
||||
op.node_index = str(id(self))
|
||||
op.socket_type = 'LnxDynamicSocket'
|
||||
op.name_format = "Arg {0}"
|
||||
op.index_name_offset = 0
|
||||
column = row.column(align=True)
|
||||
op = column.operator('lnx.node_remove_output', text='', icon='X', emboss=True)
|
||||
op.node_index = str(id(self))
|
||||
if len(self.outputs) == self.min_outputs:
|
||||
column.enabled = False
|
||||
|
||||
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
|
||||
if self.lnx_version not in (0, 1):
|
||||
raise LookupError()
|
||||
|
||||
return NodeReplacement.Identity(self)
|
21
leenkx/blender/lnx/logicnode/logic/LN_function_output.py
Normal file
21
leenkx/blender/lnx/logicnode/logic/LN_function_output.py
Normal file
@ -0,0 +1,21 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
|
||||
class FunctionOutputNode(LnxLogicTreeNode):
|
||||
"""Sets the return value for the given function.
|
||||
|
||||
@seeNode Function"""
|
||||
bl_idname = 'LNFunctionOutputNode'
|
||||
bl_label = 'Function Output'
|
||||
lnx_section = 'function'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
self.add_input('LnxDynamicSocket', 'Value')
|
||||
|
||||
function_name: StringProperty(name="Name")
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
row = layout.row(align=True)
|
||||
row.prop(self, 'function_name')
|
77
leenkx/blender/lnx/logicnode/logic/LN_gate.py
Normal file
77
leenkx/blender/lnx/logicnode/logic/LN_gate.py
Normal file
@ -0,0 +1,77 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
|
||||
def remove_extra_inputs(self, context):
|
||||
if not any(p == self.property0 for p in ['Or', 'And']):
|
||||
while len(self.inputs) > self.min_inputs:
|
||||
self.inputs.remove(self.inputs[-1])
|
||||
if self.property0 == 'Between':
|
||||
self.add_input('LnxDynamicSocket', 'Input 3')
|
||||
|
||||
class GateNode(LnxLogicTreeNode):
|
||||
"""Logic nodes way to do "if" statements. When activated, it
|
||||
compares if its two inputs are being Equal, Greater Equal,
|
||||
Less Equal, Not Equal, or Between, regardless of variable type, and passes
|
||||
through its active input to the output that matches the result of
|
||||
the comparison.
|
||||
|
||||
"And" and "Or" are being used for booleans only, and pass through
|
||||
the input when both booleans are true (And) or at least one (Or)."""
|
||||
bl_idname = 'LNGateNode'
|
||||
bl_label = 'Gate'
|
||||
lnx_version = 3
|
||||
min_inputs = 3
|
||||
|
||||
property0: HaxeEnumProperty(
|
||||
'property0',
|
||||
items = [('Equal', 'Equal', 'Equal'),
|
||||
('Not Equal', 'Not Equal', 'Not Equal'),
|
||||
('Almost Equal', 'Almost Equal', 'Almost Equal'),
|
||||
('Greater', 'Greater', 'Greater'),
|
||||
('Greater Equal', 'Greater Equal', 'Greater Equal'),
|
||||
('Less', 'Less', 'Less'),
|
||||
('Less Equal', 'Less Equal', 'Less Equal'),
|
||||
('Between', 'Between', 'Input 1 Between Input 2 and Input 3 inclusive'),
|
||||
('Or', 'Or', 'Or'),
|
||||
('And', 'And', 'And')],
|
||||
name='', default='Equal',
|
||||
update=remove_extra_inputs)
|
||||
property1: HaxeFloatProperty('property1', name='Tolerance', description='Precision for float compare', default=0.0001)
|
||||
|
||||
def __init__(self):
|
||||
super(GateNode, self).__init__()
|
||||
array_nodes[str(id(self))] = self
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
self.add_input('LnxDynamicSocket', 'Input 1')
|
||||
self.add_input('LnxDynamicSocket', 'Input 2')
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'True')
|
||||
self.add_output('LnxNodeSocketAction', 'False')
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.prop(self, 'property0')
|
||||
|
||||
if self.property0 == 'Almost Equal':
|
||||
layout.prop(self, 'property1')
|
||||
|
||||
if any(p == self.property0 for p in ['Or', 'And']):
|
||||
row = layout.row(align=True)
|
||||
op = row.operator('lnx.node_add_input', text='New', icon='PLUS', emboss=True)
|
||||
op.node_index = str(id(self))
|
||||
op.socket_type = 'LnxDynamicSocket'
|
||||
column = row.column(align=True)
|
||||
op = column.operator('lnx.node_remove_input', text='', icon='X', emboss=True)
|
||||
op.node_index = str(id(self))
|
||||
if len(self.inputs) == self.min_inputs:
|
||||
column.enabled = False
|
||||
|
||||
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
|
||||
if self.lnx_version == 1 or self.lnx_version == 2:
|
||||
return NodeReplacement(
|
||||
'LNGateNode', self.lnx_version, 'LNGateNode', 3,
|
||||
in_socket_mapping={0:0, 1:1, 2:2}, out_socket_mapping={0:0, 1:1}
|
||||
)
|
||||
else:
|
||||
raise LookupError()
|
12
leenkx/blender/lnx/logicnode/logic/LN_invert_boolean.py
Normal file
12
leenkx/blender/lnx/logicnode/logic/LN_invert_boolean.py
Normal file
@ -0,0 +1,12 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class NotNode(LnxLogicTreeNode):
|
||||
"""Inverts the plugged-in boolean. If its input is `true` it outputs `false`."""
|
||||
bl_idname = 'LNNotNode'
|
||||
bl_label = 'Invert Boolean'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxBoolSocket', 'Bool In')
|
||||
|
||||
self.add_output('LnxBoolSocket', 'Bool Out')
|
14
leenkx/blender/lnx/logicnode/logic/LN_invert_output.py
Normal file
14
leenkx/blender/lnx/logicnode/logic/LN_invert_output.py
Normal file
@ -0,0 +1,14 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
|
||||
class InverseNode(LnxLogicTreeNode):
|
||||
"""Activates the output if the input is not active."""
|
||||
bl_idname = 'LNInverseNode'
|
||||
bl_label = 'Invert Output'
|
||||
lnx_section = 'flow'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Out')
|
17
leenkx/blender/lnx/logicnode/logic/LN_is_false.py
Normal file
17
leenkx/blender/lnx/logicnode/logic/LN_is_false.py
Normal file
@ -0,0 +1,17 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
|
||||
class IsFalseNode(LnxLogicTreeNode):
|
||||
"""Passes through its activation only if the plugged-in boolean
|
||||
equals `false`.
|
||||
|
||||
@seeNode Is True"""
|
||||
bl_idname = 'LNIsFalseNode'
|
||||
bl_label = 'Is False'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
self.add_input('LnxBoolSocket', 'Bool')
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Out')
|
16
leenkx/blender/lnx/logicnode/logic/LN_is_not_null.py
Normal file
16
leenkx/blender/lnx/logicnode/logic/LN_is_not_null.py
Normal file
@ -0,0 +1,16 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class IsNotNoneNode(LnxLogicTreeNode):
|
||||
"""Passes through its activation only if the plugged-in value is
|
||||
not `null`.
|
||||
|
||||
@seeNode Is Null"""
|
||||
bl_idname = 'LNIsNotNoneNode'
|
||||
bl_label = 'Is Not Null'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
self.add_input('LnxDynamicSocket', 'Value')
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Out')
|
17
leenkx/blender/lnx/logicnode/logic/LN_is_null.py
Normal file
17
leenkx/blender/lnx/logicnode/logic/LN_is_null.py
Normal file
@ -0,0 +1,17 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
|
||||
class IsNoneNode(LnxLogicTreeNode):
|
||||
"""Passes through its activation only if the plugged-in value is
|
||||
`null` (no value).
|
||||
|
||||
@seeNode Is Not Null"""
|
||||
bl_idname = 'LNIsNoneNode'
|
||||
bl_label = 'Is Null'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
self.add_input('LnxDynamicSocket', 'Value')
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Out')
|
16
leenkx/blender/lnx/logicnode/logic/LN_is_true.py
Normal file
16
leenkx/blender/lnx/logicnode/logic/LN_is_true.py
Normal file
@ -0,0 +1,16 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class IsTrueNode(LnxLogicTreeNode):
|
||||
"""Passes through its activation only if the plugged-in boolean
|
||||
equals `true`.
|
||||
|
||||
@seeNode Is False"""
|
||||
bl_idname = 'LNIsTrueNode'
|
||||
bl_label = 'Is True'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
self.add_input('LnxBoolSocket', 'Bool')
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Out')
|
41
leenkx/blender/lnx/logicnode/logic/LN_loop.py
Normal file
41
leenkx/blender/lnx/logicnode/logic/LN_loop.py
Normal file
@ -0,0 +1,41 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class LoopNode(LnxLogicTreeNode):
|
||||
"""Resembles a for-loop (`for (i in from...to)`) that is executed at
|
||||
once when this node is activated.
|
||||
|
||||
@seeNode While
|
||||
@seeNode Loop Break
|
||||
|
||||
@input From: The value to start the loop from (inclusive)
|
||||
@input To: The value to end the loop at (exclusive)
|
||||
|
||||
@output Loop: Active at every iteration of the loop
|
||||
@output Index: The index for the current iteration
|
||||
@output Done: Activated once when the looping is done
|
||||
"""
|
||||
bl_idname = 'LNLoopNode'
|
||||
bl_label = 'Loop'
|
||||
bl_description = 'Resembles a for-loop that is executed at once when this node is activated'
|
||||
lnx_section = 'flow'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
self.add_input('LnxIntSocket', 'From')
|
||||
self.add_input('LnxIntSocket', 'To')
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Loop')
|
||||
self.add_output('LnxIntSocket', 'Index')
|
||||
self.add_output('LnxNodeSocketAction', 'Done')
|
||||
|
||||
def draw_label(self) -> str:
|
||||
inp_from = self.inputs['From']
|
||||
inp_to = self.inputs['To']
|
||||
if inp_from.is_linked and inp_to.is_linked:
|
||||
return self.bl_label
|
||||
|
||||
val_from = 'x' if inp_from.is_linked else inp_from.default_value_raw
|
||||
val_to = 'y' if inp_to.is_linked else inp_to.default_value_raw
|
||||
|
||||
return f'{self.bl_label}: {val_from}...{val_to}'
|
16
leenkx/blender/lnx/logicnode/logic/LN_loop_break.py
Normal file
16
leenkx/blender/lnx/logicnode/logic/LN_loop_break.py
Normal file
@ -0,0 +1,16 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class LoopBreakNode(LnxLogicTreeNode):
|
||||
"""Terminates the currently executing loop (only one loop is
|
||||
executed at once).
|
||||
|
||||
@seeNode Loop
|
||||
@seeNode While
|
||||
"""
|
||||
bl_idname = 'LNLoopBreakNode'
|
||||
bl_label = 'Loop Break'
|
||||
lnx_section = 'flow'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
15
leenkx/blender/lnx/logicnode/logic/LN_loop_continue.py
Normal file
15
leenkx/blender/lnx/logicnode/logic/LN_loop_continue.py
Normal file
@ -0,0 +1,15 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class LoopContinueNode(LnxLogicTreeNode):
|
||||
"""continues to the next loop.
|
||||
|
||||
@seeNode Loop
|
||||
@seeNode While
|
||||
"""
|
||||
bl_idname = 'LNLoopContinueNode'
|
||||
bl_label = 'Loop Continue'
|
||||
lnx_section = 'flow'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
91
leenkx/blender/lnx/logicnode/logic/LN_merge.py
Normal file
91
leenkx/blender/lnx/logicnode/logic/LN_merge.py
Normal file
@ -0,0 +1,91 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
|
||||
class MergeNode(LnxLogicTreeNode):
|
||||
"""Activates the output when at least one connected input is activated.
|
||||
If multiple inputs are active, the behaviour is specified by the
|
||||
`Execution Mode` option.
|
||||
|
||||
@output Active Input Index: [*Available if Execution Mode is set to
|
||||
Once Per Input*] The index of the last input that activated the output,
|
||||
-1 if there was no execution yet on the current frame.
|
||||
|
||||
@option Execution Mode: The node's behaviour if multiple inputs are
|
||||
active on the same frame.
|
||||
- `Once Per Input`: If multiple inputs are active on one frame, activate
|
||||
the output for each active input individually (simple forwarding).
|
||||
- `Once Per Frame`: If multiple inputs are active on one frame,
|
||||
trigger the output only once.
|
||||
|
||||
@option New: Add a new input socket.
|
||||
@option X Button: Remove the lowermost input socket."""
|
||||
bl_idname = 'LNMergeNode'
|
||||
bl_label = 'Merge'
|
||||
lnx_section = 'flow'
|
||||
lnx_version = 3
|
||||
min_inputs = 0
|
||||
|
||||
def update_exec_mode(self, context):
|
||||
self.outputs['Active Input Index'].hide = self.property0 == 'once_per_frame'
|
||||
|
||||
property0: HaxeEnumProperty(
|
||||
'property0',
|
||||
name='Execution Mode',
|
||||
description='The node\'s behaviour if multiple inputs are active on the same frame',
|
||||
items=[('once_per_input', 'Once Per Input',
|
||||
'If multiple inputs are active on one frame, activate the'
|
||||
' output for each active input individually (simple forwarding)'),
|
||||
('once_per_frame', 'Once Per Frame',
|
||||
'If multiple inputs are active on one frame, trigger the output only once')],
|
||||
default='once_per_input',
|
||||
update=update_exec_mode,
|
||||
)
|
||||
|
||||
def __init__(self):
|
||||
super(MergeNode, self).__init__()
|
||||
array_nodes[str(id(self))] = self
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_output('LnxNodeSocketAction', 'Out')
|
||||
self.add_output('LnxIntSocket', 'Active Input Index')
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.prop(self, 'property0', text='')
|
||||
|
||||
row = layout.row(align=True)
|
||||
op = row.operator('lnx.node_add_input', text='New', icon='PLUS', emboss=True)
|
||||
op.node_index = str(id(self))
|
||||
op.socket_type = 'LnxNodeSocketAction'
|
||||
column = row.column(align=True)
|
||||
op = column.operator('lnx.node_remove_input', text='', icon='X', emboss=True)
|
||||
op.node_index = str(id(self))
|
||||
if len(self.inputs) == self.min_inputs:
|
||||
column.enabled = False
|
||||
|
||||
def draw_label(self) -> str:
|
||||
if len(self.inputs) == self.min_inputs:
|
||||
return self.bl_label
|
||||
|
||||
return f'{self.bl_label}: [{len(self.inputs)}]'
|
||||
|
||||
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
|
||||
if self.lnx_version not in (0, 2):
|
||||
raise LookupError()
|
||||
|
||||
if self.lnx_version == 1 or self.lnx_version == 2:
|
||||
newnode = node_tree.nodes.new('LNMergeNode')
|
||||
newnode.property0 = self.property0
|
||||
|
||||
# Recreate all original inputs
|
||||
array_nodes[str(id(newnode))] = newnode
|
||||
for idx, input in enumerate(self.inputs):
|
||||
bpy.ops.lnx.node_add_input('EXEC_DEFAULT', node_index=str(id(newnode)), socket_type='LnxNodeSocketAction')
|
||||
|
||||
for link in input.links:
|
||||
node_tree.links.new(link.from_socket, newnode.inputs[idx])
|
||||
|
||||
# Recreate outputs
|
||||
for link in self.outputs[0].links:
|
||||
node_tree.links.new(newnode.outputs[0], link.to_socket)
|
||||
|
||||
return newnode
|
10
leenkx/blender/lnx/logicnode/logic/LN_null.py
Normal file
10
leenkx/blender/lnx/logicnode/logic/LN_null.py
Normal file
@ -0,0 +1,10 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class NoneNode(LnxLogicTreeNode):
|
||||
"""A `null` value that can be used in comparisons and conditions."""
|
||||
bl_idname = 'LNNoneNode'
|
||||
bl_label = 'Null'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_output('LnxDynamicSocket', 'Null')
|
15
leenkx/blender/lnx/logicnode/logic/LN_once_per_frame.py
Normal file
15
leenkx/blender/lnx/logicnode/logic/LN_once_per_frame.py
Normal file
@ -0,0 +1,15 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
|
||||
class OncePerFrameNode(LnxLogicTreeNode):
|
||||
"""Activates the output only once per frame if receives one or more inputs in that frame
|
||||
If there is no input, there will be no output"""
|
||||
bl_idname = 'LNOncePerFrameNode'
|
||||
bl_label = 'Once Per Frame'
|
||||
lnx_section = 'flow'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Out')
|
34
leenkx/blender/lnx/logicnode/logic/LN_output_sequence.py
Normal file
34
leenkx/blender/lnx/logicnode/logic/LN_output_sequence.py
Normal file
@ -0,0 +1,34 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class SequenceNode(LnxLogicTreeNode):
|
||||
"""Activates the outputs one by one sequentially and repeatedly."""
|
||||
bl_idname = 'LNSequenceNode'
|
||||
bl_label = 'Output Sequence'
|
||||
lnx_section = 'flow'
|
||||
lnx_version = 2
|
||||
min_outputs = 0
|
||||
|
||||
def __init__(self):
|
||||
super(SequenceNode, self).__init__()
|
||||
array_nodes[self.get_id_str()] = self
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
row = layout.row(align=True)
|
||||
|
||||
op = row.operator('lnx.node_add_output', text='New', icon='PLUS', emboss=True)
|
||||
op.node_index = self.get_id_str()
|
||||
op.socket_type = 'LnxNodeSocketAction'
|
||||
column = row.column(align=True)
|
||||
op = column.operator('lnx.node_remove_output', text='', icon='X', emboss=True)
|
||||
op.node_index = self.get_id_str()
|
||||
if len(self.outputs) == self.min_outputs:
|
||||
column.enabled = False
|
||||
|
||||
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
|
||||
if self.lnx_version not in (0, 1):
|
||||
raise LookupError()
|
||||
|
||||
return NodeReplacement.Identity(self)
|
13
leenkx/blender/lnx/logicnode/logic/LN_output_to_boolean.py
Normal file
13
leenkx/blender/lnx/logicnode/logic/LN_output_to_boolean.py
Normal file
@ -0,0 +1,13 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class ToBoolNode(LnxLogicTreeNode):
|
||||
"""Converts a signal to a boolean value. If the input signal is
|
||||
active, the boolean is `true`; if not, the boolean is `false`."""
|
||||
bl_idname = 'LNToBoolNode'
|
||||
bl_label = 'Output to Boolean'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
|
||||
self.add_output('LnxBoolSocket', 'Bool')
|
19
leenkx/blender/lnx/logicnode/logic/LN_pulse.py
Normal file
19
leenkx/blender/lnx/logicnode/logic/LN_pulse.py
Normal file
@ -0,0 +1,19 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class PulseNode(LnxLogicTreeNode):
|
||||
"""Sends a signal repeatedly between the given time interval until you stop it.
|
||||
|
||||
@input Start: Starts to send the signals
|
||||
@input Stop: Stops to send the signals
|
||||
@input Interval: The interval between the signals
|
||||
"""
|
||||
bl_idname = 'LNPulseNode'
|
||||
bl_label = 'Pulse'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'Start')
|
||||
self.add_input('LnxNodeSocketAction', 'Stop')
|
||||
self.add_input('LnxFloatSocket', 'Interval', default_value=0.1)
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Out')
|
128
leenkx/blender/lnx/logicnode/logic/LN_select.py
Normal file
128
leenkx/blender/lnx/logicnode/logic/LN_select.py
Normal file
@ -0,0 +1,128 @@
|
||||
try:
|
||||
from bpy.types import NodeSocketInterfaceInt
|
||||
except:
|
||||
from bpy.types import NodeTreeInterfaceSocketInt
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class SelectNode(LnxLogicTreeNode):
|
||||
"""Selects one of multiple values (of arbitrary types) based on some
|
||||
input state. The exact behaviour of this node is specified by the
|
||||
`Execution Mode` option (see below).
|
||||
|
||||
@output Out: [*Available if Execution Mode is set to From Input*]
|
||||
Activated after the node was executed.
|
||||
|
||||
@output Value: The last selected value. This value is not reset
|
||||
until the next execution of this node.
|
||||
|
||||
@option Execution Mode: Specifies the condition that determines
|
||||
what value to choose.
|
||||
- `From Index`: Select the value at the given index. If there is
|
||||
no value at that index, the value plugged in to the
|
||||
`Default` input is used instead (`null` if unconnected).
|
||||
- `From Input`: This mode uses input pairs of one action socket
|
||||
and one value socket. Depending on which action socket is
|
||||
activated, the associated value socket (the value with the
|
||||
same index as the activated action input) is forwarded to
|
||||
the `Value` output.
|
||||
|
||||
@option New: Add a new value to the list of values.
|
||||
@option X Button: Remove the value with the highest index."""
|
||||
bl_idname = 'LNSelectNode'
|
||||
bl_label = 'Select'
|
||||
lnx_version = 2
|
||||
min_inputs = 2
|
||||
|
||||
def update_exec_mode(self, context):
|
||||
self.set_mode()
|
||||
|
||||
property0: HaxeEnumProperty(
|
||||
'property0',
|
||||
name='Execution Mode',
|
||||
description="The node's behaviour.",
|
||||
items=[
|
||||
('from_index', 'From Index', 'Choose the value from the given index'),
|
||||
('from_input', 'From Input', 'Choose the value with the same position as the active input')],
|
||||
default='from_index',
|
||||
update=update_exec_mode,
|
||||
)
|
||||
|
||||
# The number of choices, NOT of individual inputs. This needs to be
|
||||
# a property in order to be saved with each individual node
|
||||
num_choices: IntProperty(default=1, min=0)
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
array_nodes[str(id(self))] = self
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.set_mode()
|
||||
|
||||
def set_mode(self):
|
||||
self.inputs.clear()
|
||||
self.outputs.clear()
|
||||
|
||||
if self.property0 == 'from_index':
|
||||
self.add_input('LnxIntSocket', 'Index')
|
||||
self.add_input('LnxDynamicSocket', 'Default')
|
||||
self.num_choices = 0
|
||||
|
||||
# from_input
|
||||
else:
|
||||
# We could also start with index 1 here, but we need to use
|
||||
# 0 for the "from_index" mode and it makes the code simpler
|
||||
# if we stick to the same convention for both exec modes
|
||||
self.add_input('LnxNodeSocketAction', 'Input 0')
|
||||
self.add_input('LnxDynamicSocket', 'Value 0')
|
||||
self.num_choices = 1
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Out')
|
||||
|
||||
self.add_output('LnxDynamicSocket', 'Value')
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.prop(self, 'property0', text='')
|
||||
|
||||
row = layout.row(align=True)
|
||||
op = row.operator('lnx.node_call_func', text='New', icon='PLUS', emboss=True)
|
||||
op.node_index = str(id(self))
|
||||
op.callback_name = 'add_input_func'
|
||||
|
||||
column = row.column(align=True)
|
||||
op = column.operator('lnx.node_call_func', text='', icon='X', emboss=True)
|
||||
op.node_index = str(id(self))
|
||||
op.callback_name = 'remove_input_func'
|
||||
if len(self.inputs) == self.min_inputs:
|
||||
column.enabled = False
|
||||
|
||||
def add_input_func(self):
|
||||
if self.property0 == 'from_input':
|
||||
self.add_input('LnxNodeSocketAction', f'Input {self.num_choices}')
|
||||
|
||||
# Move new action input up to the end of all other action inputs
|
||||
self.inputs.move(from_index=len(self.inputs) - 1, to_index=self.num_choices)
|
||||
|
||||
self.add_input('LnxDynamicSocket', f'Value {self.num_choices}')
|
||||
|
||||
self.num_choices += 1
|
||||
|
||||
def remove_input_func(self):
|
||||
if self.property0 == 'from_input':
|
||||
if len(self.inputs) > self.min_inputs:
|
||||
self.inputs.remove(self.inputs[self.num_choices - 1])
|
||||
|
||||
if len(self.inputs) > self.min_inputs:
|
||||
self.inputs.remove(self.inputs[-1])
|
||||
self.num_choices -= 1
|
||||
|
||||
def draw_label(self) -> str:
|
||||
if self.num_choices == 0:
|
||||
return self.bl_label
|
||||
|
||||
return f'{self.bl_label}: [{self.num_choices}]'
|
||||
|
||||
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
|
||||
if self.lnx_version not in (0, 1):
|
||||
raise LookupError()
|
||||
|
||||
return NodeReplacement.Identity(self)
|
40
leenkx/blender/lnx/logicnode/logic/LN_select_output.py
Normal file
40
leenkx/blender/lnx/logicnode/logic/LN_select_output.py
Normal file
@ -0,0 +1,40 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class SelectOutputNode(LnxLogicTreeNode):
|
||||
"""Selects one of multiple outputs depending on the index.
|
||||
|
||||
@input In: Action input.
|
||||
|
||||
@input Index: Output index to run.
|
||||
|
||||
@output Default: Run if output index not present.
|
||||
"""
|
||||
|
||||
bl_idname = 'LNSelectOutputNode'
|
||||
bl_label = 'Select output'
|
||||
lnx_version = 1
|
||||
min_outputs = 2
|
||||
|
||||
def __init__(self):
|
||||
super(SelectOutputNode, self).__init__()
|
||||
array_nodes[self.get_id_str()] = self
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
self.add_input('LnxIntSocket', 'Index')
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Default')
|
||||
self.add_output('LnxNodeSocketAction', 'Index 0')
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
row = layout.row(align=True)
|
||||
op = row.operator('lnx.node_add_output', text='New', icon='PLUS', emboss=True)
|
||||
op.node_index = self.get_id_str()
|
||||
op.socket_type = 'LnxNodeSocketAction'
|
||||
op.name_format = 'Index {0}'
|
||||
op.index_name_offset = -1
|
||||
column = row.column(align=True)
|
||||
op = column.operator('lnx.node_remove_output', text='', icon='X', emboss=True)
|
||||
op.node_index = self.get_id_str()
|
||||
if len(self.outputs) == self.min_outputs:
|
||||
column.enabled = False
|
43
leenkx/blender/lnx/logicnode/logic/LN_switch_output.py
Normal file
43
leenkx/blender/lnx/logicnode/logic/LN_switch_output.py
Normal file
@ -0,0 +1,43 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class SwitchNode(LnxLogicTreeNode):
|
||||
"""Activates the outputs depending of the value. If the "value" is equal to "case 1", the output "case 1" will be activated.
|
||||
|
||||
@output Default: Activated if the input value does not match any case.
|
||||
"""
|
||||
bl_idname = 'LNSwitchNode'
|
||||
bl_label = 'Switch Output'
|
||||
lnx_version = 4
|
||||
min_inputs = 2
|
||||
|
||||
def __init__(self):
|
||||
super(SwitchNode, self).__init__()
|
||||
array_nodes[self.get_id_str()] = self
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
self.add_input('LnxDynamicSocket', 'Value')
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Default')
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
row = layout.row(align=True)
|
||||
op = row.operator('lnx.node_add_input_output', text='New', icon='PLUS', emboss=True)
|
||||
op.node_index = self.get_id_str()
|
||||
op.in_socket_type = 'LnxDynamicSocket'
|
||||
op.out_socket_type = 'LnxNodeSocketAction'
|
||||
op.in_name_format = 'Case {0}'
|
||||
op.out_name_format = 'Case {0}'
|
||||
op.in_index_name_offset = -1
|
||||
op.out_index_name_offset = -1
|
||||
column = row.column(align=True)
|
||||
op = column.operator('lnx.node_remove_input_output', text='', icon='X', emboss=True)
|
||||
op.node_index = self.get_id_str()
|
||||
if len(self.inputs) == self.min_inputs:
|
||||
column.enabled = False
|
||||
|
||||
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
|
||||
if self.lnx_version not in (0, 3):
|
||||
raise LookupError()
|
||||
|
||||
return NodeReplacement.Identity(self)
|
38
leenkx/blender/lnx/logicnode/logic/LN_value_changed.py
Normal file
38
leenkx/blender/lnx/logicnode/logic/LN_value_changed.py
Normal file
@ -0,0 +1,38 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
|
||||
class ValueChangedNode(LnxLogicTreeNode):
|
||||
"""Upon activation through the `In` input, this node checks whether
|
||||
the given value is different than the value from the last execution
|
||||
of this node.
|
||||
|
||||
@output Changed: Activates if the value has changed compared to the
|
||||
last time the node was executed or if the node is executed for
|
||||
the first time and there is no value for comparison yet.
|
||||
@output Unchanged: Activates if the value is the same as it was when
|
||||
the node was executed the last time.
|
||||
@output Is Initial: Activates if the value is equal to the value at
|
||||
the first time the node was executed or if the node is executed
|
||||
for the first time. This output works independently of the
|
||||
`Changed` or `Unchanged` outputs.
|
||||
"""
|
||||
bl_idname = 'LNValueChangedNode'
|
||||
bl_label = 'Value Changed'
|
||||
lnx_version = 2
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
self.add_input('LnxDynamicSocket', 'Value')
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Changed')
|
||||
self.add_output('LnxNodeSocketAction', 'Unchanged')
|
||||
self.add_output('LnxNodeSocketAction', 'Is Initial')
|
||||
|
||||
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
|
||||
if self.lnx_version not in (0, 1):
|
||||
raise LookupError()
|
||||
|
||||
return NodeReplacement(
|
||||
'LNValueChangedNode', self.lnx_version, 'LNValueChangedNode', 2,
|
||||
in_socket_mapping={0: 0, 1: 1}, out_socket_mapping={0: 0, 1: 2}
|
||||
)
|
31
leenkx/blender/lnx/logicnode/logic/LN_wait_for.py
Normal file
31
leenkx/blender/lnx/logicnode/logic/LN_wait_for.py
Normal file
@ -0,0 +1,31 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class WaitForNode(LnxLogicTreeNode):
|
||||
"""
|
||||
Activate the output when all inputs have been activated at least once since the node's initialization.
|
||||
Use This node for parallel flows. Inputs don't need to be active at the same point in time.
|
||||
|
||||
@input Input[0-n]: list of inputs to be activated
|
||||
@output Output: output triggered when all inputs are activated
|
||||
"""
|
||||
bl_idname = 'LNWaitForNode'
|
||||
bl_label = 'Wait for All Inputs'
|
||||
lnx_section = 'flow'
|
||||
lnx_version = 1
|
||||
|
||||
def __init__(self):
|
||||
super(WaitForNode, self).__init__()
|
||||
array_nodes[str(id(self))] = self
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_output('LnxNodeSocketAction', 'Out')
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
row = layout.row(align=True)
|
||||
|
||||
op = row.operator('lnx.node_add_input', text='New', icon='PLUS', emboss=True)
|
||||
op.node_index = str(id(self))
|
||||
op.socket_type = 'LnxNodeSocketAction'
|
||||
op2 = row.operator('lnx.node_remove_input', text='', icon='X', emboss=True)
|
||||
op2.node_index = str(id(self))
|
||||
|
23
leenkx/blender/lnx/logicnode/logic/LN_while_true.py
Normal file
23
leenkx/blender/lnx/logicnode/logic/LN_while_true.py
Normal file
@ -0,0 +1,23 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class WhileNode(LnxLogicTreeNode):
|
||||
"""Loops while the condition is `true`.
|
||||
|
||||
@seeNode Loop
|
||||
@seeNode Loop Break
|
||||
|
||||
@input Condition: boolean that resembles the result of the condition
|
||||
|
||||
@output Loop: Activated on every iteration step
|
||||
@output Done: Activated when the loop is done executing"""
|
||||
bl_idname = 'LNWhileNode'
|
||||
bl_label = 'While True'
|
||||
lnx_section = 'flow'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
self.add_input('LnxBoolSocket', 'Condition')
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Loop')
|
||||
self.add_output('LnxNodeSocketAction', 'Done')
|
4
leenkx/blender/lnx/logicnode/logic/__init__.py
Normal file
4
leenkx/blender/lnx/logicnode/logic/__init__.py
Normal file
@ -0,0 +1,4 @@
|
||||
from lnx.logicnode.lnx_nodes import add_node_section
|
||||
|
||||
add_node_section(name='flow', category='Logic')
|
||||
add_node_section(name='function', category='Logic')
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user