From f0cdcb0cad4acdd98ad9f832a1d33a2f97408929 Mon Sep 17 00:00:00 2001 From: Durman Date: Sat, 21 Dec 2019 10:27:25 +0400 Subject: [PATCH 1/9] vectorizing and refactoring --- nodes/generator/line_mk3.py | 261 ++++++++++++++++++++++-------------- 1 file changed, 159 insertions(+), 102 deletions(-) diff --git a/nodes/generator/line_mk3.py b/nodes/generator/line_mk3.py index e1ba7110f..2df5cdba6 100644 --- a/nodes/generator/line_mk3.py +++ b/nodes/generator/line_mk3.py @@ -16,11 +16,15 @@ # # ##### END GPL LICENSE BLOCK ##### +from itertools import chain, cycle +import numpy as np + import bpy from bpy.props import IntProperty, FloatProperty, BoolProperty, EnumProperty, FloatVectorProperty + from sverchok.node_tree import SverchCustomTreeNode -from sverchok.data_structure import updateNode, fullList, match_long_repeat -from sverchok.utils.modules.geom_utils import interp_v3_v3v3, normalize, add_v3_v3v3, sub_v3_v3v3 +from sverchok.data_structure import updateNode + directionItems = [ ("X", "X", "Along X axis", 0), @@ -30,25 +34,121 @@ directionItems = [ ("OD", "OD", "Origin and Direction", 4), ] - -def make_line(steps, center, direction, vert_a, vert_b): - if direction == "X": - vec = lambda l: (l, 0.0, 0.0) - elif direction == "Y": - vec = lambda l: (0.0, l, 0.0) - elif direction == "Z": - vec = lambda l: (0.0, 0.0, l) - elif direction in ["AB", "OD"]: - vec = lambda l: interp_v3_v3v3(vert_a, vert_b, l) - - verts = [] - add_vert = verts.append - x = -sum(steps) / 2 if center else 0 - for s in [0.0] + steps: - x = x + s - add_vert(vec(x)) - edges = [[i, i + 1] for i in range(len(steps))] - return verts, edges + +def make_line(numbers, steps, sizes, mode='X', normalized=False, center=False): + """ + Generate simple lines along X, Y or Z axis. All lines locates in one object. + :param numbers: Number of vertices, list of int + :param steps: Step length, list of float + :param sizes: Size of lines in normalized mode, list of floats + :param mode: 'X' or 'Y' or 'Z' + :param normalized: fit line into given size + :param center: move center of line in center of coordinates + :return: list of vertices, list of edges + """ + max_len = max(len(numbers), len(sizes), len(steps)) + numbers = chain(numbers, cycle([numbers[-1]])) + steps = chain(steps, cycle([steps[-1]])) + sizes = chain(sizes, cycle([sizes[-1]])) + verts_lines = [] + edges_lines = [] + + for i, n, st, size in zip(range(max_len), numbers, steps, sizes): + if normalized and center: + co1, co2 = -size / 2, size / 2 + elif normalized: + co1, co2 = 0, size + elif center: + co1, co2 = -st * (n - 1) / 2, st * (n - 1) / 2 + else: + co1, co2 = 0, st * (n - 1) + va = np.array((co1 if mode == "X" else 0, co1 if mode == "Y" else 0, co1 if mode == "Z" else 0)) + vb = np.array((co2 if mode == "X" else 0, co2 if mode == "Y" else 0, co2 if mode == "Z" else 0)) + edges_lines.extend((i + len(verts_lines), i + len(verts_lines) + 1) for i in range(1 if n <= 2 else n - 1)) + verts_lines.extend(generate_verts(va, vb, n).tolist()) + return verts_lines, edges_lines + + +def make_line_advanced(numbers, steps, sizes, verts_a, verts_b, mode='AB', normalized=False, center=False): + """ + Generate lines between two given points in 'AB' mode or determined by origin(vert_a) and direction(vert_b) + :param numbers: Number of vertices, list of int + :param steps: Step length, list of float + :param sizes: Size of lines in normalized mode, list of floats + :param verts_a: or origin, list of vertices + :param verts_b: or direction, list of vertices + :param mode: 'AB' - generate line between points, 'OD' - generate line from origin with determined direction + :param normalized: fit line into given size + :param center: move center of line into vert_a + :return: list of vertices, list of edges (*one object) + """ + max_len = max(len(numbers), len(steps), len(sizes), len(verts_a), len(verts_b)) + numbers = chain(numbers, cycle([numbers[-1]])) + steps = chain(steps, cycle([steps[-1]])) + sizes = chain(sizes, cycle([sizes[-1]])) + verts_a = chain(verts_a, cycle([verts_a[-1]])) + verts_b = chain(verts_b, cycle([verts_b[-1]])) + verts_lines = [] + edges_lines = [] + + for i, n, st, size, va, vb in zip(range(max_len), numbers, steps, sizes, verts_a, verts_b): + va, vb = np.array(va), np.array(vb) + if normalized or center: + # if center is true then va becomes center + + if mode == 'AB': + len_line = np.linalg.norm(vb - va) + dir_line = (vb - va) * 1 / len_line + else: + len_line = st * (n - 1 if n > 2 else 1) + dir_line = (vb - va) * 1 / np.linalg.norm(vb - va) + + if normalized and center: + verts_line = generate_verts(va + dir_line * (-size / 2), va + dir_line * (size / 2), n) + elif normalized: + verts_line = generate_verts(va, va + dir_line * size, n) + elif center: + verts_line = generate_verts(va + dir_line * (-len_line / 2), va + dir_line * (len_line / 2), n) + else: + if mode == 'AB': + verts_line = generate_verts(va, vb, n) + else: + len_line = st * (n - 1 if n > 2 else 1) + dir_line = (vb - va) * 1 / np.linalg.norm(vb - va) + verts_line = generate_verts(va, va + dir_line * len_line, n) + + edges_lines.extend((i + len(verts_lines), i + len(verts_lines) + 1) for i in range(1 if n <= 2 else n - 1)) + verts_lines.extend(verts_line.tolist()) + return verts_lines, edges_lines + + +def generate_verts(va, vb, number): + # interpolate vertices between two given + if number <= 2: + return np.array((va, vb)) + x = np.linspace(va[0], vb[0], number) + y = np.linspace(va[1], vb[1], number) + z = np.linspace(va[2], vb[2], number) + return np.stack((x, y, z), axis=-1) + + +def split_lines_to_objects(verts, edges): + # detect lines and split them into separate objects + # vertices and edges should be ordered according generator lines logic + verts = iter(verts) + verts_out = [[]] + + for i in range(len(edges)): + verts_out[-1].append(next(verts)) + # current edge - (0, 1), next edge - (1, 2) - still on the same line + # current edge - (1, 2), next edge - (3, 4) - the current line is finished + is_end = True if i + 1 >= len(edges) else True if edges[i][1] != edges[i + 1][0] else False + if is_end: + verts_out[-1].append(next(verts)) + if i != len(edges) - 1: + verts_out.append([]) + edges_out = [[(i, i + 1) for i in range(len(vs) - 1)] for vs in verts_out] + return verts_out, edges_out class SvLineNodeMK3(bpy.types.Node, SverchCustomTreeNode): @@ -63,9 +163,7 @@ class SvLineNodeMK3(bpy.types.Node, SverchCustomTreeNode): def update_size_socket(self, context): """ need to do UX transformation before updating node""" - size_socket = self.inputs["Size"] - size_socket.hide_safe = not self.normalize - + self.inputs["Size"].hide_safe = not self.normalize updateNode(self, context) def update_vect_socket(self, context): @@ -134,27 +232,21 @@ class SvLineNodeMK3(bpy.types.Node, SverchCustomTreeNode): size=3, default=(1, 1, 1), update=updateNode) - def set_size_socket(self): - size_socket = self.inputs.new('SvStringsSocket', "Size") - size_socket.prop_name = 'size' - size_socket.hide_safe = not self.normalize - - def set_vector_sockets(self): - si = self.inputs - si.new('SvVerticesSocket', "A").prop_name = 'v3_input_0' - si.new('SvVerticesSocket', "B").prop_name = 'v3_input_1' - si[3].hide_safe = self.direction not in ["AB", " OD"] - si[4].hide_safe = self.direction not in ["AB", " OD"] + split: BoolProperty(name="Split to objects", description="Each object in separate object", update=updateNode) def sv_init(self, context): - si = self.inputs - si.new('SvStringsSocket', "Num").prop_name = 'num' - si.new('SvStringsSocket', "Step").prop_name = 'step' - self.set_size_socket() - self.set_vector_sockets() + self.inputs.new('SvStringsSocket', "Num").prop_name = 'num' + self.inputs.new('SvStringsSocket', "Step").prop_name = 'step' + self.inputs.new('SvStringsSocket', "Size").prop_name = 'size' + self.inputs.new('SvVerticesSocket', "A").prop_name = 'v3_input_0' + self.inputs.new('SvVerticesSocket', "B").prop_name = 'v3_input_1' self.outputs.new('SvVerticesSocket', "Vertices") self.outputs.new('SvStringsSocket', "Edges") + self.inputs['Size'].hide_safe = True + self.inputs["A"].hide_safe = True + self.inputs["B"].hide_safe = True + def draw_buttons(self, context, layout): col = layout.column(align=True) row = col.row(align=True) @@ -163,74 +255,39 @@ class SvLineNodeMK3(bpy.types.Node, SverchCustomTreeNode): row.prop(self, "center", toggle=True) row.prop(self, "normalize", toggle=True) - def get_data(self): - c, d = self.center, self.direction - input_num = self.inputs["Num"].sv_get() - input_step = self.inputs["Step"].sv_get() - normal_size = [2.0 if c else 1.0] - - if self.normalize: - - normal_size = self.inputs["Size"].sv_get()[0] - - params = [input_num, input_step, normal_size] - - if d in ["AB", "OD"]: - v_a = self.inputs[3].sv_get()[0] - v_b = self.inputs[4].sv_get()[0] - params.append(v_a) - params.append(v_b) - - return match_long_repeat(params) - - def define_steplist(self, step_list, s, n, nor, normal): - - for num in n: - num = max(2, num) - s = s[:(num - 1)] # shorten if needed - fullList(s, num - 1) # extend if needed - step_list.append([S * nor / sum(s) for S in s] if normal else s) - - def process_vectors(self, pts_list, d, va, vb): - if d == "AB" and self.normalize: - vb = add_v3_v3v3(normalize(sub_v3_v3v3(vb, va)), va) - elif d == "OD": - vb = add_v3_v3v3(normalize(vb), va) - pts_list.append((va, vb)) + def draw_buttons_ext(self, context, layout): + layout.prop(self, 'split') def process(self): if not any(s.is_linked for s in self.outputs): return - c, d = self.center, self.direction - step_list = [] - pts_list = [] - verts_out, edges_out = [], [] - normal = self.normalize or d == "AB" - advanced = d in ["AB", "OD"] - params = self.get_data() - if advanced: - for p in zip(*params): - n, s, nor, va, vb = p - self.define_steplist(step_list, s, n, nor, normal) - self.process_vectors(pts_list, d, va, vb) - for s, vc in zip(step_list, pts_list): - r1, r2 = make_line(s, c, d, vc[0], vc[1]) - verts_out.append(r1) - edges_out.append(r2) + number, step, size, vas, vbs = [sock.sv_get() for sock in self.inputs] + max_len = max([len(item) for item in [number, step, size, vas, vbs]]) + number = chain(number, cycle([number[-1]])) + step = chain(step, cycle([step[-1]])) + size = chain(size, cycle([size[-1]])) + vas = chain(vas, cycle([vas[-1]])) + vbs = chain(vbs, cycle([vbs[-1]])) + out = [] + for i, n, st, si, va, vb in zip(range(max_len), number, step, size, vas, vbs): + if self.direction in ['X', 'Y', 'Z']: + out.append(make_line(n, st, si, self.direction, self.normalize, self.center)) + else: + out.append(make_line_advanced(n, st, si, va, vb, self.direction, self.normalize, self.center)) + + if self.split: + verts, edges = zip(*out) + verts_out, edges_out = [], [] + for vs, ns in zip(verts, edges): + v_out, e_out = split_lines_to_objects(vs, ns) + verts_out.extend(v_out) + edges_out.extend(e_out) else: - for p in zip(*params): - n, s, nor = p - self.define_steplist(step_list, s, n, nor, normal) - for s in step_list: - r1, r2 = make_line(s, c, d, [], []) - verts_out.append(r1) - edges_out.append(r2) - - if self.outputs['Vertices'].is_linked: - self.outputs['Vertices'].sv_set(verts_out) - if self.outputs['Edges'].is_linked: - self.outputs['Edges'].sv_set(edges_out) + verts_out, edges_out = zip(*out) + + self.outputs['Vertices'].sv_set(verts_out) + self.outputs['Edges'].sv_set(edges_out) def register(): -- GitLab From 91255d9453ad39ef9ec03a8762328f2d6cc858bb Mon Sep 17 00:00:00 2001 From: Durman Date: Sat, 21 Dec 2019 18:49:57 +0400 Subject: [PATCH 2/9] fix OD mode --- nodes/generator/line_mk3.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nodes/generator/line_mk3.py b/nodes/generator/line_mk3.py index 2df5cdba6..61893ffc9 100644 --- a/nodes/generator/line_mk3.py +++ b/nodes/generator/line_mk3.py @@ -101,7 +101,7 @@ def make_line_advanced(numbers, steps, sizes, verts_a, verts_b, mode='AB', norma dir_line = (vb - va) * 1 / len_line else: len_line = st * (n - 1 if n > 2 else 1) - dir_line = (vb - va) * 1 / np.linalg.norm(vb - va) + dir_line = vb * 1 / np.linalg.norm(vb) if normalized and center: verts_line = generate_verts(va + dir_line * (-size / 2), va + dir_line * (size / 2), n) @@ -114,7 +114,7 @@ def make_line_advanced(numbers, steps, sizes, verts_a, verts_b, mode='AB', norma verts_line = generate_verts(va, vb, n) else: len_line = st * (n - 1 if n > 2 else 1) - dir_line = (vb - va) * 1 / np.linalg.norm(vb - va) + dir_line = vb * 1 / np.linalg.norm(vb) verts_line = generate_verts(va, va + dir_line * len_line, n) edges_lines.extend((i + len(verts_lines), i + len(verts_lines) + 1) for i in range(1 if n <= 2 else n - 1)) -- GitLab From 0d3ee24457f108af268d65384ff858b4c153d19a Mon Sep 17 00:00:00 2001 From: durman Date: Mon, 23 Dec 2019 13:08:52 +0400 Subject: [PATCH 3/9] np.array output for X, Y, Z modes --- nodes/generator/line_mk3.py | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/nodes/generator/line_mk3.py b/nodes/generator/line_mk3.py index 61893ffc9..0bda89ec3 100644 --- a/nodes/generator/line_mk3.py +++ b/nodes/generator/line_mk3.py @@ -44,16 +44,23 @@ def make_line(numbers, steps, sizes, mode='X', normalized=False, center=False): :param mode: 'X' or 'Y' or 'Z' :param normalized: fit line into given size :param center: move center of line in center of coordinates - :return: list of vertices, list of edges + :return: np.array of vertices, np.array of edges """ - max_len = max(len(numbers), len(sizes), len(steps)) + number_of_lines = max(len(numbers), len(sizes), len(steps)) + number_of_edges = sum([v_number - 1 if v_number > 1 else 1 for _, v_number in + zip(range(number_of_lines), chain(numbers, cycle([numbers[-1]])))]) + number_of_vertices = sum([v_number if v_number > 1 else 2 for _, v_number in + zip(range(number_of_lines), chain(numbers, cycle([numbers[-1]])))]) numbers = chain(numbers, cycle([numbers[-1]])) steps = chain(steps, cycle([steps[-1]])) sizes = chain(sizes, cycle([sizes[-1]])) - verts_lines = [] - edges_lines = [] + verts_lines = np.empty((number_of_vertices, 3)) + edges_lines = np.empty((number_of_edges, 2)) + num_added_edges = 0 + num_added_verts = 0 - for i, n, st, size in zip(range(max_len), numbers, steps, sizes): + for i_line, n, st, size in zip(range(number_of_lines), numbers, steps, sizes): + n = 2 if n < 2 else n if normalized and center: co1, co2 = -size / 2, size / 2 elif normalized: @@ -64,8 +71,10 @@ def make_line(numbers, steps, sizes, mode='X', normalized=False, center=False): co1, co2 = 0, st * (n - 1) va = np.array((co1 if mode == "X" else 0, co1 if mode == "Y" else 0, co1 if mode == "Z" else 0)) vb = np.array((co2 if mode == "X" else 0, co2 if mode == "Y" else 0, co2 if mode == "Z" else 0)) - edges_lines.extend((i + len(verts_lines), i + len(verts_lines) + 1) for i in range(1 if n <= 2 else n - 1)) - verts_lines.extend(generate_verts(va, vb, n).tolist()) + edges_lines[num_added_edges: num_added_edges + n - 1] = np.stack([np.arange(n - 1), np.arange(1, n)], axis=1) + verts_lines[num_added_verts: num_added_verts + n] = (generate_verts(va, vb, n)) + num_added_edges += n - 1 + num_added_verts += n return verts_lines, edges_lines @@ -233,6 +242,7 @@ class SvLineNodeMK3(bpy.types.Node, SverchCustomTreeNode): update=updateNode) split: BoolProperty(name="Split to objects", description="Each object in separate object", update=updateNode) + as_numpy: BoolProperty(name="Numpy output", description="Format of output data", update=updateNode) def sv_init(self, context): self.inputs.new('SvStringsSocket', "Num").prop_name = 'num' @@ -257,6 +267,11 @@ class SvLineNodeMK3(bpy.types.Node, SverchCustomTreeNode): def draw_buttons_ext(self, context, layout): layout.prop(self, 'split') + layout.prop(self, 'as_numpy') + + def rclick_menu(self, context, layout): + layout.prop(self, 'split') + layout.prop(self, 'as_numpy') def process(self): if not any(s.is_linked for s in self.outputs): @@ -286,6 +301,10 @@ class SvLineNodeMK3(bpy.types.Node, SverchCustomTreeNode): else: verts_out, edges_out = zip(*out) + if not self.as_numpy: + verts_out = [ar.tolist() for ar in verts_out] + edges_out = [ar.tolist() for ar in edges_out] + self.outputs['Vertices'].sv_set(verts_out) self.outputs['Edges'].sv_set(edges_out) -- GitLab From 2d1577779d5a89ed564b5b5001c91700fad10c1c Mon Sep 17 00:00:00 2001 From: durman Date: Thu, 9 Jan 2020 13:14:35 +0400 Subject: [PATCH 4/9] add new segment generator node fix face of line generator --- index.md | 1 + nodes/generator/edge.py | 135 ++++++++++++++++++++++++++++++++++++ nodes/generator/line_mk3.py | 23 ++++-- 3 files changed, 152 insertions(+), 7 deletions(-) create mode 100644 nodes/generator/edge.py diff --git a/index.md b/index.md index d2b351625..85a5c309f 100644 --- a/index.md +++ b/index.md @@ -10,6 +10,7 @@ ## Generator SvLineNodeMK3 + SvSegmentGenerator SvPlaneNodeMK2 SvNGonNode SvBoxNode diff --git a/nodes/generator/edge.py b/nodes/generator/edge.py new file mode 100644 index 000000000..5ae0df920 --- /dev/null +++ b/nodes/generator/edge.py @@ -0,0 +1,135 @@ +# This file is part of project Sverchok. It's copyrighted by the contributors +# recorded in the version control history of the file, available from +# its original location https://github.com/nortikin/sverchok/commit/master +# +# SPDX-License-Identifier: GPL3 +# License-Filename: LICENSE + + +from itertools import chain, cycle +import numpy as np + +import bpy + +from sverchok.node_tree import SverchCustomTreeNode +from sverchok.data_structure import updateNode + + +def generate_edges(verts_a, verts_b, cuts): + """ + Generate lines between two given points + :param verts_a: list of tuple(float, float, float) + :param verts_b: list of tuple(float, float, float) + :param cuts: list of int + :return: numpy array with shape (number of vertices, 3), list of tuple(int, int) + """ + line_number = max(len(cuts), len(verts_a), len(verts_b)) + verts_number = sum([cuts + 2 if cuts >= 0 else 2 for _, cuts in + zip(range(line_number), chain(cuts, cycle([cuts[-1]])))]) + cuts = chain(cuts, cycle([cuts[-1]])) + verts_a = chain(verts_a, cycle([verts_a[-1]])) + verts_b = chain(verts_b, cycle([verts_b[-1]])) + verts_lines = np.empty((verts_number, 3)) + edges_lines = [] + num_added_verts = 0 + indexes = iter(range(int(1e+100))) + + for i, c, va, vb in zip(range(line_number), cuts, verts_a, verts_b): + va, vb = np.array(va), np.array(vb) + verts_line = generate_verts(va, vb, c) + edges_lines.extend([(i, i + 1) for i, _ in zip(indexes, verts_line[:-1])]) + verts_lines[num_added_verts: num_added_verts + len(verts_line)] = verts_line + num_added_verts += len(verts_line) + + return verts_lines, edges_lines + + +def generate_verts(va, vb, cuts): + # interpolate vertices between two given + if cuts <= 0: + return np.array((va, vb)) + x = np.linspace(va[0], vb[0], cuts + 2) + y = np.linspace(va[1], vb[1], cuts + 2) + z = np.linspace(va[2], vb[2], cuts + 2) + return np.stack((x, y, z), axis=-1) + + +def split_lines_to_objects(verts, edges): + """ + detect lines and split them into separate objects + vertices and edges should be ordered according generator lines logic + :param verts: numpy array with shape(n, 3) + :param edges: list of tuple(int, int) + :return: list of np arrays, list of list of tuple(int, int) + """ + split_slice = [0] + for i in range(len(edges)): + split_slice[-1] += 1 + # current edge - (0, 1), next edge - (1, 2) - still on the same line + # current edge - (1, 2), next edge - (3, 4) - the current line is finished + is_end = True if i + 1 >= len(edges) else True if edges[i][1] != edges[i + 1][0] else False + if is_end: + split_slice[-1] += 1 + if i != len(edges) - 1: + split_slice.append(split_slice[-1]) + edges_out = [[(i, i + 1) for i in range(len(v_num) - 1)] + for v_num in np.split(np.empty(len(verts)), split_slice)[:-1]] + return np.split(verts, split_slice)[:-1], edges_out + + +class SvSegmentGenerator(bpy.types.Node, SverchCustomTreeNode): + """ + Triggers: Create edges between two points + + Can subdivide output edge + Vertices can be as numpy array + """ + bl_idname = 'SvSegmentGenerator' + bl_label = 'Segment' + bl_icon = 'GRIP' + sv_icon = 'SV_LINE' + + a: bpy.props.FloatVectorProperty(name='A', update=updateNode) + b: bpy.props.FloatVectorProperty(name='B', default=(0.5, 0.5, 0.5), update=updateNode) + cuts_number: bpy.props.IntProperty(name='Number of cuts', min=0, update=updateNode) + as_numpy: bpy.props.BoolProperty(name="Numpy output", description="Format of output data", update=updateNode) + split: bpy.props.BoolProperty(name="Split to objects", description="Each object in separate object", + update=updateNode, default=True) + + def draw_buttons_ext(self, context, layout): + layout.prop(self, 'as_numpy') + layout.prop(self, 'split') + + def rclick_menu(self, context, layout): + layout.prop(self, 'split') + layout.prop(self, 'as_numpy') + + def sv_init(self, context): + self.inputs.new('SvVerticesSocket', 'A').prop_name = 'a' + self.inputs.new('SvVerticesSocket', 'B').prop_name = 'b' + self.inputs.new('SvStringsSocket', 'Cuts').prop_name = 'cuts_number' + self.outputs.new('SvVerticesSocket', 'Verts') + self.outputs.new('SvStringsSocket', 'Edges') + + def process(self): + num_objects = max([len(sock.sv_get(deepcopy=False)) for sock in self.inputs]) + out = [] + for i, a, b, c in zip( + range(num_objects), + *[chain(sock.sv_get(deepcopy=False), cycle([sock.sv_get(deepcopy=False)[-1]])) + for sock in self.inputs]): + out.append(generate_edges(a, b, c)) + if self.split: + temp = [split_lines_to_objects(*data) for data in out] + out = [v for res in temp for v in zip(*res)] + if not self.as_numpy: + out = [(ar.tolist(), edges) for ar, edges in out] + [sock.sv_set(data) for sock, data in zip(self.outputs, zip(*out))] + + +def register(): + bpy.utils.register_class(SvSegmentGenerator) + + +def unregister(): + bpy.utils.unregister_class(SvSegmentGenerator) diff --git a/nodes/generator/line_mk3.py b/nodes/generator/line_mk3.py index 0bda89ec3..605ac9a3c 100644 --- a/nodes/generator/line_mk3.py +++ b/nodes/generator/line_mk3.py @@ -16,6 +16,7 @@ # # ##### END GPL LICENSE BLOCK ##### +from collections import namedtuple from itertools import chain, cycle import numpy as np @@ -26,14 +27,19 @@ from sverchok.node_tree import SverchCustomTreeNode from sverchok.data_structure import updateNode +Directions = namedtuple('Directions', ['x', 'y', 'z', 'od']) +DIRECTION = Directions('X', 'Y', 'Z', 'OD') directionItems = [ - ("X", "X", "Along X axis", 0), - ("Y", "Y", "Along Y axis", 1), - ("Z", "Z", "Along Z axis", 2), - ("AB", "AB", "Between 2 points", 3), - ("OD", "OD", "Origin and Direction", 4), + (DIRECTION.x, DIRECTION.x, "Along X axis", 0), + (DIRECTION.y, DIRECTION.y, "Along Y axis", 1), + (DIRECTION.z, DIRECTION.z, "Along Z axis", 2), + (DIRECTION.od, DIRECTION.od, "Origin and Direction", 3), ] +Lengths = namedtuple('Lengths', ['size', 'number', 'step']) +LENGTH = Lengths('Size', 'Number', 'Step') +length_items = [(i, i, '') for i in LENGTH] + def make_line(numbers, steps, sizes, mode='X', normalized=False, center=False): """ @@ -243,6 +249,7 @@ class SvLineNodeMK3(bpy.types.Node, SverchCustomTreeNode): split: BoolProperty(name="Split to objects", description="Each object in separate object", update=updateNode) as_numpy: BoolProperty(name="Numpy output", description="Format of output data", update=updateNode) + length_mode: EnumProperty(items=length_items, update=updateNode) def sv_init(self, context): self.inputs.new('SvStringsSocket', "Num").prop_name = 'num' @@ -262,8 +269,9 @@ class SvLineNodeMK3(bpy.types.Node, SverchCustomTreeNode): row = col.row(align=True) row.prop(self, "direction", expand=True) row = col.row(align=True) - row.prop(self, "center", toggle=True) - row.prop(self, "normalize", toggle=True) + row.prop(self, "length_mode", expand=True) + row = col.row(align=True) + row.prop(self, "center", text="Center to origin") def draw_buttons_ext(self, context, layout): layout.prop(self, 'split') @@ -274,6 +282,7 @@ class SvLineNodeMK3(bpy.types.Node, SverchCustomTreeNode): layout.prop(self, 'as_numpy') def process(self): + return if not any(s.is_linked for s in self.outputs): return -- GitLab From 979178e65d7c817ce19f4b2fdef4cf194b2665c5 Mon Sep 17 00:00:00 2001 From: durman Date: Thu, 9 Jan 2020 13:19:56 +0400 Subject: [PATCH 5/9] move line mk3 to old nodes --- index.md | 2 +- nodes/generator/line_mk3.py | 8 +- old_nodes/line_mk3.py | 240 ++++++++++++++++++++++++++++++++++++ 3 files changed, 245 insertions(+), 5 deletions(-) create mode 100644 old_nodes/line_mk3.py diff --git a/index.md b/index.md index 85a5c309f..8899adad1 100644 --- a/index.md +++ b/index.md @@ -9,7 +9,7 @@ > Failing to follow these points will break the node category parser. ## Generator - SvLineNodeMK3 + SvLineNodeMK4 SvSegmentGenerator SvPlaneNodeMK2 SvNGonNode diff --git a/nodes/generator/line_mk3.py b/nodes/generator/line_mk3.py index 605ac9a3c..dbc4b4601 100644 --- a/nodes/generator/line_mk3.py +++ b/nodes/generator/line_mk3.py @@ -166,12 +166,12 @@ def split_lines_to_objects(verts, edges): return verts_out, edges_out -class SvLineNodeMK3(bpy.types.Node, SverchCustomTreeNode): +class SvLineNodeMK4(bpy.types.Node, SverchCustomTreeNode): """ Triggers: Line, segment. Tooltip: Generate line. """ - bl_idname = 'SvLineNodeMK3' + bl_idname = 'SvLineNodeMK4' bl_label = 'Line' bl_icon = 'GRIP' sv_icon = 'SV_LINE' @@ -319,8 +319,8 @@ class SvLineNodeMK3(bpy.types.Node, SverchCustomTreeNode): def register(): - bpy.utils.register_class(SvLineNodeMK3) + bpy.utils.register_class(SvLineNodeMK4) def unregister(): - bpy.utils.unregister_class(SvLineNodeMK3) + bpy.utils.unregister_class(SvLineNodeMK4) diff --git a/old_nodes/line_mk3.py b/old_nodes/line_mk3.py new file mode 100644 index 000000000..e00faf169 --- /dev/null +++ b/old_nodes/line_mk3.py @@ -0,0 +1,240 @@ +# ##### 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, EnumProperty, FloatVectorProperty +from sverchok.node_tree import SverchCustomTreeNode +from sverchok.data_structure import updateNode, fullList, match_long_repeat +from sverchok.utils.modules.geom_utils import interp_v3_v3v3, normalize, add_v3_v3v3, sub_v3_v3v3 + +directionItems = [ + ("X", "X", "Along X axis", 0), + ("Y", "Y", "Along Y axis", 1), + ("Z", "Z", "Along Z axis", 2), + ("AB", "AB", "Between 2 points", 3), + ("OD", "OD", "Origin and Direction", 4), +] + + +def make_line(steps, center, direction, vert_a, vert_b): + if direction == "X": + vec = lambda l: (l, 0.0, 0.0) + elif direction == "Y": + vec = lambda l: (0.0, l, 0.0) + elif direction == "Z": + vec = lambda l: (0.0, 0.0, l) + elif direction in ["AB", "OD"]: + vec = lambda l: interp_v3_v3v3(vert_a, vert_b, l) + + verts = [] + add_vert = verts.append + x = -sum(steps) / 2 if center else 0 + for s in [0.0] + steps: + x = x + s + add_vert(vec(x)) + edges = [[i, i + 1] for i in range(len(steps))] + return verts, edges + + +class SvLineNodeMK3(bpy.types.Node, SverchCustomTreeNode): + """ + Triggers: Line, segment. + Tooltip: Generate line. + """ + bl_idname = 'SvLineNodeMK3' + bl_label = 'Line' + bl_icon = 'GRIP' + sv_icon = 'SV_LINE' + + def update_size_socket(self, context): + """ need to do UX transformation before updating node""" + size_socket = self.inputs["Size"] + size_socket.hide_safe = not self.normalize + + updateNode(self, context) + + def update_vect_socket(self, context): + """ need to do UX transformation before updating node""" + si = self.inputs + sd = self.direction + if sd == "OD" and not si[3].name[0] == "O": + si[3].name = "Origin" + si[4].name = "Direction" + si[3].prop_name = 'v3_origin' + si[4].prop_name = 'v3_dir' + elif sd == "AB" and not si[3].name[0] == "A": + si[3].name = "A" + si[4].name = "B" + si[3].prop_name = 'v3_input_0' + si[4].prop_name = 'v3_input_1' + + ortho = sd not in ["AB", "OD"] + if (not ortho and si[3].hide_safe) or ortho: + si[3].hide_safe = ortho + si[4].hide_safe = ortho + + updateNode(self, context) + + direction: EnumProperty( + name="Direction", items=directionItems, + default="X", update=update_vect_socket) + + num: IntProperty( + name='Num Verts', description='Number of Vertices', + default=2, min=2, update=updateNode) + + step: FloatProperty( + name='Step', description='Step length', + default=1.0, update=updateNode) + + center: BoolProperty( + name='Center', description='Center the line', + default=False, update=updateNode) + + normalize: BoolProperty( + name='Normalize', description='Normalize line to size', + default=False, update=update_size_socket) + + size: FloatProperty( + name='Size', description='Size of line', + default=10.0, update=updateNode) + + v3_input_0: FloatVectorProperty( + name='A', description='Starting point', + size=3, default=(0, 0, 0), + update=updateNode) + + v3_input_1: FloatVectorProperty( + name='B', description='End point', + size=3, default=(0.5, 0.5, 0.5), + update=updateNode) + + v3_origin: FloatVectorProperty( + name='Origin', description='Origin of line', + size=3, default=(0, 0, 0), + update=updateNode) + + v3_dir: FloatVectorProperty( + name='Direction', description='Direction', + size=3, default=(1, 1, 1), + update=updateNode) + + def set_size_socket(self): + size_socket = self.inputs.new('SvStringsSocket', "Size") + size_socket.prop_name = 'size' + size_socket.hide_safe = not self.normalize + + def set_vector_sockets(self): + si = self.inputs + si.new('SvVerticesSocket', "A").prop_name = 'v3_input_0' + si.new('SvVerticesSocket', "B").prop_name = 'v3_input_1' + si[3].hide_safe = self.direction not in ["AB", " OD"] + si[4].hide_safe = self.direction not in ["AB", " OD"] + + def sv_init(self, context): + si = self.inputs + si.new('SvStringsSocket', "Num").prop_name = 'num' + si.new('SvStringsSocket', "Step").prop_name = 'step' + self.set_size_socket() + self.set_vector_sockets() + self.outputs.new('SvVerticesSocket', "Vertices") + self.outputs.new('SvStringsSocket', "Edges") + + def draw_buttons(self, context, layout): + col = layout.column(align=True) + row = col.row(align=True) + row.prop(self, "direction", expand=True) + row = col.row(align=True) + row.prop(self, "center", toggle=True) + row.prop(self, "normalize", toggle=True) + + def get_data(self): + c, d = self.center, self.direction + input_num = self.inputs["Num"].sv_get() + input_step = self.inputs["Step"].sv_get() + normal_size = [2.0 if c else 1.0] + + if self.normalize: + normal_size = self.inputs["Size"].sv_get()[0] + + params = [input_num, input_step, normal_size] + + if d in ["AB", "OD"]: + v_a = self.inputs[3].sv_get()[0] + v_b = self.inputs[4].sv_get()[0] + params.append(v_a) + params.append(v_b) + + return match_long_repeat(params) + + def define_steplist(self, step_list, s, n, nor, normal): + + for num in n: + num = max(2, num) + s = s[:(num - 1)] # shorten if needed + fullList(s, num - 1) # extend if needed + step_list.append([S * nor / sum(s) for S in s] if normal else s) + + def process_vectors(self, pts_list, d, va, vb): + if d == "AB" and self.normalize: + vb = add_v3_v3v3(normalize(sub_v3_v3v3(vb, va)), va) + elif d == "OD": + vb = add_v3_v3v3(normalize(vb), va) + pts_list.append((va, vb)) + + def process(self): + if not any(s.is_linked for s in self.outputs): + return + + c, d = self.center, self.direction + step_list = [] + pts_list = [] + verts_out, edges_out = [], [] + normal = self.normalize or d == "AB" + advanced = d in ["AB", "OD"] + params = self.get_data() + if advanced: + for p in zip(*params): + n, s, nor, va, vb = p + self.define_steplist(step_list, s, n, nor, normal) + self.process_vectors(pts_list, d, va, vb) + for s, vc in zip(step_list, pts_list): + r1, r2 = make_line(s, c, d, vc[0], vc[1]) + verts_out.append(r1) + edges_out.append(r2) + else: + for p in zip(*params): + n, s, nor = p + self.define_steplist(step_list, s, n, nor, normal) + for s in step_list: + r1, r2 = make_line(s, c, d, [], []) + verts_out.append(r1) + edges_out.append(r2) + + if self.outputs['Vertices'].is_linked: + self.outputs['Vertices'].sv_set(verts_out) + if self.outputs['Edges'].is_linked: + self.outputs['Edges'].sv_set(edges_out) + + +def register(): + bpy.utils.register_class(SvLineNodeMK3) + + +def unregister(): + bpy.utils.unregister_class(SvLineNodeMK3) \ No newline at end of file -- GitLab From e4f20a5b544fff9a28ecb637aff943f967290701 Mon Sep 17 00:00:00 2001 From: Durman Date: Thu, 9 Jan 2020 18:54:44 +0400 Subject: [PATCH 6/9] full reimplementation of the node --- nodes/generator/line_mk3.py | 340 ++++++++++++++++-------------------- 1 file changed, 153 insertions(+), 187 deletions(-) diff --git a/nodes/generator/line_mk3.py b/nodes/generator/line_mk3.py index dbc4b4601..c929e4bbb 100644 --- a/nodes/generator/line_mk3.py +++ b/nodes/generator/line_mk3.py @@ -41,99 +41,98 @@ LENGTH = Lengths('Size', 'Number', 'Step') length_items = [(i, i, '') for i in LENGTH] -def make_line(numbers, steps, sizes, mode='X', normalized=False, center=False): +def make_line(numbers=None, steps=None, sizes=None, verts_a=None, verts_b=None, + dir_mode=DIRECTION.x, size_mode=LENGTH.size, center=False): """ - Generate simple lines along X, Y or Z axis. All lines locates in one object. - :param numbers: Number of vertices, list of int - :param steps: Step length, list of float - :param sizes: Size of lines in normalized mode, list of floats - :param mode: 'X' or 'Y' or 'Z' - :param normalized: fit line into given size - :param center: move center of line in center of coordinates + Generate lines + :param numbers: list of values, number of generated vertices + :param steps: list of values, distance between points for step mode only + :param sizes: list of values, length of a line for size mode only + :param verts_a: list of tuple(float, float, float), custom origin of a line + :param verts_b: list of tuple(float, float, float), custom direction of a line + :param dir_mode: 'X', 'Y', 'Z' or 'OD', 'OD' mode for custom origin and direction + :param size_mode: 'Size' or 'Step', length of line + :param center: if True center of a line is moved to origin :return: np.array of vertices, np.array of edges """ - number_of_lines = max(len(numbers), len(sizes), len(steps)) - number_of_edges = sum([v_number - 1 if v_number > 1 else 1 for _, v_number in - zip(range(number_of_lines), chain(numbers, cycle([numbers[-1]])))]) - number_of_vertices = sum([v_number if v_number > 1 else 2 for _, v_number in - zip(range(number_of_lines), chain(numbers, cycle([numbers[-1]])))]) - numbers = chain(numbers, cycle([numbers[-1]])) - steps = chain(steps, cycle([steps[-1]])) - sizes = chain(sizes, cycle([sizes[-1]])) - verts_lines = np.empty((number_of_vertices, 3)) - edges_lines = np.empty((number_of_edges, 2)) - num_added_edges = 0 + line_number = max(len(numbers), len(sizes), len(steps), len(verts_a), len(verts_b)) + vert_number = sum([v_number if v_number > 1 else 2 for _, v_number in + zip(range(line_number), chain(numbers, cycle([numbers[-1]])))]) + numbers = cycle([None]) if numbers is None else chain(numbers, cycle([numbers[-1]])) + steps = cycle([None]) if steps is None else chain(steps, cycle([steps[-1]])) + sizes = cycle([None]) if sizes is None else chain(sizes, cycle([sizes[-1]])) + verts_a = cycle([None]) if verts_a is None else chain(verts_a, cycle([verts_a[-1]])) + verts_b = cycle([None]) if verts_b is None else chain(verts_b, cycle([verts_b[-1]])) + verts_lines = np.empty((vert_number, 3)) + edges_lines = [] num_added_verts = 0 - - for i_line, n, st, size in zip(range(number_of_lines), numbers, steps, sizes): - n = 2 if n < 2 else n - if normalized and center: - co1, co2 = -size / 2, size / 2 - elif normalized: - co1, co2 = 0, size - elif center: - co1, co2 = -st * (n - 1) / 2, st * (n - 1) / 2 - else: - co1, co2 = 0, st * (n - 1) - va = np.array((co1 if mode == "X" else 0, co1 if mode == "Y" else 0, co1 if mode == "Z" else 0)) - vb = np.array((co2 if mode == "X" else 0, co2 if mode == "Y" else 0, co2 if mode == "Z" else 0)) - edges_lines[num_added_edges: num_added_edges + n - 1] = np.stack([np.arange(n - 1), np.arange(1, n)], axis=1) - verts_lines[num_added_verts: num_added_verts + n] = (generate_verts(va, vb, n)) - num_added_edges += n - 1 - num_added_verts += n + indexes = iter(range(int(1e+100))) + + for i_line, n, st, size, va, vb in zip(range(line_number), numbers, steps, sizes, verts_a, verts_b): + va, vb = get_corner_points(dir_mode, center, va, vb, get_len_line(size_mode, n, size, st)) + line_verts = generate_verts(va, vb, n) + edges_lines.extend([(i, i + 1) for i, _ in zip(indexes, line_verts[:-1])]) + verts_lines[num_added_verts: num_added_verts + len(line_verts)] = line_verts + num_added_verts += len(line_verts) return verts_lines, edges_lines -def make_line_advanced(numbers, steps, sizes, verts_a, verts_b, mode='AB', normalized=False, center=False): +def get_len_line(len_mode, number, size, step): + # returns length of line according logic of a mode + if len_mode == LENGTH.size: + return size + elif len_mode == LENGTH.number: + return (number - 1 if number > 2 else 1) * step + + +def get_corner_points(dir_mode=DIRECTION.x, center=False, vert_a=None, vert_b=None, len_line=None): + # returns coordinates of firs and last points of live according properties of the node + directions = {'X': (1, 0, 0), 'Y': (0, 1, 0), 'Z': (0, 0, 1)} + origin = np.array(vert_a) if dir_mode == DIRECTION.od else np.array((0, 0, 0)) + direction = (np.array(vert_b) / np.linalg.norm(np.array(vert_b)) + if dir_mode == DIRECTION.od else np.array(directions[dir_mode])) + if center: + origin = origin - direction * (len_line / 2) + return origin, direction * len_line + origin + + +def make_line_multiple_steps(steps, verts_a=None, verts_b=None, dir_mode=DIRECTION.x, center=False): """ Generate lines between two given points in 'AB' mode or determined by origin(vert_a) and direction(vert_b) - :param numbers: Number of vertices, list of int - :param steps: Step length, list of float - :param sizes: Size of lines in normalized mode, list of floats - :param verts_a: or origin, list of vertices - :param verts_b: or direction, list of vertices - :param mode: 'AB' - generate line between points, 'OD' - generate line from origin with determined direction - :param normalized: fit line into given size - :param center: move center of line into vert_a - :return: list of vertices, list of edges (*one object) + :param steps: list of values, each step is nest segment of a same line + :param verts_a: list of tuple(float, float, float), origin of a line, only for 'OD' mode + :param verts_b: list of tuple(float, float, float), direction of a line, only for 'OD' mode + :param dir_mode: 'X', 'Y', 'Z' or 'OD' where 'OD' means custom origin and direction + :param center: if True center of a line is moved to origin + :return: numpy array with shape(number of vertices, 3), list of tuple(int, int) """ - max_len = max(len(numbers), len(steps), len(sizes), len(verts_a), len(verts_b)) - numbers = chain(numbers, cycle([numbers[-1]])) - steps = chain(steps, cycle([steps[-1]])) - sizes = chain(sizes, cycle([sizes[-1]])) - verts_a = chain(verts_a, cycle([verts_a[-1]])) - verts_b = chain(verts_b, cycle([verts_b[-1]])) - verts_lines = [] + line_number = max(len(verts_a or 1), len(verts_b or 1)) + vert_number = line_number * (len(steps) + 1) + len_line = sum(steps) + accum_steps = np.add.accumulate(steps) + verts_a = cycle([None]) if verts_a is None else chain(verts_a, cycle([verts_a[-1]])) + verts_b = cycle([None]) if verts_b is None else chain(verts_b, cycle([verts_b[-1]])) + accum_steps = cycle([accum_steps]) + verts_lines = np.empty((vert_number, 3)) edges_lines = [] - - for i, n, st, size, va, vb in zip(range(max_len), numbers, steps, sizes, verts_a, verts_b): - va, vb = np.array(va), np.array(vb) - if normalized or center: - # if center is true then va becomes center - - if mode == 'AB': - len_line = np.linalg.norm(vb - va) - dir_line = (vb - va) * 1 / len_line - else: - len_line = st * (n - 1 if n > 2 else 1) - dir_line = vb * 1 / np.linalg.norm(vb) - - if normalized and center: - verts_line = generate_verts(va + dir_line * (-size / 2), va + dir_line * (size / 2), n) - elif normalized: - verts_line = generate_verts(va, va + dir_line * size, n) - elif center: - verts_line = generate_verts(va + dir_line * (-len_line / 2), va + dir_line * (len_line / 2), n) - else: - if mode == 'AB': - verts_line = generate_verts(va, vb, n) - else: - len_line = st * (n - 1 if n > 2 else 1) - dir_line = vb * 1 / np.linalg.norm(vb) - verts_line = generate_verts(va, va + dir_line * len_line, n) - - edges_lines.extend((i + len(verts_lines), i + len(verts_lines) + 1) for i in range(1 if n <= 2 else n - 1)) - verts_lines.extend(verts_line.tolist()) + num_added_verts = 0 + indexes = iter(range(int(1e+100))) + + for line_i, sts, va, vb in zip(range(line_number), accum_steps, verts_a, verts_b): + directions = {'X': (1, 0, 0), 'Y': (0, 1, 0), 'Z': (0, 0, 1)} + origin = np.array(va) if dir_mode == DIRECTION.od else np.array((0, 0, 0)) + direction = (np.array(vb) / np.linalg.norm(np.array(vb)) + if dir_mode == DIRECTION.od else np.array(directions[dir_mode])) + if center: + origin = origin - direction * (len_line / 2) + line_verts = np.full((len(steps), 3), direction) + line_verts = line_verts * sts.reshape((len(steps), 1)) + line_verts = line_verts + origin + + edges_lines.extend([(i, i + 1) for i, _ in zip(indexes, line_verts)]) + verts_lines[num_added_verts] = origin + verts_lines[num_added_verts + 1: num_added_verts + len(line_verts) + 1] = line_verts + num_added_verts += len(line_verts) + 1 return verts_lines, edges_lines @@ -148,22 +147,26 @@ def generate_verts(va, vb, number): def split_lines_to_objects(verts, edges): - # detect lines and split them into separate objects - # vertices and edges should be ordered according generator lines logic - verts = iter(verts) - verts_out = [[]] - + """ + detect lines and split them into separate objects + vertices and edges should be ordered according generator lines logic + :param verts: numpy array with shape(n, 3) + :param edges: list of tuple(int, int) + :return: list of np arrays, list of list of tuple(int, int) + """ + split_slice = [0] for i in range(len(edges)): - verts_out[-1].append(next(verts)) + split_slice[-1] += 1 # current edge - (0, 1), next edge - (1, 2) - still on the same line # current edge - (1, 2), next edge - (3, 4) - the current line is finished is_end = True if i + 1 >= len(edges) else True if edges[i][1] != edges[i + 1][0] else False if is_end: - verts_out[-1].append(next(verts)) + split_slice[-1] += 1 if i != len(edges) - 1: - verts_out.append([]) - edges_out = [[(i, i + 1) for i in range(len(vs) - 1)] for vs in verts_out] - return verts_out, edges_out + split_slice.append(split_slice[-1]) + edges_out = [[(i, i + 1) for i in range(len(v_num) - 1)] + for v_num in np.split(np.empty(len(verts)), split_slice)[:-1]] + return np.split(verts, split_slice)[:-1], edges_out class SvLineNodeMK4(bpy.types.Node, SverchCustomTreeNode): @@ -176,91 +179,60 @@ class SvLineNodeMK4(bpy.types.Node, SverchCustomTreeNode): bl_icon = 'GRIP' sv_icon = 'SV_LINE' - def update_size_socket(self, context): + def update_sockets(self, context): """ need to do UX transformation before updating node""" - self.inputs["Size"].hide_safe = not self.normalize - updateNode(self, context) + def set_hide(sock, status): + if sock.hide_safe != status: + sock.hide_safe = status - def update_vect_socket(self, context): - """ need to do UX transformation before updating node""" - si = self.inputs - sd = self.direction - if sd == "OD" and not si[3].name[0] == "O": - si[3].name = "Origin" - si[4].name = "Direction" - si[3].prop_name = 'v3_origin' - si[4].prop_name = 'v3_dir' - elif sd == "AB" and not si[3].name[0] == "A": - si[3].name = "A" - si[4].name = "B" - si[3].prop_name = 'v3_input_0' - si[4].prop_name = 'v3_input_1' - - ortho = sd not in ["AB", "OD"] - if (not ortho and si[3].hide_safe) or ortho: - si[3].hide_safe = ortho - si[4].hide_safe = ortho + if self.direction == DIRECTION.od: + self.inputs['A'].hide_safe = False + self.inputs['B'].hide_safe = False + else: + self.inputs['A'].hide_safe = True + self.inputs['B'].hide_safe = True + + if self.length_mode == LENGTH.size: + set_hide(self.inputs['Num'], False) + set_hide(self.inputs['Steps'], True) + set_hide(self.inputs['Size'], False) + self.inputs['Steps'].prop_name = 'step' + elif self.length_mode == LENGTH.number: + set_hide(self.inputs['Num'], False) + set_hide(self.inputs['Steps'], False) + set_hide(self.inputs['Size'], True) + self.inputs['Steps'].prop_name = 'step' + elif self.length_mode == LENGTH.step: + set_hide(self.inputs['Num'], True) + set_hide(self.inputs['Steps'], False) + set_hide(self.inputs['Size'], True) + self.inputs['Steps'].prop_name = '' updateNode(self, context) - direction : EnumProperty( - name="Direction", items=directionItems, - default="X", update=update_vect_socket) - - num : IntProperty( - name='Num Verts', description='Number of Vertices', - default=2, min=2, update=updateNode) - - step : FloatProperty( - name='Step', description='Step length', - default=1.0, update=updateNode) - - center : BoolProperty( - name='Center', description='Center the line', - default=False, update=updateNode) - - normalize : BoolProperty( - name='Normalize', description='Normalize line to size', - default=False, update=update_size_socket) - - size : FloatProperty( - name='Size', description='Size of line', - default=10.0, update=updateNode) - - v3_input_0 : FloatVectorProperty( - name='A', description='Starting point', - size=3, default=(0, 0, 0), - update=updateNode) - - v3_input_1 : FloatVectorProperty( - name='B', description='End point', - size=3, default=(0.5, 0.5, 0.5), - update=updateNode) - - v3_origin : FloatVectorProperty( - name='Origin', description='Origin of line', - size=3, default=(0, 0, 0), - update=updateNode) - - v3_dir : FloatVectorProperty( - name='Direction', description='Direction', - size=3, default=(1, 1, 1), - update=updateNode) - - split: BoolProperty(name="Split to objects", description="Each object in separate object", update=updateNode) + direction: EnumProperty(name="Direction", items=directionItems, default="X", update=update_sockets) + num: IntProperty(name='Num Verts', description='Number of Vertices', default=2, min=2, update=updateNode) + step: FloatProperty(name='Step', description='Step length', default=1.0, update=updateNode) + center: BoolProperty(name='Center', description='Center the line', default=False, update=updateNode) + size: FloatProperty(name='Size', description='Size of line', default=10.0, update=updateNode) + split: BoolProperty(name="Split to objects", description="Each object in separate object", default=True, + update=updateNode) as_numpy: BoolProperty(name="Numpy output", description="Format of output data", update=updateNode) - length_mode: EnumProperty(items=length_items, update=updateNode) + length_mode: EnumProperty(items=length_items, update=update_sockets) + v3_dir: FloatVectorProperty(name='Direction', description='Direction', size=3, default=(1, 1, 1), update=updateNode) + v3_origin: FloatVectorProperty(name='Origin', description='Origin of line', size=3, default=(0, 0, 0), + update=updateNode) def sv_init(self, context): self.inputs.new('SvStringsSocket', "Num").prop_name = 'num' - self.inputs.new('SvStringsSocket', "Step").prop_name = 'step' + self.inputs.new('SvStringsSocket', "Steps").prop_name = 'step' self.inputs.new('SvStringsSocket', "Size").prop_name = 'size' - self.inputs.new('SvVerticesSocket', "A").prop_name = 'v3_input_0' - self.inputs.new('SvVerticesSocket', "B").prop_name = 'v3_input_1' + self.inputs.new('SvVerticesSocket', "A").prop_name = 'v3_origin' + self.inputs.new('SvVerticesSocket', "B").prop_name = 'v3_dir' self.outputs.new('SvVerticesSocket', "Vertices") self.outputs.new('SvStringsSocket', "Edges") - self.inputs['Size'].hide_safe = True + self.inputs['Steps'].hide_safe = True self.inputs["A"].hide_safe = True self.inputs["B"].hide_safe = True @@ -274,6 +246,12 @@ class SvLineNodeMK4(bpy.types.Node, SverchCustomTreeNode): row.prop(self, "center", text="Center to origin") def draw_buttons_ext(self, context, layout): + col = layout.column(align=True) + row = col.row(align=True) + row.prop(self, "direction", expand=True) + row = col.row(align=True) + row.prop(self, "length_mode", expand=True) + layout.prop(self, "center", text="Center to origin") layout.prop(self, 'split') layout.prop(self, 'as_numpy') @@ -282,40 +260,28 @@ class SvLineNodeMK4(bpy.types.Node, SverchCustomTreeNode): layout.prop(self, 'as_numpy') def process(self): - return - if not any(s.is_linked for s in self.outputs): + if self.length_mode == LENGTH.step and not self.inputs['Steps'].is_linked: return - number, step, size, vas, vbs = [sock.sv_get() for sock in self.inputs] - max_len = max([len(item) for item in [number, step, size, vas, vbs]]) + number, step, size, vas, vbs = [sock.sv_get(deepcopy=False) for sock in self.inputs] + num_objects = max([len(item) for item in [number, step, size, vas, vbs]]) number = chain(number, cycle([number[-1]])) step = chain(step, cycle([step[-1]])) size = chain(size, cycle([size[-1]])) vas = chain(vas, cycle([vas[-1]])) vbs = chain(vbs, cycle([vbs[-1]])) out = [] - for i, n, st, si, va, vb in zip(range(max_len), number, step, size, vas, vbs): - if self.direction in ['X', 'Y', 'Z']: - out.append(make_line(n, st, si, self.direction, self.normalize, self.center)) + for i, n, st, si, va, vb in zip(range(num_objects), number, step, size, vas, vbs): + if self.length_mode == LENGTH.step: + out.append(make_line_multiple_steps(st, va, vb, self.direction, self.center)) else: - out.append(make_line_advanced(n, st, si, va, vb, self.direction, self.normalize, self.center)) - + out.append(make_line(n, st, si, va, vb, self.direction, self.length_mode, self.center)) if self.split: - verts, edges = zip(*out) - verts_out, edges_out = [], [] - for vs, ns in zip(verts, edges): - v_out, e_out = split_lines_to_objects(vs, ns) - verts_out.extend(v_out) - edges_out.extend(e_out) - else: - verts_out, edges_out = zip(*out) - + temp = [split_lines_to_objects(*data) for data in out] + out = [v for res in temp for v in zip(*res)] if not self.as_numpy: - verts_out = [ar.tolist() for ar in verts_out] - edges_out = [ar.tolist() for ar in edges_out] - - self.outputs['Vertices'].sv_set(verts_out) - self.outputs['Edges'].sv_set(edges_out) + out = [(ar.tolist(), edges) for ar, edges in out] + [sock.sv_set(data) for sock, data in zip(self.outputs, zip(*out))] def register(): -- GitLab From 066203ba031b98b0a74daabfaa219eb6d5278a83 Mon Sep 17 00:00:00 2001 From: Durman Date: Thu, 9 Jan 2020 18:56:30 +0400 Subject: [PATCH 7/9] rename file to mk4 --- nodes/generator/{line_mk3.py => line_mk4.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename nodes/generator/{line_mk3.py => line_mk4.py} (100%) diff --git a/nodes/generator/line_mk3.py b/nodes/generator/line_mk4.py similarity index 100% rename from nodes/generator/line_mk3.py rename to nodes/generator/line_mk4.py -- GitLab From 2062b6d4c1d7af1e2285d1d51051efd921bf73eb Mon Sep 17 00:00:00 2001 From: durman Date: Fri, 10 Jan 2020 13:01:17 +0400 Subject: [PATCH 8/9] bunch of little improvements --- nodes/generator/line_mk4.py | 48 ++++++++++++------------- nodes/generator/{edge.py => segment.py} | 2 +- old_nodes/line_mk3.py | 2 ++ 3 files changed, 27 insertions(+), 25 deletions(-) rename nodes/generator/{edge.py => segment.py} (99%) diff --git a/nodes/generator/line_mk4.py b/nodes/generator/line_mk4.py index c929e4bbb..d5d74e2f2 100644 --- a/nodes/generator/line_mk4.py +++ b/nodes/generator/line_mk4.py @@ -41,36 +41,36 @@ LENGTH = Lengths('Size', 'Number', 'Step') length_items = [(i, i, '') for i in LENGTH] -def make_line(numbers=None, steps=None, sizes=None, verts_a=None, verts_b=None, +def make_line(numbers=None, steps=None, sizes=None, verts_or=None, verts_dir=None, dir_mode=DIRECTION.x, size_mode=LENGTH.size, center=False): """ Generate lines :param numbers: list of values, number of generated vertices :param steps: list of values, distance between points for step mode only :param sizes: list of values, length of a line for size mode only - :param verts_a: list of tuple(float, float, float), custom origin of a line - :param verts_b: list of tuple(float, float, float), custom direction of a line + :param verts_or: list of tuple(float, float, float), custom origin of a line + :param verts_dir: list of tuple(float, float, float), custom direction of a line :param dir_mode: 'X', 'Y', 'Z' or 'OD', 'OD' mode for custom origin and direction :param size_mode: 'Size' or 'Step', length of line :param center: if True center of a line is moved to origin :return: np.array of vertices, np.array of edges """ - line_number = max(len(numbers), len(sizes), len(steps), len(verts_a), len(verts_b)) + line_number = max(len(numbers), len(sizes), len(steps), len(verts_or), len(verts_dir)) vert_number = sum([v_number if v_number > 1 else 2 for _, v_number in zip(range(line_number), chain(numbers, cycle([numbers[-1]])))]) numbers = cycle([None]) if numbers is None else chain(numbers, cycle([numbers[-1]])) steps = cycle([None]) if steps is None else chain(steps, cycle([steps[-1]])) sizes = cycle([None]) if sizes is None else chain(sizes, cycle([sizes[-1]])) - verts_a = cycle([None]) if verts_a is None else chain(verts_a, cycle([verts_a[-1]])) - verts_b = cycle([None]) if verts_b is None else chain(verts_b, cycle([verts_b[-1]])) + verts_or = cycle([None]) if verts_or is None else chain(verts_or, cycle([verts_or[-1]])) + verts_dir = cycle([None]) if verts_dir is None else chain(verts_dir, cycle([verts_dir[-1]])) verts_lines = np.empty((vert_number, 3)) edges_lines = [] num_added_verts = 0 indexes = iter(range(int(1e+100))) - for i_line, n, st, size, va, vb in zip(range(line_number), numbers, steps, sizes, verts_a, verts_b): - va, vb = get_corner_points(dir_mode, center, va, vb, get_len_line(size_mode, n, size, st)) - line_verts = generate_verts(va, vb, n) + for i_line, n, st, size, vor, vdir in zip(range(line_number), numbers, steps, sizes, verts_or, verts_dir): + vor, vdir = get_corner_points(dir_mode, center, vor, vdir, get_len_line(size_mode, n, size, st)) + line_verts = generate_verts(vor, vdir, n) edges_lines.extend([(i, i + 1) for i, _ in zip(indexes, line_verts[:-1])]) verts_lines[num_added_verts: num_added_verts + len(line_verts)] = line_verts num_added_verts += len(line_verts) @@ -186,11 +186,11 @@ class SvLineNodeMK4(bpy.types.Node, SverchCustomTreeNode): sock.hide_safe = status if self.direction == DIRECTION.od: - self.inputs['A'].hide_safe = False - self.inputs['B'].hide_safe = False + self.inputs['Origin'].hide_safe = False + self.inputs['Direction'].hide_safe = False else: - self.inputs['A'].hide_safe = True - self.inputs['B'].hide_safe = True + self.inputs['Origin'].hide_safe = True + self.inputs['Direction'].hide_safe = True if self.length_mode == LENGTH.size: set_hide(self.inputs['Num'], False) @@ -227,14 +227,14 @@ class SvLineNodeMK4(bpy.types.Node, SverchCustomTreeNode): self.inputs.new('SvStringsSocket', "Num").prop_name = 'num' self.inputs.new('SvStringsSocket', "Steps").prop_name = 'step' self.inputs.new('SvStringsSocket', "Size").prop_name = 'size' - self.inputs.new('SvVerticesSocket', "A").prop_name = 'v3_origin' - self.inputs.new('SvVerticesSocket', "B").prop_name = 'v3_dir' + self.inputs.new('SvVerticesSocket', "Origin").prop_name = 'v3_origin' + self.inputs.new('SvVerticesSocket', "Direction").prop_name = 'v3_dir' self.outputs.new('SvVerticesSocket', "Vertices") self.outputs.new('SvStringsSocket', "Edges") self.inputs['Steps'].hide_safe = True - self.inputs["A"].hide_safe = True - self.inputs["B"].hide_safe = True + self.inputs["Origin"].hide_safe = True + self.inputs["Direction"].hide_safe = True def draw_buttons(self, context, layout): col = layout.column(align=True) @@ -263,19 +263,19 @@ class SvLineNodeMK4(bpy.types.Node, SverchCustomTreeNode): if self.length_mode == LENGTH.step and not self.inputs['Steps'].is_linked: return - number, step, size, vas, vbs = [sock.sv_get(deepcopy=False) for sock in self.inputs] - num_objects = max([len(item) for item in [number, step, size, vas, vbs]]) + number, step, size, ors, dirs = [sock.sv_get(deepcopy=False) for sock in self.inputs] + num_objects = max([len(item) for item in [number, step, size, ors, dirs]]) number = chain(number, cycle([number[-1]])) step = chain(step, cycle([step[-1]])) size = chain(size, cycle([size[-1]])) - vas = chain(vas, cycle([vas[-1]])) - vbs = chain(vbs, cycle([vbs[-1]])) + ors = chain(ors, cycle([ors[-1]])) + dirs = chain(dirs, cycle([dirs[-1]])) out = [] - for i, n, st, si, va, vb in zip(range(num_objects), number, step, size, vas, vbs): + for i, n, st, si, va, d in zip(range(num_objects), number, step, size, ors, dirs): if self.length_mode == LENGTH.step: - out.append(make_line_multiple_steps(st, va, vb, self.direction, self.center)) + out.append(make_line_multiple_steps(st, va, d, self.direction, self.center)) else: - out.append(make_line(n, st, si, va, vb, self.direction, self.length_mode, self.center)) + out.append(make_line(n, st, si, va, d, self.direction, self.length_mode, self.center)) if self.split: temp = [split_lines_to_objects(*data) for data in out] out = [v for res in temp for v in zip(*res)] diff --git a/nodes/generator/edge.py b/nodes/generator/segment.py similarity index 99% rename from nodes/generator/edge.py rename to nodes/generator/segment.py index 5ae0df920..dadf4c5b2 100644 --- a/nodes/generator/edge.py +++ b/nodes/generator/segment.py @@ -79,7 +79,7 @@ def split_lines_to_objects(verts, edges): class SvSegmentGenerator(bpy.types.Node, SverchCustomTreeNode): """ - Triggers: Create edges between two points + Triggers: 2pt Line Can subdivide output edge Vertices can be as numpy array diff --git a/old_nodes/line_mk3.py b/old_nodes/line_mk3.py index e00faf169..c003c3d53 100644 --- a/old_nodes/line_mk3.py +++ b/old_nodes/line_mk3.py @@ -90,6 +90,8 @@ class SvLineNodeMK3(bpy.types.Node, SverchCustomTreeNode): updateNode(self, context) + replacement_nodes = [('SvLineNodeMK4', None, None)] + direction: EnumProperty( name="Direction", items=directionItems, default="X", update=update_vect_socket) -- GitLab From babd99df395c38543b0867880f31bcbaab6d561f Mon Sep 17 00:00:00 2001 From: Durman Date: Sun, 12 Jan 2020 10:15:31 +0400 Subject: [PATCH 9/9] fix `replacement_nodes` attribute --- nodes/generator/line_mk4.py | 2 +- old_nodes/line_mk3.py | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/nodes/generator/line_mk4.py b/nodes/generator/line_mk4.py index d5d74e2f2..ab40c05c6 100644 --- a/nodes/generator/line_mk4.py +++ b/nodes/generator/line_mk4.py @@ -229,7 +229,7 @@ class SvLineNodeMK4(bpy.types.Node, SverchCustomTreeNode): self.inputs.new('SvStringsSocket', "Size").prop_name = 'size' self.inputs.new('SvVerticesSocket', "Origin").prop_name = 'v3_origin' self.inputs.new('SvVerticesSocket', "Direction").prop_name = 'v3_dir' - self.outputs.new('SvVerticesSocket', "Vertices") + self.outputs.new('SvVerticesSocket', "Verts") self.outputs.new('SvStringsSocket', "Edges") self.inputs['Steps'].hide_safe = True diff --git a/old_nodes/line_mk3.py b/old_nodes/line_mk3.py index c003c3d53..7dd527510 100644 --- a/old_nodes/line_mk3.py +++ b/old_nodes/line_mk3.py @@ -90,7 +90,12 @@ class SvLineNodeMK3(bpy.types.Node, SverchCustomTreeNode): updateNode(self, context) - replacement_nodes = [('SvLineNodeMK4', None, None)] + @property + def replacement_nodes(self): + if self.direction == 'AB': + return [('SvSegmentGenerator', {'Num': 'Cuts'}, {'Vertices': 'Verts'})] + else: + return [('SvLineNodeMK4', None, {'Vertices': 'Verts'})] direction: EnumProperty( name="Direction", items=directionItems, -- GitLab