Update Files

This commit is contained in:
2025-01-22 16:18:30 +01:00
parent ed4603cf95
commit a36294b518
16718 changed files with 2960346 additions and 0 deletions

View 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))

View 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')

View 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)

View 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}]'

View 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)

View 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')

View 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()

View 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')

View 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')

View 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')

View 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')

View 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')

View 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')

View 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}'

View 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')

View 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')

View 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

View 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')

View 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')

View 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)

View 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')

View 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')

View 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)

View 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

View 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)

View 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}
)

View 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))

View 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')

View 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')