Не подтверждена Коммит 193d62f1 создал по автору Victor Doval's avatar Victor Doval Зафиксировано автором GitHub
Просмотр файлов

New Oscillator node (#2652)

* New Oscillator node

* typo fix

* code documenting

* docs to index

* Oscillator now in Numpy and with icons
владелец a6513cc6
......@@ -208,7 +208,7 @@ list_match_func = {
"XREF": match_cross,
"XREF2": match_cross2
}
#####################################################
################# list levels magic #################
......@@ -277,6 +277,18 @@ def levelsOflist(lst):
return level
return 0
def levels_of_list_or_np(lst):
"""calc list nesting only in countainment level integer"""
level = 1
for n in lst:
if isinstance(n, (list, tuple)):
level += levels_of_list_or_np(n)
elif isinstance(n, (np.ndarray)):
level += len(n.shape)
return level
return 0
def get_data_nesting_level(data, data_types=(float, int, np.float64, str)):
"""
data: number, or list of numbers, or list of lists, etc.
......
Fibonacci Sequence
==================
*destination after Beta: Number*
Functionality
-------------
......@@ -23,7 +21,7 @@ All parameters can be given by the node or an external input.
This node has the following parameters:
+----------------+---------------+-------------+----------------------------------------------------+
| Parameter | Type | Default | Description |
| Parameter | Type | Default | Description |
+================+===============+=============+====================================================+
| **X1** | Float | 1.0 | First item of sequence. |
+----------------+---------------+-------------+----------------------------------------------------+
......@@ -54,4 +52,3 @@ Given simplest nodes setup:
you will have something like:
.. image:: https://cloud.githubusercontent.com/assets/284644/5691664/227c9d0e-98f5-11e4-87c9-fb6f552e1b89.png
......@@ -4,8 +4,8 @@ List Input
Functionality
-------------
Provides a way to creat a flat list of *Integers*, *Floats*, or *Vectors*.
The length of the list is hardcoded to a maximum of **32** elements for integer or float and **10** vectors,
Provides a way to create a flat list of *Integers*, *Floats*, or *Vectors*.
The length of the list is hardcoded to a maximum of **32** elements for integer or float and **10** vectors,
we believe that if you need more then you should use a Text File and the Text In node.
Parameters
......@@ -31,4 +31,3 @@ Useful when you have no immediate need to generate such lists programmatically.
.. image:: https://cloud.githubusercontent.com/assets/619340/4186018/0f61f4b6-375d-11e4-99c8-6f7ef62598b3.PNG
:alt: ListInputDemo2.PNG
......@@ -18,3 +18,4 @@ Number
range_map
numbers
scalar_mk3
oscillator
Oscillator
==========
Functionality
-------------
This node creates a oscillation signal from a lineal range.
Inputs & Parameters
-------------------
All parameters except for **Mode** can be given by the node or an external input.
This node has the following parameters:
+----------------+----------+-----------------------------------------------------------------------+
| Parameter | Type | Description |
+================+==========+=======================================================================+
| **Mode** | Enum | Sine, Square, Saw, Triangle, Custom |
+----------------+----------+-----------------------------------------------------------------------+
| **Value** | Float | Point(s) in time to evaluate |
+----------------+----------+-----------------------------------------------------------------------+
| **Amplitude** | Float | Amplitude of the wave |
+----------------+----------+-----------------------------------------------------------------------+
| **Period** | Float | Time (Value) to make a full cycle of the wave. If you want to use the |
| | | frequency as input remember that 1/frequency = period |
+----------------+----------+-----------------------------------------------------------------------+
| **Phase** | Float | Starting Phase of the wave |
+----------------+----------+-----------------------------------------------------------------------+
| **Offset** | Float | Value added the wave |
+----------------+----------+-----------------------------------------------------------------------+
| **Wave** | Vertices | On Custom mode you can input a path of points (more than 1 vector) |
| | | to create a custom wave. The node will evaluate the Y value |
+----------------+----------+-----------------------------------------------------------------------+
Advanced Parameters
-------------------
In the N-Panel (and on the right-click menu) you can find:
**Wave Interpolation**: (On Custom mode) Define how the wave should be interpolated (Linear or Cubic)
**Wave Knots Mode**: (On Custom mode) Define how the wave knots should be interpolated (Manhattan, Euclidan, Points or Chebyshev)
**Output NumPy**: Get NumPy arrays in stead of regular lists (makes the node faster)
**List Match**: Define how list with different lengths should be matched
Outputs
-------
This node has one output: **Out**.
Inputs and outputs are vectorized, so if series of values is passed to one of
inputs, then this node will produce several sequences.
Example of usage
----------------
Generating basic waves:
.. image:: https://raw.githubusercontent.com/vicdoval/sverchok/docs_images/images_for_docs/number/Oscillator/Oscillator_example_01.png
The "Wave" mode allows you to use a custom wave shape:
.. image:: https://raw.githubusercontent.com/vicdoval/sverchok/docs_images/images_for_docs/number/Oscillator/Oscillator_example_02.png
As with the musical synths you can create complex waves out of mixing the basics:
.. image:: https://raw.githubusercontent.com/vicdoval/sverchok/docs_images/images_for_docs/number/Oscillator/Oscillator_example_03.png
Surface modeled by a combination of Oscillator nodes.
.. image:: https://raw.githubusercontent.com/vicdoval/sverchok/docs_images/images_for_docs/number/Oscillator/Oscillator_example_04.png
......@@ -208,6 +208,7 @@
---
SvGenFibonacci
SvGenExponential
SvOscillatorNode
## Vector
GenVectorsNode
......
# ##### 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 #####
from math import pi
import bpy
from bpy.props import EnumProperty, FloatProperty, BoolProperty
from sverchok.ui.sv_icons import custom_icon
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode, list_match_func, list_match_modes
from sverchok.utils.sv_itertools import (recurse_f_level_control)
from sverchok.utils.geom import LinearSpline, CubicSpline
import numpy as np
mode_Items = [
("Sine", "Sine", "Sinusoidal wave", custom_icon("SV_OSCILLATOR_SINE"), 0),
("Square", "Square", "Square wave", custom_icon("SV_OSCILLATOR_INT"), 1),
("Saw", "Saw", "Saw wave", custom_icon("SV_OSCILLATOR_SAW"), 2),
("Triangular", "Triangle", "Triangular", custom_icon("SV_OSCILLATOR_TRI"), 3),
("Custom", "Custom", "Custom wave", custom_icon("SV_OSCILLATOR_WAVE"), 4),
]
def oscillator(params, constant, matching_f):
result = []
mode, spline_func, knots, out_numpy = constant
params = matching_f(params)
for props in zip(*params):
regular_prop = matching_f(props[:5])
wave = props[5]
val, amplitude, period, phase, offset = [np.array(prop) for prop in regular_prop]
if mode == 'Sine':
res = amplitude * np.sin((val / period + phase) * pi*2) + offset
elif mode == 'Square':
act_phase = np.ones(amplitude.shape)
mask = np.sin((val / period + phase) *pi*2) < 0
act_phase[mask] = -1
res = amplitude * act_phase + offset
elif mode == 'Saw':
res = amplitude - amplitude * (((val / period + phase) * 2) % 2) + offset
elif mode == 'Triangular':
mask = ((val / period + phase) * 2) % 2 > 1
res = 2 * amplitude * (((val / period + phase)*2) % 1) - amplitude
res[mask] *= -1
res += offset
elif mode == 'Custom' and wave:
spline = spline_func(wave, metric=knots, is_cyclic=False)
val = spline.eval(np.abs(val / period + phase) % 1)
res = amplitude * np.array(val)[:, 1] + offset
result.append(res if out_numpy else res.tolist())
return result
class SvOscillatorNode(bpy.types.Node, SverchCustomTreeNode):
"""
Triggers: Sine, Saw, Square
Tooltip: Generate a oscillating values from a lineal values .
"""
bl_idname = 'SvOscillatorNode'
bl_label = 'Oscillator'
sv_icon = 'SV_OSCILLATOR'
def mode_change(self, context):
self.update_sockets()
updateNode(self, context)
current_op: EnumProperty(
name="Function", description="Function choice", default="Sine",
items=mode_Items, update=mode_change)
x_: FloatProperty(default=1.0, name='Value', update=updateNode)
amplitude: FloatProperty(default=1.0, name='Amplitude', update=updateNode)
period: FloatProperty(default=1.0, name='Period', update=updateNode)
phase: FloatProperty(default=1.0, name='Phase', update=updateNode)
addition: FloatProperty(default=1.0, name='Offset', update=updateNode)
wave_interp_modes = [('SPL', 'Cubic', "Cubic Spline", 0),
('LIN', 'Linear', "Linear Interpolation", 1)]
wave_interp_mode: EnumProperty(
name='Mode', default="LIN",
items=wave_interp_modes, update=updateNode)
knot_modes = [('MANHATTAN', 'Manhattan', "Manhattan distance metric", 0),
('DISTANCE', 'Euclidan', "Eudlcian distance metric", 1),
('POINTS', 'Points', "Points based", 2),
('CHEBYSHEV', 'Chebyshev', "Chebyshev distance", 3)]
knot_mode: EnumProperty(
name='Knot Mode', default="DISTANCE",
items=knot_modes, update=updateNode)
wave_interp: EnumProperty(
name="Wave Interpolation",
description="Behavior on different list lengths",
items=list_match_modes, default="REPEAT",
update=updateNode)
list_match: EnumProperty(
name="List Match",
description="Behavior on different list lengths",
items=list_match_modes, default="REPEAT",
update=updateNode)
output_numpy: BoolProperty(
name='Output NumPy',
description='Output NumPy arrays',
default=False, update=updateNode)
def draw_label(self):
label = [self.current_op, "Oscillator"]
return " ".join(label)
def draw_buttons(self, ctx, layout):
row = layout.row(align=True)
row.prop(self, "current_op", text="")
def draw_buttons_ext(self, ctx, layout):
layout.row().prop(self, "current_op", text="", icon_value=custom_icon("SV_FUNCTION"))
layout.label(text="Wave interpolation options:")
if self.current_op == 'Custom':
layout.row().prop(self, "wave_interp_mode", expand=True)
layout.row().prop(self, "knot_mode", expand=False)
layout.prop(self, "list_match", expand=False)
layout.prop(self, "output_numpy", expand=False)
def rclick_menu(self, context, layout):
layout.prop_menu_enum(self, "current_op", text="Function")
if self.current_op == 'Custom':
layout.prop_menu_enum(self, "wave_interp_mode", text="Wave Interpolation")
layout.prop_menu_enum(self, "knot_mode", text="Wave knots mode")
layout.prop_menu_enum(self, "list_match", text="List Match")
layout.prop(self, "output_numpy", expand=False)
def sv_init(self, context):
self.inputs.new('SvStringsSocket', "Value").prop_name = 'x_'
self.inputs.new('SvStringsSocket', "Amplitude").prop_name = 'amplitude'
self.inputs.new('SvStringsSocket', "Period").prop_name = 'period'
self.inputs.new('SvStringsSocket', "Phase").prop_name = 'phase'
self.inputs.new('SvStringsSocket', "Offset").prop_name = 'addition'
self.inputs.new('SvVerticesSocket', "Wave")
self.outputs.new('SvStringsSocket', "Out")
self.inputs["Wave"].hide_safe = True
def update_sockets(self):
if self.current_op == "Custom":
if self.inputs["Wave"].hide_safe == True:
self.inputs["Wave"].hide_safe = False
else:
self.inputs["Wave"].hide_safe = True
def process(self):
if self.outputs[0].is_linked:
if self.current_op == 'Custom' and not self.inputs["Wave"].is_linked:
return
params = [si.sv_get(default=[[]], deepcopy=False) for si in self.inputs[:6]]
matching_f = list_match_func[self.list_match]
spline_func = CubicSpline if self.wave_interp_mode == 'SPL' else LinearSpline
desired_levels = [2, 2, 2, 2, 2, 3]
ops = [self.current_op, spline_func, self.knot_mode, self.output_numpy]
result = recurse_f_level_control(params, ops, oscillator, matching_f, desired_levels)
self.outputs[0].sv_set(result)
classes = [SvOscillatorNode]
register, unregister = bpy.utils.register_classes_factory(classes)
from itertools import chain, repeat, zip_longest
from sverchok.data_structure import levels_of_list_or_np
# the class based should be slower but kept until tested
class SvZipExhausted(Exception):
......@@ -25,7 +25,7 @@ class SvSentinel:
class sv_zip_longest:
def __init__(self, *args):
self.counter = len(args)
self.counter = len(args)
self.iterators = []
for lst in args:
fl = lst[-1]
......@@ -33,7 +33,7 @@ class sv_zip_longest:
self.iterators.append(chain(lst, SvSentinel(fl,self), filler))
def __next__(self):
try:
try:
if self.counter:
return tuple(map(next, self.iterators))
else:
......@@ -42,7 +42,7 @@ class sv_zip_longest:
raise StopIteration
def __iter__(self):
return self
return self
def sv_zip_longest2(*args):
......@@ -51,8 +51,8 @@ def sv_zip_longest2(*args):
itrs = [iter(sl) for sl in args]
for i in range(longest):
yield tuple((next(iterator, args[idx][-1]) for idx, iterator in enumerate(itrs)))
def recurse_fx(l, f):
if isinstance(l, (list, tuple)):
return [recurse_fx(i, f) for i in l]
......@@ -61,9 +61,9 @@ def recurse_fx(l, f):
def recurse_fxy(l1, l2, f):
l1_type = isinstance(l1, (list, tuple))
l2_type = isinstance(l2, (list, tuple))
l2_type = isinstance(l2, (list, tuple))
if not (l1_type or l2_type):
return f(l1, l2)
return f(l1, l2)
elif l1_type and l2_type:
fl = l2[-1] if len(l1) > len(l2) else l1[-1]
res = []
......@@ -76,6 +76,68 @@ def recurse_fxy(l1, l2, f):
else: #not l1_type and l2_type
return [recurse_fxy(l1, y, f) for y in l2]
def recurse_f_multipar(params, f, matching_f):
'''params will spread using the matching function (matching_f)
on the lowest level applys f (function)'''
is_list = [isinstance(l, (list, tuple)) for l in params]
if any(is_list):
res = []
if not all(is_list):
l_temp = []
for l in params:
if not isinstance(l, (list, tuple)):
l_temp.append([l])
else:
l_temp.append(l)
params = l_temp
params = matching_f(params)
for z in zip(*params):
res.append(recurse_f_multipar(z,f, matching_f))
return res
else:
return f(params)
def recurse_f_multipar_const(params, const, f, matching_f):
'''params will spread using the matching function, the const is a constant
parameter that you dont want to spread '''
is_list = [isinstance(l, (list, tuple)) for l in params]
if any(is_list):
res = []
if not all(is_list):
l_temp = []
for l in params:
if not isinstance(l, (list, tuple)):
l_temp.append([l])
else:
l_temp.append(l)
params = l_temp
params = matching_f(params)
for z in zip(*params):
res.append(recurse_f_multipar_const(z, const, f, matching_f))
return res
else:
return f(params, const)
def recurse_f_level_control(params, constant, main_func, matching_f, desired_levels):
'''params will spread using the matching function (matching_f), the const is a constant
parameter that you dont want to spread , the main_func is the function to apply
and the desired_levels should be like [1, 2, 1, 3...] one level per parameter'''
input_levels = [levels_of_list_or_np(p) for p in params]
over_levels = [lv > dl for lv, dl in zip(input_levels, desired_levels)]
if any(over_levels):
p_temp = []
result = []
for p, lv, dl in zip(params, input_levels, desired_levels):
if lv <= dl:
p_temp.append([p])
else:
p_temp.append(p)
params = matching_f(p_temp)
for g in zip(*params):
result.append(recurse_f_level_control(matching_f(g), constant, main_func, matching_f, desired_levels))
else:
result = main_func(params, constant, matching_f)
return result
def extend_if_needed(vl, wl, default=0.5):
# match wl to correspond with vl
......
Поддерживает Markdown
0% или .
You are about to add 0 people to the discussion. Proceed with caution.
Сначала завершите редактирование этого сообщения!
Пожалуйста, зарегистрируйтесь или чтобы прокомментировать