From 63d34dd3406cf035c6fb0f6b7204446471ed1950 Mon Sep 17 00:00:00 2001 From: DolphinDream Date: Fri, 1 May 2020 22:55:22 -0400 Subject: [PATCH] Add new RangeSwitch node This node toggles a switch state ON/OFF based on a driven value relative to a given value range. --- docs/nodes/logic/logic_index.rst | 1 + docs/nodes/logic/range_switch.rst | 83 ++++++++++++++ index.md | 1 + nodes/logic/range_switch.py | 184 ++++++++++++++++++++++++++++++ 4 files changed, 269 insertions(+) create mode 100644 docs/nodes/logic/range_switch.rst create mode 100644 nodes/logic/range_switch.py diff --git a/docs/nodes/logic/logic_index.rst b/docs/nodes/logic/logic_index.rst index 3141b8db8..6c94608f7 100644 --- a/docs/nodes/logic/logic_index.rst +++ b/docs/nodes/logic/logic_index.rst @@ -10,3 +10,4 @@ Logic switch_MK2 input_switch_mod custom_switcher + range_switch \ No newline at end of file diff --git a/docs/nodes/logic/range_switch.rst b/docs/nodes/logic/range_switch.rst new file mode 100644 index 000000000..60e946b03 --- /dev/null +++ b/docs/nodes/logic/range_switch.rst @@ -0,0 +1,83 @@ +Range Switch +============ + +Functionality +------------- + +This node set a switch state to ON/OFF based on a driving value relative to a given value range. + + +Inputs +------ + +All inputs will accept single values. + +- **Value** +- **Boundary1** [1] +- **Boundary2** [1] + +Notes: +[1] : The boundary values do not have to be in increasing order or absolute. + + +Parameters +---------- + +The **Mode** parameter allows to select one of the three switch modes: **Inside ON**, **Inside OFF** and **Pass Through**. + +- For **Inside ON** mode the switch is ON when the value is inside the boundary range and OFF otherwise. +- For **Inside OFF** mode the switch is OFF when the value is inside the boundary range and ON otherwise. +- For **Pass Through** mode the switch will toggle betwen OFF/ON (or ON/OFF) when the value crossess the range from one side to the other. + + (-) ----[0]---outside---[b1]---inside---[b2]---outside ---> (+) + + INSIDE ON mode: + + OFF |b1| ON |b2| OFF # inside ON, outside OFF + + INSIDE OFF mode: + + ON |b1| OFF |b2| ON # inside OFF, outside ON + + PASS THROUGH mode: + + ON -> |b1| - ON -> |b2| -> OFF # pass through switches state + ON <- |b1| <- OFF - |b2| <- OFF # + ++------------------+---------------+--------------+----------------------------------+ +| Param | Type | Default | Description | ++==================+===============+==============+==================================+ +| **Mode** | Enum | Pass Through | The switching mode [1] | +| | Inside ON | | | +| | Inside OFF | | | +| | Pass Through | | | ++------------------+---------------+--------------+----------------------------------+ +| **Value** | Float | 1.0 | The driving value of the switch | ++------------------+---------------+--------------+----------------------------------+ +| **Boundary1** | Float | 2.0 | The first boundary of the range | ++------------------+---------------+--------------+----------------------------------+ +| **Boundary2** | Float | 3.0 | The second boundary of the range | ++------------------+---------------+--------------+----------------------------------+ + +Notes: +[1] : When the mode is **Pass Through** a "Toggle State" button becomes available to allow you to set the initial state of the switch. + + +Outputs +------- +Outputs will be generated when connected. + +**State** +This output is the current state of the switch. (ON = True, OFF = False) + +**Zone** +This output is the number of the zone (1,2,3) the driving value is in relative to the boundary values. + +Zone 1 = value < min(b1,b2) +Zone 2 = min(b1,b2) < value < max(b1, b2) +Zone 3 = max(b1,b2) < value + + +Example of usage +---------------- + diff --git a/index.md b/index.md index 6d4727735..32e7c30b8 100644 --- a/index.md +++ b/index.md @@ -377,6 +377,7 @@ SvInputSwitchNodeMOD SvNeuroElman1LNode SvCustomSwitcher + SvRangeSwitchNode ## Viz Sv3DviewPropsNode diff --git a/nodes/logic/range_switch.py b/nodes/logic/range_switch.py new file mode 100644 index 000000000..2a6775d60 --- /dev/null +++ b/nodes/logic/range_switch.py @@ -0,0 +1,184 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +import bpy +from bpy.props import IntProperty, FloatProperty, BoolProperty, StringProperty, EnumProperty + +from sverchok.node_tree import SverchCustomTreeNode +from sverchok.data_structure import updateNode, match_long_repeat + +MODE_INSIDE_ON = "INSIDE ON" +MODE_INSIDE_OFF = "INSIDE OFF" +MODE_PASS_THROUGH = "PASS THROUGH" + +switch_mode_items = [(MODE_INSIDE_ON, "Inside ON", "", 1), + (MODE_INSIDE_OFF, "Inside OFF", "", 2), + (MODE_PASS_THROUGH, "Pass Through", "", 3)] + + +class SvSwitchOperatorCallback(bpy.types.Operator): + ''' Callbacks to the main node ''' + bl_idname = "nodes.sv_switcher_callback" + bl_label = "Sv Ops Switcher callback" + + function_name: StringProperty() # what function to call + + def execute(self, context): + n = context.node + getattr(n, self.function_name)(context) + return {"FINISHED"} + + +class SvRangeSwitchNode(bpy.types.Node, SverchCustomTreeNode): + """ + Triggers: Switch, Range + Tooltip: Switches state for a value relative to a value range. + """ + bl_idname = 'SvRangeSwitchNode' + bl_label = 'Range Switch' + bl_icon = "SNAP_INCREMENT" + + """ + (-) ----[0]---outside---[b1]---inside---[b2]---outside ---> (+) + + INSIDE ON mode: + + OFF |b1| ON |b2| OFF # inside ON, outside OFF + + INSIDE OFF mode: + + ON |b1| OFF |b2| ON # inside OFF, outside ON + + PASS THROUGH mode: + + ON -> |b1| - ON -> |b2| -> OFF # pass through switches state + ON <- |b1| <- OFF - |b2| <- OFF # + """ + mode: EnumProperty( + name="Mode", description="Select a switch behavior", + items=switch_mode_items, + default=MODE_PASS_THROUGH, + update=updateNode) + + range_val: FloatProperty( + name="Value", description="Value", + default=0.0, + update=updateNode) + + range_b1: FloatProperty( + name="Boundary 1", description="First boundary of the range", + default=1.0, + update=updateNode) + + range_b2: FloatProperty( + name="Boundary 2", description="Second boundary of the range", + default=2.0, + update=updateNode) + + state: BoolProperty( + name="State", description="Current state of the switch", + default=False) + + last_zone: IntProperty( + name="Last Zone", description="Last zone number (1,2,3)", + default=1) + + def draw_buttons(self, context, layout): + layout.prop(self, "mode", text="") + + if self.mode == MODE_PASS_THROUGH: + cb = SvSwitchOperatorCallback.bl_idname + button = layout.operator(cb, text='Toggle Switch') + button.function_name = "toggle_state" + + def toggle_state(self, context): + self.state = not self.state + updateNode(self, context) + + def get_zone(self, val, minB, maxB): + if val < minB: + return 1 + elif val < maxB: + return 2 + else: # val > maxB + return 3 + + def sv_init(self, context): + self.width = 150 + self.inputs.new('SvStringsSocket', "val").prop_name = 'range_val' + self.inputs.new('SvStringsSocket', "b1").prop_name = 'range_b1' + self.inputs.new('SvStringsSocket', "b2").prop_name = 'range_b2' + + self.outputs.new('SvStringsSocket', "State") + self.outputs.new('SvStringsSocket', "Zone") + + def process(self): + # return if no outputs are connected + if not any(s.is_linked for s in self.outputs): + return + + # input values lists (single or multi value) + input_val = self.inputs["val"].sv_get()[0][0] + input_b1 = self.inputs["b1"].sv_get()[0][0] + input_b2 = self.inputs["b2"].sv_get()[0][0] + + input_val = abs(input_val) + input_b1 = abs(input_b1) + input_b2 = abs(input_b2) + + val = input_val + minB = min(input_b1, input_b2) + maxB = max(input_b1, input_b2) + + zone = self.get_zone(val, minB, maxB) + + if self.mode == MODE_INSIDE_ON: + if zone == 2: + self.state = True + else: + self.state = False + + elif self.mode == MODE_INSIDE_OFF: + if zone == 2: + self.state = False + else: + self.state = True + + else: # PASS through switch ON->OFF, OFF->ON + if self.last_zone == 1: + if zone == 3: + self.last_zone = zone + self.state = not self.state + + elif self.last_zone == 3: + if zone == 1: + self.last_zone = zone + self.state = not self.state + + self.outputs["State"].sv_set([[self.state]]) + self.outputs["Zone"].sv_set([[zone]]) + + +def register(): + bpy.utils.register_class(SvSwitchOperatorCallback) + bpy.utils.register_class(SvRangeSwitchNode) + + +def unregister(): + bpy.utils.unregister_class(SvRangeSwitchNode) + bpy.utils.unregister_class(SvSwitchOperatorCallback) -- GitLab