From b72083b37b5a55b1d1f13cab0c77bd2f481f262b Mon Sep 17 00:00:00 2001 From: Dealga McArdle Date: Sat, 3 Nov 2018 14:03:23 +0100 Subject: [PATCH 01/23] adresses bug in upstream list modification --- nodes/viz/viewer_polyline_mk1.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nodes/viz/viewer_polyline_mk1.py b/nodes/viz/viewer_polyline_mk1.py index ee6f4f164..a1d940c1b 100644 --- a/nodes/viz/viewer_polyline_mk1.py +++ b/nodes/viz/viewer_polyline_mk1.py @@ -316,8 +316,8 @@ class SvPolylineViewerNodeMK1(bpy.types.Node, SverchCustomTreeNode): return dataCorrect(data) mverts = get('vertices') - mradii = self.inputs['radii'].sv_get(deepcopy=False) - mtwist = self.inputs['twist'].sv_get(deepcopy=False) + mradii = self.inputs['radii'].sv_get(deepcopy=True) + mtwist = self.inputs['twist'].sv_get(deepcopy=True) mmtrix = get('matrix') # extend all non empty lists to longest of these -- GitLab From c92ebe097d23c2087dd8e6885d6f8f21d6869980 Mon Sep 17 00:00:00 2001 From: zeffii Date: Thu, 8 Nov 2018 15:27:25 +0100 Subject: [PATCH 02/23] add some error prevention --- nodes/generators_extended/smooth_lines.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/nodes/generators_extended/smooth_lines.py b/nodes/generators_extended/smooth_lines.py index 835ad1af2..10a9cc548 100644 --- a/nodes/generators_extended/smooth_lines.py +++ b/nodes/generators_extended/smooth_lines.py @@ -39,7 +39,13 @@ def find_projected_arc_center(p1, p2, b, radius=0.5): focal = (a + c) / 2.0 focal_length = (b-focal).length - angleA = (a-b).angle(c-b) / 2.0 + + try: + angleA = (a-b).angle(c-b) / 2.0 + except ValueError as e: + print('smoothlines encountered non zero length vectors') + # Vector.angle(other): zero length vectors have no valid angle + return None sideA = radius sideB = sideA / tan(angleA) @@ -93,6 +99,8 @@ def spline_points(points, weights, index, params): p2 = Vector(c).lerp(Vector(b), weight_to_use_2)[:] elif params.mode == 'arc': pts = find_projected_arc_center(c, a, b, radius=w2) + if not pts: + return [b] return three_point_arc(pts=pts, num_verts=divs, make_edges=False)[0] return [v[:] for v in bezlerp(p1, b, b, p2, divs)] -- GitLab From 33af0e1f6c425959dd41b96752e61f50949e0118 Mon Sep 17 00:00:00 2001 From: Alessandro Zomparelli Date: Thu, 8 Nov 2018 17:28:06 +0100 Subject: [PATCH 03/23] "Gcode Exporter" and "Evaluate Image" (#2298) * added gcode node * description corrected * fixed continuous extrusion * added credits * added feed movements * new formula * close all shapes * list matches and path preview * text format * removed one output socket * Added Evaluate Image Node and fixed limit in Image Node * removed comments * CLIP and EXTEND tile mode in Evaluate Image Node * removed unnecessary code --- index.md | 3 +- nodes/analyzer/evaluate_image.py | 200 +++++++++++++++++++++ nodes/generator/image.py | 18 +- nodes/text/export_gcode.py | 297 +++++++++++++++++++++++++++++++ utils/sv_itertools.py | 89 ++++++++- 5 files changed, 589 insertions(+), 18 deletions(-) create mode 100644 nodes/analyzer/evaluate_image.py create mode 100644 nodes/text/export_gcode.py diff --git a/index.md b/index.md index 30a384e3e..ba22b0c3d 100644 --- a/index.md +++ b/index.md @@ -64,6 +64,7 @@ SvProportionalEditNode SvRaycasterLiteNode SvOBJInsolationNode + EvaluateImageNode ## Transforms SvRotationNode @@ -310,6 +311,7 @@ SvSetCustomMeshNormals --- SvSpiralNode + SvExportGcodeNode ## Alpha Nodes SvCurveViewerNode @@ -340,4 +342,3 @@ SvOffsetLineNode SvContourNode SvPlanarEdgenetToPolygons - diff --git a/nodes/analyzer/evaluate_image.py b/nodes/analyzer/evaluate_image.py new file mode 100644 index 000000000..66d455d50 --- /dev/null +++ b/nodes/analyzer/evaluate_image.py @@ -0,0 +1,200 @@ +# ##### 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, StringProperty, EnumProperty + +from sverchok.node_tree import SverchCustomTreeNode +from sverchok.data_structure import updateNode, fullList +from math import floor + + +class EvaluateImageNode(bpy.types.Node, SverchCustomTreeNode): + ''' Evaluate Image ''' + bl_idname = 'EvaluateImageNode' + bl_label = 'Evaluate Image' + bl_icon = 'FILE_IMAGE' + + image_name = StringProperty(name='image_name', description='image name', default='', update=updateNode) + + boundary_modes = [ + ("CLIP", "Clip", "", 0), + ("EXTEND", "Extend", "", 1), + ("CYCLIC", "Repeat", "", 2), + ("MIRROR", "Mirror", "", 3)] + + shift_modes = [ + ("NONE", "None", "", 0), + ("ALTERNATE", "Alternate", "", 1), + ("CONSTANT", "Constant", "", 2)] + + shift_mode_U = EnumProperty( + name="U Shift", + description="U Shift", + default="NONE", items=shift_modes, + update=updateNode) + + shift_mode_V = EnumProperty( + name="V Shift", + description="V Shift", + default="NONE", items=shift_modes, + update=updateNode) + + boundU = EnumProperty( + name="U Bounds", + description="U Boundaries", + default="CYCLIC", items=boundary_modes, + update=updateNode) + + boundV = EnumProperty( + name="V Bounds", + description="V Boundaries", + default="CYCLIC", items=boundary_modes, + update=updateNode) + + domU = FloatProperty( + name='U domain', description='U domain', default=1, min=0.00001, + options={'ANIMATABLE'}, update=updateNode) + + domV = FloatProperty( + name='V domain', description='V domain', default=1, min=0.00001, + options={'ANIMATABLE'}, update=updateNode) + + shiftU = FloatProperty( + name='U shift', description='U shift', default=0.5, soft_min=-1, soft_max=1, + options={'ANIMATABLE'}, update=updateNode) + + shiftV = FloatProperty( + name='V shift', description='V shift', default=0, soft_min=-1, soft_max=1, + options={'ANIMATABLE'}, update=updateNode) + + def sv_init(self, context): + self.inputs.new('VerticesSocket', "Verts UV") + self.inputs.new('StringsSocket', "U domain").prop_name = 'domU' + self.inputs.new('StringsSocket', "V domain").prop_name = 'domV' + self.outputs.new('StringsSocket', "R") + self.outputs.new('StringsSocket', "G") + self.outputs.new('StringsSocket', "B") + + def draw_buttons(self, context, layout): + layout.label(text="Image:") + layout.prop_search(self, "image_name", bpy.data, 'images', text="") + + row = layout.row(align=True) + col = row.column(align=True) + col.label(text="Tile U:") + col.prop(self, "boundU", text="") + col.label(text="Shift U:") + col.prop(self, "shift_mode_U", text="") + if self.shift_mode_U != 'NONE': + col.prop(self, "shiftU", text="") + + col = row.column(align=True) + col.label(text="Tile V:") + col.prop(self, "boundV", text="") + col.label(text="Shift V:") + col.prop(self, "shift_mode_V", text="") + if self.shift_mode_V != 'NONE': + col.prop(self, "shiftV", text="") + + + def process(self): + verts = self.inputs['Verts UV'].sv_get() + + inputs, outputs = self.inputs, self.outputs + + # inputs + if inputs['Verts UV'].is_linked: + verts = inputs['Verts UV'].sv_get()[0] + else: + verts = [(0,0,0),(0,1,0),(1,0,0),(1,1,0)] + + if inputs['U domain'].is_linked: + domU = inputs['U domain'].sv_get()[0][0] + else: domU = self.domU + + if inputs['V domain'].is_linked: + domV = inputs['V domain'].sv_get()[0][0] + else: domV = self.domV + + # outputs + red = [[]] + green = [[]] + blue = [[]] + + if outputs['R'].is_linked or outputs['G'].is_linked or outputs['B'].is_linked: + imag = bpy.data.images[self.image_name].pixels[:] + sizeU = bpy.data.images[self.image_name].size[0] + sizeV = bpy.data.images[self.image_name].size[1] + for vert in verts: + vx = vert[0]*(sizeU-1)/domU + vy = vert[1]*sizeV/domV + u = floor(vx) + v = floor(vy) + u0 = u + inside_domain = True + + if self.shift_mode_U == 'ALTERNATE': + if (v//sizeV)%2: u += floor(sizeU*self.shiftU) + if self.shift_mode_U == 'CONSTANT': + u += floor(sizeU*self.shiftU*(v//sizeV)) + if self.boundU == 'CLIP': + inside_domain = 0 <= u < sizeU + elif self.boundU == 'CYCLIC': + u = u%sizeU + elif self.boundU == 'MIRROR': + if (u//sizeU)%2: u = sizeU - 1 - u%(sizeU) + else: u = u%(sizeU) + elif self.boundU == 'EXTEND': + u = max(0,min(u,sizeU-1)) + + + if self.shift_mode_V == 'ALTERNATE': + if (u0//sizeU)%2: v += floor(sizeV*self.shiftV) + if self.shift_mode_V == 'CONSTANT': + v += floor(sizeV*self.shiftV*(u0//sizeU)) + if self.boundV == 'CLIP': + inside_domain = inside_domain and 0 <= v < sizeV + elif self.boundV == 'CYCLIC': + v = v%sizeV + elif self.boundV == 'MIRROR': + if (v//sizeV)%2: v = sizeV - 1 - v%(sizeV) + else: v = v%(sizeV) + elif self.boundV == 'EXTEND': + v = max(0,min(v,sizeV-1)) + + if inside_domain: + index = int(u*4 + v*4*sizeU) + red[0].append(imag[index]) + green[0].append(imag[index+1]) + blue[0].append(imag[index+2]) + else: + red[0].append(0) + green[0].append(0) + blue[0].append(0) + outputs['R'].sv_set(red) + outputs['G'].sv_set(green) + outputs['B'].sv_set(blue) + + +def register(): + bpy.utils.register_class(EvaluateImageNode) + + +def unregister(): + bpy.utils.unregister_class(EvaluateImageNode) diff --git a/nodes/generator/image.py b/nodes/generator/image.py index fd1399f91..ffb789cae 100644 --- a/nodes/generator/image.py +++ b/nodes/generator/image.py @@ -45,19 +45,19 @@ class ImageNode(bpy.types.Node, SverchCustomTreeNode): options={'ANIMATABLE'}, update=updateNode) Xvecs = IntProperty( - name='Xvecs', description='Xvecs', default=10, min=2, max=100, + name='Xvecs', description='Xvecs', default=10, min=2, soft_max=100, options={'ANIMATABLE'}, update=updateNode) Yvecs = IntProperty( - name='Yvecs', description='Yvecs', default=10, min=2, max=100, + name='Yvecs', description='Yvecs', default=10, min=2, soft_max=100, options={'ANIMATABLE'}, update=updateNode) Xstep = FloatProperty( - name='Xstep', description='Xstep', default=1.0, min=0.01, max=100, + name='Xstep', description='Xstep', default=1.0, min=0.01, soft_max=100, options={'ANIMATABLE'}, update=updateNode) Ystep = FloatProperty( - name='Ystep', description='Ystep', default=1.0, min=0.01, max=100, + name='Ystep', description='Ystep', default=1.0, min=0.01, soft_max=100, options={'ANIMATABLE'}, update=updateNode) def sv_init(self, context): @@ -82,12 +82,12 @@ class ImageNode(bpy.types.Node, SverchCustomTreeNode): # inputs if inputs['vecs X'].is_linked: - IntegerX = min(int(inputs['vecs X'].sv_get()[0][0]), 100) + IntegerX = min(int(inputs['vecs X'].sv_get()[0][0]), 1000000) else: IntegerX = int(self.Xvecs) if inputs['vecs Y'].is_linked: - IntegerY = min(int(inputs['vecs Y'].sv_get()[0][0]), 100) + IntegerY = min(int(inputs['vecs Y'].sv_get()[0][0]), 1000000) else: IntegerY = int(self.Yvecs) @@ -109,7 +109,7 @@ class ImageNode(bpy.types.Node, SverchCustomTreeNode): if outputs['edgs'].is_linked: listEdg = [] - + for i in range(IntegerY): for j in range(IntegerX-1): listEdg.append((IntegerX*i+j, IntegerX*i+j+1)) @@ -127,7 +127,7 @@ class ImageNode(bpy.types.Node, SverchCustomTreeNode): listPlg.append((IntegerX*j+i, IntegerX*j+i+1, IntegerX*j+i+IntegerX+1, IntegerX*j+i+IntegerX)) plg = [list(listPlg)] outputs['pols'].sv_set(plg) - + def make_vertices(self, delitelx, delitely, stepx, stepy, image_name): lenx = bpy.data.images[image_name].size[0] @@ -150,7 +150,7 @@ class ImageNode(bpy.types.Node, SverchCustomTreeNode): # каждый пиксель кодируется RGBA, и записан строкой, без разделения на строки и столбцы. middle = (imag[addition]*R+imag[addition+1]*G+imag[addition+2]*B)*imag[addition+3] vertex = [x*stepx[x], y*stepy[y], middle] - vertices.append(vertex) + vertices.append(vertex) addition += int(xcoef*4) return vertices diff --git a/nodes/text/export_gcode.py b/nodes/text/export_gcode.py new file mode 100644 index 000000000..eaaacb586 --- /dev/null +++ b/nodes/text/export_gcode.py @@ -0,0 +1,297 @@ +# ##### 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 ##### + +# made by: Alessandro Zomparelli +# url: www.alessandrozomparelli.com + +import io +import itertools +import pprint +import sverchok + +import bpy, os, mathutils +from numpy import mean +import operator +from math import pi + +from bpy.props import BoolProperty, EnumProperty, StringProperty, FloatProperty, IntProperty + +from sverchok.node_tree import SverchCustomTreeNode, StringsSocket +from sverchok.data_structure import node_id, multi_socket, updateNode +from sverchok.utils.sv_itertools import sv_zip_longest2, flatten, list_of_lists, recurse_verts_fxy, match_longest_lists + +from sverchok.utils.sv_text_io_common import ( + FAIL_COLOR, READY_COLOR, TEXT_IO_CALLBACK, + get_socket_type, + new_output_socket, + name_dict, + text_modes +) + +def convert_to_text(list): + while True: + if type(list) is str: break + elif type(list) in (tuple, list): + try: + list = '\n'.join(list) + break + except: list = list[0] + else: break + return list + +class SvExportGcodeNode(bpy.types.Node, SverchCustomTreeNode): + """ + Triggers: Export gcode from vertices position + Tooltip: Generate a gcode file from a list of vertices + """ + bl_idname = 'SvExportGcodeNode' + bl_label = 'Export Gcode' + bl_icon = 'COPYDOWN' + + last_e = FloatProperty(name="Pull", default=5.0, min=0, soft_max=10) + path_length = FloatProperty(name="Pull", default=5.0, min=0, soft_max=10) + + folder = StringProperty(name="File", default="", subtype='FILE_PATH') + pull = FloatProperty(name="Pull", default=5.0, min=0, soft_max=10) + push = FloatProperty(name="Push", default=5.0, min=0, soft_max=10) + dz = FloatProperty(name="dz", default=2.0, min=0, soft_max=20) + flow_mult = FloatProperty(name="Flow Mult", default=1.0, min=0, soft_max=3) + feed = IntProperty(name="Feed Rate (F)", default=1000, min=0, soft_max=20000) + feed_horizontal = IntProperty(name="Feed Horizontal", default=2000, min=0, soft_max=20000) + feed_vertical = IntProperty(name="Feed Vertical", default=500, min=0, soft_max=20000) + feed = IntProperty(name="Feed Rate (F)", default=1000, min=0, soft_max=20000) + esteps = FloatProperty(name="E Steps/Unit", default=5, min=0, soft_max=100) + start_code = StringProperty(name="Start", default='') + end_code = StringProperty(name="End", default='') + auto_sort = BoolProperty(name="Auto Sort", default=True) + close_all = BoolProperty(name="Close Shapes", default=False) + nozzle = FloatProperty(name="Nozzle", default=0.4, min=0, soft_max=10) + layer_height = FloatProperty(name="Layer Height", default=0.1, min=0, soft_max=10) + filament = FloatProperty(name="Filament (\u03A6)", default=1.75, min=0, soft_max=120) + + gcode_mode = EnumProperty(items=[ + ("CONT", "Continuous", ""), + ("RETR", "Retraction", "") + ], default='CONT', name="Mode") + + def sv_init(self, context): + self.inputs.new('StringsSocket', 'Layer Height',).prop_name = 'layer_height' + self.inputs.new('StringsSocket', 'Flow Mult',).prop_name = 'flow_mult' + self.inputs.new('VerticesSocket', 'Vertices',) + + self.outputs.new('StringsSocket', 'Info',) + self.outputs.new('VerticesSocket', 'Vertices',) + self.outputs.new('StringsSocket', 'Printed Edges',) + self.outputs.new('StringsSocket', 'Travel Edges',) + + def draw_buttons(self, context, layout): + + addon = context.user_preferences.addons.get(sverchok.__name__) + over_sized_buttons = addon.preferences.over_sized_buttons + + col = layout.column(align=True) + row = col.row() + row.prop(self, 'folder', toggle=True, text='') + col = layout.column(align=True) + row = col.row() + row.prop(self, 'gcode_mode', expand=True, toggle=True) + #col = layout.column(align=True) + col = layout.column(align=True) + col.label(text="Extrusion:", icon='MOD_FLUIDSIM') + #col.prop(self, 'esteps') + col.prop(self, 'filament') + col.prop(self, 'nozzle') + col.separator() + col.label(text="Speed (Feed Rate F):", icon='DRIVER') + col.prop(self, 'feed', text='Print') + if self.gcode_mode == 'RETR': + col.prop(self, 'feed_vertical', text='Z Lift') + col.prop(self, 'feed_horizontal', text='Travel') + col.separator() + if self.gcode_mode == 'RETR': + col.label(text="Retraction:", icon='NOCURVE') + col.prop(self, 'pull', text='Retraction') + col.prop(self, 'dz', text='Z Hop') + col.prop(self, 'push', text='Preload') + col.prop(self, 'auto_sort', text="Sort Layers (z)") + col.prop(self, 'close_all') + col.separator() + col.label(text='Custom Code:', icon='SCRIPT') + col.prop_search(self, 'start_code', bpy.data, 'texts') + col.prop_search(self, 'end_code', bpy.data, 'texts') + col.separator() + row = col.row(align=True) + row.scale_y = 4.0 + row.operator(TEXT_IO_CALLBACK, text='Export Gcode').fn_name = 'process' + + def update_socket(self, context): + self.update() + + def process(self): + # manage data + feed = self.feed + feed_v = self.feed_vertical + feed_h = self.feed_horizontal + layer = self.layer_height + layer = self.inputs['Layer Height'].sv_get() + vertices = self.inputs['Vertices'].sv_get() + flow_mult = self.inputs['Flow Mult'].sv_get() + + # data matching + vertices = list_of_lists(vertices) + flow_mult = list_of_lists(flow_mult) + layer = list_of_lists(layer) + vertices, flow_mult, layer = match_longest_lists([vertices, flow_mult, layer]) + print(vertices) + print(layer) + + # open file + if self.folder == '': + folder = '//' + os.path.splitext(bpy.path.basename(bpy.context.blend_data.filepath))[0] + else: + folder = self.folder + if '.gcode' not in folder: folder += '.gcode' + path = bpy.path.abspath(folder) + file = open(path, 'w') + try: + for line in bpy.data.texts[self.start_code].lines: + file.write(line.body + '\n') + except: + pass + + # sort vertices + if self.auto_sort and self.gcode_mode == 'RETR': + sorted_verts = [] + for curve in vertices: + # mean z + listz = [v[2] for v in curve] + meanz = mean(listz) + # store curve and meanz + sorted_verts.append((curve, meanz)) + vertices = [data[0] for data in sorted(sorted_verts, key=lambda height: height[1])] + + # initialize variables + e = 0 + last_vert = mathutils.Vector((0,0,0)) + maxz = 0 + path_length = 0 + + printed_verts = [] + printed_edges = [] + travel_verts = [] + travel_edges = [] + + # write movements + for i in range(len(vertices)): + curve = vertices[i] + first_id = len(printed_verts) + for j in range(len(curve)): + v = curve[j] + v_flow_mult = flow_mult[i][j] + v_layer = layer[i][j] + + # record max z + maxz = max(maxz,v[2]) + + # first point of the gcode + if i == j == 0: + printed_verts.append(v) + file.write('G92 E0 \n') + params = v[:3] + (feed,) + to_write = 'G1 X{0:.4f} Y{1:.4f} Z{2:.4f} F{3:.0f}\n'.format(*params) + file.write(to_write) + else: + # start after retraction + if j == 0 and self.gcode_mode == 'RETR': + params = v[:2] + (maxz+self.dz,) + (feed_h,) + to_write = 'G1 X{0:.4f} Y{1:.4f} Z{2:.4f} F{3:.0f}\n'.format(*params) + file.write(to_write) + e += self.push + params = v[:3] + (feed_v,) + to_write = 'G1 X{0:.4f} Y{1:.4f} Z{2:.4f} F{3:.0f}\n'.format(*params) + file.write(to_write) + file.write( 'G1 E' + format(e, '.4f') + '\n') + printed_verts.append((v[0], v[1], maxz+self.dz)) + travel_edges.append((len(printed_verts)-1, len(printed_verts)-2)) + printed_verts.append(v) + travel_edges.append((len(printed_verts)-1, len(printed_verts)-2)) + # regular extrusion + else: + printed_verts.append(v) + v1 = mathutils.Vector(v) + v0 = mathutils.Vector(curve[j-1]) + dist = (v1-v0).length + print(dist) + area = v_layer * self.nozzle + pi*(v_layer/2)**2 # rectangle + circle + cylinder = pi*(self.filament/2)**2 + flow = area / cylinder + e += dist * v_flow_mult * flow + params = v[:3] + (e,) + to_write = 'G1 X{0:.4f} Y{1:.4f} Z{2:.4f} E{3:.4f}\n'.format(*params) + file.write(to_write) + path_length += dist + printed_edges.append([len(printed_verts)-1, len(printed_verts)-2]) + if self.gcode_mode == 'RETR': + v0 = mathutils.Vector(curve[-1]) + if self.close_all: + #printed_verts.append(v0) + printed_edges.append([len(printed_verts)-1, first_id]) + + v1 = mathutils.Vector(curve[0]) + dist = (v0-v1).length + area = v_layer * self.nozzle + pi*(v_layer/2)**2 # rectangle + circle + cylinder = pi*(self.filament/2)**2 + flow = area / cylinder + e += dist * v_flow_mult * flow + params = v1[:3] + (e,) + to_write = 'G1 X{0:.4f} Y{1:.4f} Z{2:.4f} E{3:.4f}\n'.format(*params) + file.write(to_write) + path_length += dist + v0 = v1 + if i < len(vertices)-1: + e -= self.pull + file.write('G0 E' + format(e, '.4f') + '\n') + params = v0[:2] + (maxz+self.dz,) + (feed_v,) + to_write = 'G1 X{0:.4f} Y{1:.4f} Z{2:.4f} F{3:.0f}\n'.format(*params) + file.write(to_write) + printed_verts.append(v0.to_tuple()) + printed_verts.append((v0.x, v0.y, maxz+self.dz)) + travel_edges.append((len(printed_verts)-1, len(printed_verts)-2)) + + # end code + try: + for line in bpy.data.texts[self.end_code].lines: + file.write(line.body + '\n') + except: + pass + file.close() + print("Saved gcode to " + path) + info = "Extruded Filament: " + format(e, '.2f') + '\n' + info += "Extruded Volume: " + format(e*pi*(self.filament/2)**2, '.2f') + '\n' + info += "Printed Path: " + format(path_length, '.2f') + self.outputs[0].sv_set(info) + self.outputs[1].sv_set(printed_verts) + self.outputs[2].sv_set(printed_edges) + self.outputs[3].sv_set(travel_edges) + +def register(): + bpy.utils.register_class(SvExportGcodeNode) + + +def unregister(): + bpy.utils.unregister_class(SvExportGcodeNode) diff --git a/utils/sv_itertools.py b/utils/sv_itertools.py index b47669bb9..781c6a39c 100644 --- a/utils/sv_itertools.py +++ b/utils/sv_itertools.py @@ -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,8 +42,7 @@ class sv_zip_longest: raise StopIteration def __iter__(self): - return self - + return self def sv_zip_longest2(*args): # by zeffi @@ -51,8 +50,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 +60,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 = [] @@ -77,6 +76,80 @@ def recurse_fxy(l1, l2, f): return [recurse_fxy(l1, y, f) for y in l2] +def recurse_verts_fxy(l1, l2, f): + l1_type = isinstance(l1, (list)) + l2_type = isinstance(l2, (list)) + if not (l1_type or l2_type): + return f(l1, l2) + elif l1_type and l2_type: + fl = l2[-1] if len(l1) > len(l2) else l1[-1] + res = [] + res_append = res.append + for x, y in zip_longest(l1, l2, fillvalue=fl): + res_append(recurse_verts_fxy(x, y, f)) + return res + elif l1_type and not l2_type: + return [recurse_verts_fxy(x, l2, f) for x in l1] + else: #not l1_type and l2_type + return [recurse_verts_fxy(l1, y, f) for y in l2] + +# append all the elements to one single list +def append_all(l, flat): + if isinstance(l,(list)): + return [append_all(i, flat) for i in l] + else: + flat.append(l) + return l + +# flatten sublists +def flatten(l): + flat = [] + append_all(l, flat) + return flat + +# append all the lists to one single list +def append_lists(l, lists): + if isinstance(l,(list)): + flat_list = True + for i in l: + if isinstance(i,(list)): + flat_list = False + break + if flat_list: + lists.append(l) + return None + else: + return [append_lists(i, lists) for i in l] + else: + lists.append([l]) + return None + +# generate a single list with 1 level lists inside +def list_of_lists(l): + out_list = [] + append_lists(l, out_list) + return out_list + +# works with irregular sublists +def match_longest_lists(lists): + add_level = max([isinstance(l, list) for l in lists]) + if add_level: + for i in range(len(lists)): + l = lists[i] + if not isinstance(l, list): + lists[i] = [l] + length = [len(l) for l in lists] + max_length = max(length) + # extend shorter lists + for l in lists: + for i in range(len(l), max_length): + l.append(l[-1]) + try: + return zip(*[match_longest_lists([l[i] for l in lists]) for i in range(max_length)]) + except: + return lists + + def extend_if_needed(vl, wl, default=0.5): # match wl to correspond with vl try: -- GitLab From 7f8f0966ab2343ecfec1ad21331a0d390dc3f58b Mon Sep 17 00:00:00 2001 From: DolphinDream Date: Tue, 13 Nov 2018 09:57:21 -0600 Subject: [PATCH 04/23] Add default font scale to index viewer node settable via preferences For some screen resolutions (e.g. mac book pro with retina display / double pixel resolution) the index viewer node's font size was too small to read and needed a scale factor of 2x. With this new setting the user can define the desired font scale in the SV preference settings. The default font scale is 1.0. --- settings.py | 9 +++++++-- ui/index_viewer_draw.py | 12 ++++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/settings.py b/settings.py index a1a0460fa..7c8f21895 100644 --- a/settings.py +++ b/settings.py @@ -30,7 +30,7 @@ class SverchokPreferences(AddonPreferences): color_def.apply_theme() tab_modes = [(k, k, '', i) for i, k in enumerate(["General", "Node Defaults", "Theme"])] - + selected_tab = bpy.props.EnumProperty( items=tab_modes, description="pick viewing mode", @@ -168,6 +168,9 @@ class SverchokPreferences(AddonPreferences): stethoscope_view_xy_multiplier = FloatProperty( default=1.0, min=0.01, step=0.01, description='default stethoscope scale') + index_viewer_scale = FloatProperty( + default=1.0, min=0.01, step=0.01, description='default index viewer scale') + datafiles = os.path.join(bpy.utils.user_resource('DATAFILES', path='sverchok', create=True)) defaults_location = StringProperty(default=datafiles, description='usually ..data_files\\sverchok\\defaults\\nodes.json') external_editor = StringProperty(description='which external app to invoke to view sources') @@ -178,7 +181,7 @@ class SverchokPreferences(AddonPreferences): def update_log_level(self, context): logging.info("Setting log level to %s", self.log_level) logging.setLevel(self.log_level) - + log_levels = [ ("DEBUG", "Debug", "Debug output", 0), ("INFO", "Information", "Informational output", 1), @@ -270,6 +273,8 @@ class SverchokPreferences(AddonPreferences): box_sub1_col.label('stethoscope mk2 settings') box_sub1_col.prop(self, 'stethoscope_view_scale', text='scale') box_sub1_col.prop(self, 'stethoscope_view_xy_multiplier', text='xy multiplier') + box_sub1_col.label('index viewer settings') + box_sub1_col.prop(self, 'index_viewer_scale', text='scale') col3 = row_sub1.split().column() col3.label('Location of custom defaults') diff --git a/ui/index_viewer_draw.py b/ui/index_viewer_draw.py index 44f9a3655..49b81b763 100644 --- a/ui/index_viewer_draw.py +++ b/ui/index_viewer_draw.py @@ -24,6 +24,7 @@ import blf import bgl from mathutils import Vector +from sverchok.utils.context_managers import sv_preferences from sverchok.data_structure import Vector_generate, Matrix_generate SpaceView3D = bpy.types.SpaceView3D @@ -154,8 +155,15 @@ def draw_callback_px(n_id, draw_verts, draw_edges, draw_faces, draw_matrix, draw display_edge_index = settings['display_edge_index'] display_face_index = settings['display_face_index'] + try: + with sv_preferences() as prefs: + scale = prefs.index_viewer_scale + except: + # print('did not find preferences - you need to save user preferences') + scale = 1.0 + font_id = 0 - text_height = 13 + text_height = int(13.0 * scale) blf.size(font_id, text_height, 72) # should check prefs.dpi region_mid_width = region.width / 2.0 @@ -222,7 +230,7 @@ def draw_callback_px(n_id, draw_verts, draw_edges, draw_faces, draw_matrix, draw if data_edges and display_edge_index: for edge_index, (idx1, idx2) in enumerate(data_edges[obj_index]): - + v1 = Vector(final_verts[idx1]) v2 = Vector(final_verts[idx2]) loc = v1 + ((v2 - v1) / 2) -- GitLab From c5551c9c82f4effc40d97bab6d047b4fd8d95ff1 Mon Sep 17 00:00:00 2001 From: DolphinDream Date: Tue, 13 Nov 2018 18:02:19 -0600 Subject: [PATCH 05/23] Fix render scale and location of easing and texture viewer nodes - made scale and xy multiplier pref settings generic to be used by multiple nodes - add scale/multiplier adjustments to the texture viewer and easing node and update stethoscope node to use the same scale/multiplier --- nodes/number/easing.py | 56 +++++++++++++++++++++-------------- nodes/text/stethoscope_mk2.py | 5 ++-- nodes/viz/texture_viewer.py | 13 ++++++++ settings.py | 16 ++++++---- 4 files changed, 60 insertions(+), 30 deletions(-) diff --git a/nodes/number/easing.py b/nodes/number/easing.py index d1c292f92..5c2392da3 100644 --- a/nodes/number/easing.py +++ b/nodes/number/easing.py @@ -23,6 +23,7 @@ from bpy.props import FloatProperty, EnumProperty, StringProperty, BoolProperty import blf import bgl +from sverchok.utils.context_managers import sv_preferences from sverchok.data_structure import updateNode, node_id from sverchok.node_tree import SverchCustomTreeNode from sverchok.ui import nodeview_bgl_viewer_draw_mk2 as nvBGL2 @@ -38,12 +39,12 @@ for k in sorted(easing_dict.keys()): palette_dict = { "default": ( - (0.243299, 0.590403, 0.836084, 1.00), # back_color + (0.243299, 0.590403, 0.836084, 1.00), # back_color (0.390805, 0.754022, 1.000000, 1.00), # grid_color (1.000000, 0.330010, 0.107140, 1.00) # line_color ), "scope": ( - (0.274677, 0.366253, 0.386430, 1.00), # back_color + (0.274677, 0.366253, 0.386430, 1.00), # back_color (0.423268, 0.558340, 0.584078, 1.00), # grid_color (0.304762, 1.000000, 0.062827, 1.00) # line_color ) @@ -54,40 +55,42 @@ palette_dict = { def simple_grid_xy(x, y, args): func = args[0] back_color, grid_color, line_color = args[1] + scale = args[2] def draw_rect(x=0, y=0, w=30, h=10, color=(0.0, 0.0, 0.0, 1.0)): - bgl.glColor4f(*color) + bgl.glColor4f(*color) bgl.glBegin(bgl.GL_POLYGON) - for coord in [(x, y), (x+w, y), (w+x, y-h), (x, y-h)]: + for coord in [(x, y), (x + w, y), (w + x, y - h), (x, y - h)]: bgl.glVertex2f(*coord) bgl.glEnd() + size = 140 * scale # draw bg fill - draw_rect(x=x, y=y, w=140, h=140, color=back_color) + draw_rect(x=x, y=y, w=size, h=size, color=back_color) # draw grid bgl.glColor4f(*grid_color) num_divs = 8 - offset = 140/num_divs + offset = size / num_divs line_parts_x = [] line_parts_y = [] - for i in range(num_divs+1): - xpos1 = x + (i*offset) + for i in range(num_divs + 1): + xpos1 = x + (i * offset) ypos1 = y - ypos2 = y - 140 + ypos2 = y - size line_parts_x.extend([[xpos1, ypos1], [xpos1, ypos2]]) - ypos = y - (i*offset) - line_parts_y.extend([[x, ypos], [x+140, ypos]]) + ypos = y - (i * offset) + line_parts_y.extend([[x, ypos], [x + size, ypos]]) bgl.glLineWidth(0.8) bgl.glBegin(bgl.GL_LINES) for coord in line_parts_x + line_parts_y: bgl.glVertex2f(*coord) - bgl.glEnd() + bgl.glEnd() # draw graph-line bgl.glColor4f(*line_color) @@ -95,13 +98,11 @@ def simple_grid_xy(x, y, args): bgl.glBegin(bgl.GL_LINE_STRIP) num_points = 100 seg_diff = 1 / num_points - for i in range(num_points+1): - _px = x + ((i * seg_diff) * 140) - _py = y - (1 - func(i * seg_diff) * 140) - 140 + for i in range(num_points + 1): + _px = x + ((i * seg_diff) * size) + _py = y - (1 - func(i * seg_diff) * size) - size bgl.glVertex2f(_px, _py) - bgl.glEnd() - - + bgl.glEnd() class SvEasingNode(bpy.types.Node, SverchCustomTreeNode): @@ -169,18 +170,29 @@ class SvEasingNode(bpy.types.Node, SverchCustomTreeNode): palette = palette_dict.get(self.selected_theme_mode)[:] x, y = [int(j) for j in (self.location + Vector((self.width + 20, 0)))[:]] - + + # adjust render location based on preference multiplier setting + try: + with sv_preferences() as prefs: + multiplier = prefs.render_location_xy_multiplier + scale = prefs.render_scale + except: + # print('did not find preferences - you need to save user preferences') + multiplier = 1.0 + scale = 1.0 + x, y = [x * multiplier, y * multiplier] + draw_data = { 'tree_name': self.id_data.name[:], - 'mode': 'custom_function', + 'mode': 'custom_function', 'custom_function': simple_grid_xy, 'loc': (x, y), - 'args': (easing_func, palette) + 'args': (easing_func, palette, scale) } nvBGL2.callback_enable(n_id, draw_data) def free(self): - nvBGL2.callback_disable(node_id(self)) + nvBGL2.callback_disable(node_id(self)) # reset n_id on copy def copy(self, node): diff --git a/nodes/text/stethoscope_mk2.py b/nodes/text/stethoscope_mk2.py index 6fa5d89a6..31744a203 100644 --- a/nodes/text/stethoscope_mk2.py +++ b/nodes/text/stethoscope_mk2.py @@ -132,7 +132,7 @@ class SvStethoscopeNodeMK2(bpy.types.Node, SverchCustomTreeNode): try: with sv_preferences() as prefs: scale = prefs.stethoscope_view_scale - location_theta = prefs.stethoscope_view_xy_multiplier + location_theta = prefs.render_location_xy_multiplier except: # print('did not find preferences - you need to save user preferences') scale = 1.0 @@ -142,7 +142,6 @@ class SvStethoscopeNodeMK2(bpy.types.Node, SverchCustomTreeNode): data = inputs[0].sv_get(deepcopy=False) self.num_elements = len(data) - if self.selected_mode == 'text-based': props = lambda: None props.line_width = self.line_width @@ -190,7 +189,7 @@ class SvStethoscopeNodeMK2(bpy.types.Node, SverchCustomTreeNode): return try: if not self.inputs[0].other: - nvBGL.callback_disable(node_id(self)) + nvBGL.callback_disable(node_id(self)) except: print('stethoscope update holdout (not a problem)') diff --git a/nodes/viz/texture_viewer.py b/nodes/viz/texture_viewer.py index e267325e1..e0854e852 100644 --- a/nodes/viz/texture_viewer.py +++ b/nodes/viz/texture_viewer.py @@ -26,6 +26,7 @@ from bpy.props import ( FloatProperty, EnumProperty, StringProperty, BoolProperty, IntProperty ) +from sverchok.utils.context_managers import sv_preferences from sverchok.data_structure import updateNode, node_id from sverchok.node_tree import SverchCustomTreeNode from sverchok.ui import nodeview_bgl_viewer_draw_mk2 as nvBGL2 @@ -384,6 +385,18 @@ class SvTextureViewerNode(bpy.types.Node, SverchCustomTreeNode): self.texture[n_id] = name[0] init_texture(width, height, name[0], texture, gl_color_constant) + # adjust render location based on preference multiplier setting + try: + with sv_preferences() as prefs: + multiplier = prefs.render_location_xy_multiplier + scale = prefs.render_scale + except: + # print('did not find preferences - you need to save user preferences') + multiplier = 1.0 + scale = 1.0 + x, y = [x * multiplier, y * multiplier] + width, height =[width * scale, height * scale] + draw_data = { 'tree_name': self.id_data.name[:], 'mode': 'custom_function', diff --git a/settings.py b/settings.py index 7c8f21895..100c9a3fc 100644 --- a/settings.py +++ b/settings.py @@ -163,10 +163,14 @@ class SverchokPreferences(AddonPreferences): enable_live_objin = BoolProperty( description="Objects in edit mode will be updated in object-in Node") + render_scale = FloatProperty( + default=1.0, min=0.01, step=0.01, description='default render scale') + + render_location_xy_multiplier = FloatProperty( + default=1.0, min=0.01, step=0.01, description='default render location xy multiplier') + stethoscope_view_scale = FloatProperty( default=1.0, min=0.01, step=0.01, description='default stethoscope scale') - stethoscope_view_xy_multiplier = FloatProperty( - default=1.0, min=0.01, step=0.01, description='default stethoscope scale') index_viewer_scale = FloatProperty( default=1.0, min=0.01, step=0.01, description='default index viewer scale') @@ -270,10 +274,12 @@ class SverchokPreferences(AddonPreferences): row_sub1 = col.row().split(0.5) box_sub1 = row_sub1.box() box_sub1_col = box_sub1.column(align=True) - box_sub1_col.label('stethoscope mk2 settings') + box_sub1_col.label('Render Scale & Location') + box_sub1_col.prop(self, 'render_location_xy_multiplier', text='xy multiplier') + box_sub1_col.prop(self, 'render_scale', text='scale') + box_sub1_col.label('Stethoscope MK2 settings') box_sub1_col.prop(self, 'stethoscope_view_scale', text='scale') - box_sub1_col.prop(self, 'stethoscope_view_xy_multiplier', text='xy multiplier') - box_sub1_col.label('index viewer settings') + box_sub1_col.label('Index Viewer settings') box_sub1_col.prop(self, 'index_viewer_scale', text='scale') col3 = row_sub1.split().column() -- GitLab From 4a8593656955811836af559487b9cbf220b30060 Mon Sep 17 00:00:00 2001 From: Victor Doval <10011941+vicdoval@users.noreply.github.com> Date: Sat, 17 Nov 2018 21:41:57 +0100 Subject: [PATCH 06/23] line mk3 with AB and OD modes (#2294) * line mk3 with AB and OD modes * updated tests to mk3 * lime mk3 docs --- docs/nodes/generator/line_mk3.rst | 84 ++++++ index.md | 2 +- .../ROADS_LARGE_nikitron_2014.json | 2 +- json_examples/Design/lights_colors.json | 2 +- .../hilbert_to_circle.json | 2 +- json_examples/Shapes/bind_sections_shape.json | 4 +- nodes/generator/line_mk3.py | 240 ++++++++++++++++++ {nodes/generator => old_nodes}/line_mk2.py | 2 + utils/modules/geom_utils.py | 3 + 9 files changed, 335 insertions(+), 6 deletions(-) create mode 100644 docs/nodes/generator/line_mk3.rst create mode 100644 nodes/generator/line_mk3.py rename {nodes/generator => old_nodes}/line_mk2.py (98%) diff --git a/docs/nodes/generator/line_mk3.rst b/docs/nodes/generator/line_mk3.rst new file mode 100644 index 000000000..f1827eada --- /dev/null +++ b/docs/nodes/generator/line_mk3.rst @@ -0,0 +1,84 @@ +Line +==== + +Functionality +------------- + +Line generator creates a series of connected segments based on the number of vertices and the length between them. Just a standard subdivided line along X axis + +Inputs +------ + +All parameters except **Center** are vectorized. They will accept single or multiple values. +Both inputs will accept a single number or an array of them. It also will work an array of arrays:: + + [2] + [2, 4, 6] + [[2], [4]] + +Parameters +---------- + +All parameters except **Center** can be given by the node or an external input. + + ++---------------+---------------+--------------+---------------------------------------------------------+ +| Param | Type | Default | Description | ++===============+===============+==============+=========================================================+ +| **Direction** | Enum | "X" | Ortho direction, "from A to B" or "Origin and Direction"| ++---------------+---------------+--------------+---------------------------------------------------------+ +| **N Verts** | Int | 2 | number of vertices. The minimum is 2 | ++---------------+---------------+--------------+---------------------------------------------------------+ +| **Step** | Float | 1.00 | length between vertices | ++---------------+---------------+--------------+---------------------------------------------------------+ +| **Center** | Boolean     | False       | center line around 0 | ++---------------+---------------+--------------+---------------------------------------------------------+ +| **Normalize** | Boolean     | False       | determine steps by fixing total length line | ++---------------+---------------+--------------+---------------------------------------------------------+ +| **Size** | Float     | 10     | total length of the segment | ++---------------+---------------+--------------+---------------------------------------------------------+ +| **A, O** | Vector     | (0,0,0)     | origin point of line | ++---------------+---------------+--------------+---------------------------------------------------------+ +| **B** | Vector     | (0.5,0.5,0.5)| end point of line | ++---------------+---------------+--------------+---------------------------------------------------------+ +| **D** | Vector     | (1,1,1) | direction of the line | ++---------------+---------------+--------------+---------------------------------------------------------+ + +Outputs +------- + +**Vertices** and **Edges**. Verts and Edges will be generated. Depending on the inputs, the node will generate only one or multiples independent lines. See examples below. + + +Example of usage +---------------- + +.. image:: https://user-images.githubusercontent.com/10011941/47713459-a177d880-dc3a-11e8-935b-a2fa494dc49b.png + :alt: LineDemo1.PNG + +The first example shows just an standard line with 5 vertices and 1.00 ud between them + +.. image:: https://user-images.githubusercontent.com/10011941/47713473-a9377d00-dc3a-11e8-94ab-39095761788c.png + :alt: LineDemo2.PNG + +In this example the step is given by a series of numbers + +.. image:: https://user-images.githubusercontent.com/10011941/47713477-ad639a80-dc3a-11e8-9884-6568326d2a33.png + :alt: LineDemo3.PNG + +You can create multiple lines if input multiple lists + +.. image:: https://user-images.githubusercontent.com/10011941/47713487-b3597b80-dc3a-11e8-996b-17edf1cec9da.png + :alt: LineDemo4.PNG + +The AB mode will output a divided segment for each vector pair, the step can be used to change the proportions of the divisions + +.. image:: https://user-images.githubusercontent.com/10011941/47713488-b3597b80-dc3a-11e8-9e6e-f742d0338ba5.png + :alt: LineDemo5.PNG + +The "OD" mode can be used to visualize normals + +.. image:: https://user-images.githubusercontent.com/10011941/47713490-b3597b80-dc3a-11e8-9b6d-b937c0375ec5.png + :alt: LineDemo5.PNG + +Advanced example using the node to create a paraboloid grid \ No newline at end of file diff --git a/index.md b/index.md index ba22b0c3d..bda1d1523 100644 --- a/index.md +++ b/index.md @@ -10,7 +10,7 @@ > Failing to follow these points will break the node category parser. ## Generator - SvLineNodeMK2 + SvLineNodeMK3 SvPlaneNodeMK2 SvNGonNode SvBoxNode diff --git a/json_examples/Architecture/ROADS_LARGE_nikitron_2014.json b/json_examples/Architecture/ROADS_LARGE_nikitron_2014.json index 6d486ed95..230efdff8 100644 --- a/json_examples/Architecture/ROADS_LARGE_nikitron_2014.json +++ b/json_examples/Architecture/ROADS_LARGE_nikitron_2014.json @@ -659,7 +659,7 @@ "width": 140.0 }, "Line MK2": { - "bl_idname": "SvLineNodeMK2", + "bl_idname": "SvLineNodeMK3", "color": [ 0.0, 0.5, diff --git a/json_examples/Design/lights_colors.json b/json_examples/Design/lights_colors.json index d856a1c2d..a07c30e05 100644 --- a/json_examples/Design/lights_colors.json +++ b/json_examples/Design/lights_colors.json @@ -40,7 +40,7 @@ "width": 140.0 }, "Line MK2": { - "bl_idname": "SvLineNodeMK2", + "bl_idname": "SvLineNodeMK3", "color": [ 0.0, 0.5, diff --git a/json_examples/ParametricModelling/hilbert_to_circle.json b/json_examples/ParametricModelling/hilbert_to_circle.json index 9bcc586ec..29a658d2e 100644 --- a/json_examples/ParametricModelling/hilbert_to_circle.json +++ b/json_examples/ParametricModelling/hilbert_to_circle.json @@ -94,7 +94,7 @@ "width": 140.0 }, "Line": { - "bl_idname": "SvLineNodeMK2", + "bl_idname": "SvLineNodeMK3", "color": [ 0.0, 0.5, diff --git a/json_examples/Shapes/bind_sections_shape.json b/json_examples/Shapes/bind_sections_shape.json index 5c1f1e649..a1922ed7c 100644 --- a/json_examples/Shapes/bind_sections_shape.json +++ b/json_examples/Shapes/bind_sections_shape.json @@ -276,7 +276,7 @@ "width": 391.4091796875 }, "Line MK2": { - "bl_idname": "SvLineNodeMK2", + "bl_idname": "SvLineNodeMK3", "color": [ 0.9200000166893005, 0.9200000166893005, @@ -297,7 +297,7 @@ "width": 140.0 }, "Line MK2.001": { - "bl_idname": "SvLineNodeMK2", + "bl_idname": "SvLineNodeMK3", "color": [ 0.9200000166893005, 0.9200000166893005, diff --git a/nodes/generator/line_mk3.py b/nodes/generator/line_mk3.py new file mode 100644 index 000000000..cfaefc875 --- /dev/null +++ b/nodes/generator/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' + + 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('StringsSocket', "Size") + size_socket.prop_name = 'size' + size_socket.hide_safe = not self.normalize + + def set_vector_sockets(self): + si = self.inputs + si.new('VerticesSocket', "A").prop_name = 'v3_input_0' + si.new('VerticesSocket', "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('StringsSocket', "Num").prop_name = 'num' + si.new('StringsSocket', "Step").prop_name = 'step' + self.set_size_socket() + self.set_vector_sockets() + self.outputs.new('VerticesSocket', "Vertices", "Vertices") + self.outputs.new('StringsSocket', "Edges", "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) diff --git a/nodes/generator/line_mk2.py b/old_nodes/line_mk2.py similarity index 98% rename from nodes/generator/line_mk2.py rename to old_nodes/line_mk2.py index 319ee5449..fe9060b93 100644 --- a/nodes/generator/line_mk2.py +++ b/old_nodes/line_mk2.py @@ -48,6 +48,8 @@ class SvLineNodeMK2(bpy.types.Node, SverchCustomTreeNode): bl_label = 'Line MK2' bl_icon = 'GRIP' + replacement_nodes = [('SvLineNodeMK3', None, None)] + def upgrade_if_needed(self): """ This allows us to keep the node mk2 - on the fly node upgrade""" if "Size" not in self.inputs: diff --git a/utils/modules/geom_utils.py b/utils/modules/geom_utils.py index 2529f95be..1ad0870d4 100644 --- a/utils/modules/geom_utils.py +++ b/utils/modules/geom_utils.py @@ -38,6 +38,9 @@ def normalize(v): def sub_v3_v3v3(a, b): return a[0]-b[0], a[1]-b[1], a[2]-b[2] +def add_v3_v3v3(a, b): + return a[0]+b[0], a[1]+b[1], a[2]+b[2] + def madd_v3_v3v3fl(a, b, f=1.0): return a[0]+b[0]*f, a[1]+b[1]*f, a[2]+b[2]*f -- GitLab From 65f187fa98a967e5095c6a9d5a6bbdf3873606ba Mon Sep 17 00:00:00 2001 From: Dealga McArdle Date: Sun, 18 Nov 2018 11:00:07 +0100 Subject: [PATCH 07/23] small prophylactic for no output --- nodes/analyzer/kd_tree_edges_mk2.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nodes/analyzer/kd_tree_edges_mk2.py b/nodes/analyzer/kd_tree_edges_mk2.py index e635bb24e..2c2a37ebd 100644 --- a/nodes/analyzer/kd_tree_edges_mk2.py +++ b/nodes/analyzer/kd_tree_edges_mk2.py @@ -63,6 +63,8 @@ class SvKDTreeEdgesNodeMK2(bpy.types.Node, SverchCustomTreeNode): try: verts = inputs['Verts'].sv_get()[0] linked = outputs['Edges'].is_linked + if not linked: + return except (IndexError, KeyError) as e: return -- GitLab From ab50e95716126c13bc746cf0ed41dca600384508 Mon Sep 17 00:00:00 2001 From: Alessandro Zomparelli Date: Sun, 18 Nov 2018 20:52:12 +0100 Subject: [PATCH 08/23] Added "BW" Grayscale and "A" Alpha channel support (#2314) * Added Alpha and BW grayscale * fixed grayscale --- nodes/analyzer/evaluate_image.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/nodes/analyzer/evaluate_image.py b/nodes/analyzer/evaluate_image.py index 66d455d50..2cc58c686 100644 --- a/nodes/analyzer/evaluate_image.py +++ b/nodes/analyzer/evaluate_image.py @@ -90,6 +90,8 @@ class EvaluateImageNode(bpy.types.Node, SverchCustomTreeNode): self.outputs.new('StringsSocket', "R") self.outputs.new('StringsSocket', "G") self.outputs.new('StringsSocket', "B") + self.outputs.new('StringsSocket', "A") + self.outputs.new('StringsSocket', "BW") def draw_buttons(self, context, layout): layout.label(text="Image:") @@ -133,11 +135,13 @@ class EvaluateImageNode(bpy.types.Node, SverchCustomTreeNode): else: domV = self.domV # outputs + bw = [[]] red = [[]] + alpha = [[]] green = [[]] blue = [[]] - if outputs['R'].is_linked or outputs['G'].is_linked or outputs['B'].is_linked: + if outputs['R'].is_linked or outputs['G'].is_linked or outputs['B'].is_linked or outputs['A'].is_linked or outputs['BW'].is_linked: imag = bpy.data.images[self.image_name].pixels[:] sizeU = bpy.data.images[self.image_name].size[0] sizeV = bpy.data.images[self.image_name].size[1] @@ -163,7 +167,6 @@ class EvaluateImageNode(bpy.types.Node, SverchCustomTreeNode): elif self.boundU == 'EXTEND': u = max(0,min(u,sizeU-1)) - if self.shift_mode_V == 'ALTERNATE': if (u0//sizeU)%2: v += floor(sizeV*self.shiftV) if self.shift_mode_V == 'CONSTANT': @@ -183,10 +186,16 @@ class EvaluateImageNode(bpy.types.Node, SverchCustomTreeNode): red[0].append(imag[index]) green[0].append(imag[index+1]) blue[0].append(imag[index+2]) + alpha[0].append(imag[index+3]) else: red[0].append(0) green[0].append(0) blue[0].append(0) + alpha[0].append(0) + if outputs['BW'].is_linked: + bw[0] = [0.2126*r + 0.7152*g + 0.0722*b for r,g,b in zip(red[0], green[0], blue[0])] + outputs['BW'].sv_set(bw) + outputs['A'].sv_set(alpha) outputs['R'].sv_set(red) outputs['G'].sv_set(green) outputs['B'].sv_set(blue) -- GitLab From 065af54b3a5b9e0aecc619c81533420a79f13d7b Mon Sep 17 00:00:00 2001 From: Dealga McArdle Date: Sun, 18 Nov 2018 20:56:39 +0100 Subject: [PATCH 09/23] Update evaluate_image.py --- nodes/analyzer/evaluate_image.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/nodes/analyzer/evaluate_image.py b/nodes/analyzer/evaluate_image.py index 2cc58c686..dd77a9496 100644 --- a/nodes/analyzer/evaluate_image.py +++ b/nodes/analyzer/evaluate_image.py @@ -141,10 +141,12 @@ class EvaluateImageNode(bpy.types.Node, SverchCustomTreeNode): green = [[]] blue = [[]] - if outputs['R'].is_linked or outputs['G'].is_linked or outputs['B'].is_linked or outputs['A'].is_linked or outputs['BW'].is_linked: + if any(socket.is_linked for socket in self.outputs): + imag = bpy.data.images[self.image_name].pixels[:] sizeU = bpy.data.images[self.image_name].size[0] sizeV = bpy.data.images[self.image_name].size[1] + for vert in verts: vx = vert[0]*(sizeU-1)/domU vy = vert[1]*sizeV/domV -- GitLab From 283a6627a736894609e0bc75ae0a5540196ea29a Mon Sep 17 00:00:00 2001 From: DolphinDream Date: Tue, 27 Nov 2018 18:11:23 -0500 Subject: [PATCH 10/23] Optimize the linear edge list generation using caching Profiling the edge generation indicated that it is a slow operation and since this edge list is generated over and over in various nodes it makes sense to cache it. Profiling of various nodes (generating edges) indicated a speedup of almost two orders of magnitude in generating the edges compared to the list comprehension counterpart (e.g. edges = [[i, i+1] for i in range(N)]. With the proposed optimization, the list of edges are stored in an edge cache (e.g. [[0,1], [1,2]... [n-1,n]] .. and the cache is extended as longer edge lists are requested/generated. Any call to get_edge_list will return a subset of this edge cache thus not having to re-generate the same list over and over. Various nodes like the line, spiral, torus knot, ellipse etc, which that generate lines with linear list of edges can benefit substantially from this speedup. To get the linear edge list use: edges = get_edge_list(n) returning: [[0, 1], [1, 2], ... , [n-1, n]] To get the loop edge list use: edges = get_edge_loop(n) returning: [[0, 1], [1, 2], ... , [n-2, n-1], [n-1, 0]] --- data_structure.py | 49 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/data_structure.py b/data_structure.py index fbc32660d..aabe229c1 100755 --- a/data_structure.py +++ b/data_structure.py @@ -927,3 +927,52 @@ def std_links_processing(matcher): return real_process return decorator + + +# EDGE CACHE settings : used to accellerate the (linear) edge list generation +_edgeCache = {} +_edgeCache["main"] = [] # e.g. [[0, 1], [1, 2], ... , [N-1, N]] (extended as needed) + + +def update_edge_cache(n): + """ + Extend the edge list cache to contain at least n edges. + + NOTE: This is called by the get_edge_list to make sure the edge cache is large + enough, but it can also be called preemptively by the nodes prior to making + multiple calls to get_edge_list in order to pre-augment the cache to a known + size and thus accellearate the subsequent calls to get_edge_list as they + will not have to augment the cache with every call. + """ + m = len(_edgeCache["main"]) # current number of edges in the edge cache + if n > m: # requested #edges < cached #edges ? => extend the cache + _edgeCache["main"].extend([[m + i, m + i + 1] for i in range(n - m)]) + + +def get_edge_list(n): + """ + Get the list of n edges connecting n+1 vertices. + + e.g. [[0, 1], [1, 2], ... , [n-1, n]] + + NOTE: This uses an "edge cache" to accellerate the edge list generation. + The cache is extended automatically as needed to satisfy the largest number + of edges within the node tree and it is shared by all nodes using this method. + """ + update_edge_cache(n) # make sure the edge cache is large enough + return _edgeCache["main"][:n] # return a subset list of the edge cache + + +def get_edge_loop(n): + """ + Get the loop list of n edges connecting n vertices. + + e.g. [[0, 1], [1, 2], ... , [n-2, n-1], [n-1, 0]] + + NOTE: This uses an "edge cache" to accellerate the edge list generation. + The cache is extended automatically as needed to satisfy the largest number + of edges within the node tree and it is shared by all nodes using this method. + """ + nn = n - 1 + update_edge_cache(nn) # make sure the edge cache is large enough + return _edgeCache["main"][:nn] + [[nn, 0]] -- GitLab From 503e629940279b8a1835f74f306c46d6bf85b3d5 Mon Sep 17 00:00:00 2001 From: nikitron Date: Mon, 3 Dec 2018 13:20:29 +0300 Subject: [PATCH 11/23] smooth about poligons --- docs/nodes/modifier_change/smooth.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/nodes/modifier_change/smooth.rst b/docs/nodes/modifier_change/smooth.rst index 7b1da4b4e..42f2e3504 100644 --- a/docs/nodes/modifier_change/smooth.rst +++ b/docs/nodes/modifier_change/smooth.rst @@ -13,7 +13,7 @@ This node has the following inputs: - **Vertices** - **Edges** -- **Faces** +- **Faces** - Only triangles and quads poligons. - **VertMask**. Selected vertices to be smoothed. - **Iterations** - **Clip threshold** @@ -26,7 +26,7 @@ Parameters This node has the following parameters: - **X**, **Y**, **Z**. Toggle axes vertices will be smoothed along. By default mesh is smoothed along all axes. -- **Laplacian Smooth**. Toggles smoothing algorithm: when checked, Laplacian smoothing is used; otherwise, simple averaging scheme will be used. By default not checked. +- **Laplacian Smooth**. Toggles smoothing algorithm: when checked, Laplacian smoothing is used; otherwise, simple averaging scheme will be used. By default not checked. __Deal only with tris and quads, not N-gons__. - **Clip X**, **Clip Y**, **Clip Z**. Toggle axes along which "Mirror Clipping" procedure will be applied. This procedure merges vertices that have X/Y/Z coordinate near zero, withing specified threshold. For example, it can merge vertices `(0.01, 3, 5)` and `(- 0.01, 3, 5)` into one vertex `(0, 3, 5)`. These parameters are available only when **Laplacian Smooth** is off. Not checked by default. - **Preserve volume**. If checked, the mesh will be "blown" a bit after smoothing, to preserve its volume. Available only when **Laplacian Smooth** is on. Checked by default. - **Iterations**. Number of smoothing operation iterations. Default value is 1. This parameter can also be provided as input. -- GitLab From f5424a4ca4dfa6a4c9cb8bc4dca799b18c212c5f Mon Sep 17 00:00:00 2001 From: nikitron Date: Mon, 3 Dec 2018 13:21:08 +0300 Subject: [PATCH 12/23] Update smooth.rst --- docs/nodes/modifier_change/smooth.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/nodes/modifier_change/smooth.rst b/docs/nodes/modifier_change/smooth.rst index 42f2e3504..b9dbbf2e9 100644 --- a/docs/nodes/modifier_change/smooth.rst +++ b/docs/nodes/modifier_change/smooth.rst @@ -26,7 +26,7 @@ Parameters This node has the following parameters: - **X**, **Y**, **Z**. Toggle axes vertices will be smoothed along. By default mesh is smoothed along all axes. -- **Laplacian Smooth**. Toggles smoothing algorithm: when checked, Laplacian smoothing is used; otherwise, simple averaging scheme will be used. By default not checked. __Deal only with tris and quads, not N-gons__. +- **Laplacian Smooth**. Toggles smoothing algorithm: when checked, Laplacian smoothing is used; otherwise, simple averaging scheme will be used. By default not checked. **Deal only with tris and quads, not N-gons**. - **Clip X**, **Clip Y**, **Clip Z**. Toggle axes along which "Mirror Clipping" procedure will be applied. This procedure merges vertices that have X/Y/Z coordinate near zero, withing specified threshold. For example, it can merge vertices `(0.01, 3, 5)` and `(- 0.01, 3, 5)` into one vertex `(0, 3, 5)`. These parameters are available only when **Laplacian Smooth** is off. Not checked by default. - **Preserve volume**. If checked, the mesh will be "blown" a bit after smoothing, to preserve its volume. Available only when **Laplacian Smooth** is on. Checked by default. - **Iterations**. Number of smoothing operation iterations. Default value is 1. This parameter can also be provided as input. -- GitLab From 9992a5bb2cb136b8fcb9c5773b851890fd083992 Mon Sep 17 00:00:00 2001 From: DolphinDream Date: Thu, 6 Dec 2018 22:26:03 -0500 Subject: [PATCH 13/23] Add new PolygonSort node to sort polygons by distance, area, and normal angle --- docs/nodes/list_mutators/polygon_sort.rst | 76 ++++++ index.md | 1 + nodes/list_mutators/polygon_sort.py | 289 ++++++++++++++++++++++ 3 files changed, 366 insertions(+) create mode 100644 docs/nodes/list_mutators/polygon_sort.rst create mode 100644 nodes/list_mutators/polygon_sort.py diff --git a/docs/nodes/list_mutators/polygon_sort.rst b/docs/nodes/list_mutators/polygon_sort.rst new file mode 100644 index 000000000..e3b844512 --- /dev/null +++ b/docs/nodes/list_mutators/polygon_sort.rst @@ -0,0 +1,76 @@ +Polygon Sort +============ + +Functionality +------------- + +Polygon Sort node sorts the input polygons by various criteria: distance, area and normal angle. + +Inputs +------ + +All parameters except **Mode** and **Descending** are vectorized and can take single of multiple input values. + +Parameters +---------- + +All parameters except **Mode** and **Descending** can be given by the node or an external input. + ++----------------+-----------+-----------+--------------------------------------------------------+ +| Param | Type | Default | Description | ++================+===========+===========+========================================================+ +| **Mode** | Enum | "D" | The sorting direction mode: | +| | | | P : Sort by the the distance to a point P | +| | | | D : Sort by the projection along a direction D | +| | | | A : Sort by the area of the polygons | +| | | | NP : Sort by the normal angle relative to point P | +| | | | ND : Sort by the normal angle relative to direction D | ++----------------+-----------+-----------+--------------------------------------------------------+ +| **Descending** | Bool  | False   | Sort the polygons in the descending order. | ++----------------+-----------+-----------+--------------------------------------------------------+ +| **Verts** | Vector  | none     | The vertices of the input mesh to be sorted. | ++----------------+-----------+-----------+--------------------------------------------------------+ +| **Polys** | Polygon  | none     | The polygons of the input mesh to be sorted. | ++----------------+-----------+-----------+--------------------------------------------------------+ +| **Point P** | Vector  | (1,0,0) | The reference vector: Point P. [1] | ++----------------+-----------+-----------+--------------------------------------------------------+ +| **Direction** | Vector  | (1,0,0) | The reference vector: Direction D. [1] | ++----------------+-----------+-----------+--------------------------------------------------------+ + +Notes: +[1] : "Point P" is shown in P and NP mode and "Direction" is shown in D and ND mode. These are used for distance and normal angle calculation. The area mode (A) does not use the input sorting vector. + +Outputs +------- + +**Indices** +The indices of the sorted polygons. + +**Vertices** +The input vertices. + +**Polygons** +The sorted polygons. + +**Quantities** +The quantity by which the polygons are sorted. Depending on the selected mode the output quantities are either Distances, Projections, Angles or Areas. + +Note: The output will be generated when the output sockets are connected. + +Modes +----- +The modes corespond to different sorting criteria and each one has a quanitity by which the polygons are sorted. +* P : In this mode the polygons are sorted by the distance from the center of each polygon to the given point P. +* D : In this mode the polygons are sorted by the projection component of polygon center vector along the given direction D. +* A : In this mode the polygons are sorted by the area of the polygons. +* ND : In this mode the polygons are sorted by the angle between the polygon normal and the given direction vector D. +* NP : In this mode the polygons are sorted by the angle between the polygon normal and the vector pointing form the center of the polygon to the given point P. + +Presets +------- +The node provides a set of predefined sort directions: along X, Y and Z. These buttons will set the mode to "D" and the sorting vector (Direction) to one of the X, Y or Z directions: (1,0,0), (0,1,0) and (0,0,1) respectively. The preset buttons are only visible as long as the sorting vector socket is not connected. + +References: +The algorythm to compute the area is based on descriptions found at this address: http://geomalgorithms.com/a01-_area.html#3D%20Polygons + + diff --git a/index.md b/index.md index bda1d1523..12c4a67e8 100644 --- a/index.md +++ b/index.md @@ -135,6 +135,7 @@ SvListModifierNode SvFixEmptyObjectsNode SvDatetimeStrings + SvPolygonSortNode ## List Main ListJoinNode diff --git a/nodes/list_mutators/polygon_sort.py b/nodes/list_mutators/polygon_sort.py new file mode 100644 index 000000000..b60270d99 --- /dev/null +++ b/nodes/list_mutators/polygon_sort.py @@ -0,0 +1,289 @@ +# ##### 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 BoolProperty, EnumProperty, FloatVectorProperty, StringProperty +from sverchok.node_tree import SverchCustomTreeNode +from sverchok.data_structure import updateNode, fullList, match_long_repeat +from sverchok.utils.sv_operator_mixins import SvGenericCallbackWithParams +from mathutils import Vector +from math import acos, pi, sqrt + +modeItems = [ + ("P", "Point", "Sort by distance to a point", 0), + ("D", "Direction", "Sort by projection along a direction", 1), + ("A", "Area", "Sort by surface area", 2), + ("NP", "Normal Angle Point", "Sort by normal angle to point", 3), + ("ND", "Normal Angle Direction", "Sort by normal angle to direction", 4)] + +directions = {"X": [1, 0, 0], "Y": [0, 1, 0], "Z": [0, 0, 1]} + +socket_names = {"P": "Point", "D": "Direction", "A": "Area", "NP": "Normal", "ND": "Normal"} +socket_props = {"P": "point_P", "D": "point_D", "A": "point_D", "NP": "point_P", "ND": "point_D"} + +quantity_names = {"P": "Distances", "D": "Distances", "A": "Areas", "NP": "Angles", "ND": "Angles"} + + +def polygon_normal(verts, poly): + ''' The normal of the given polygon ''' + v1 = Vector(verts[poly[0]]) + v2 = Vector(verts[poly[1]]) + v3 = Vector(verts[poly[2]]) + v12 = v2 - v1 + v23 = v3 - v2 + normal = v12.cross(v23) + normal.normalize() + + return list(normal) + + +def polygon_area(verts, poly): + ''' The area of the given polygon ''' + if len(poly) < 3: # not a plane - no area + return 0 + + total = Vector([0, 0, 0]) + N = len(poly) + for i in range(N): + vi1 = Vector(verts[poly[i]]) + vi2 = Vector(verts[poly[(i + 1) % N]]) + prod = vi1.cross(vi2) + total[0] += prod[0] + total[1] += prod[1] + total[2] += prod[2] + + normal = Vector(polygon_normal(verts, poly)) + area = abs(total.dot(normal)) / 2 + + return area + + +def polygon_center(verts, poly): + ''' The center of the given polygon ''' + vx = 0 + vy = 0 + vz = 0 + for v in poly: + vx = vx + verts[v][0] + vy = vy + verts[v][1] + vz = vz + verts[v][2] + n = len(poly) + vx = vx / n + vy = vy / n + vz = vz / n + + return [vx, vy, vz] + + +def polygon_distance_P(verts, poly, P): + ''' The distance from the center of the polygon to the given point ''' + C = polygon_center(verts, poly) + CP = [C[0] - P[0], C[1] - P[1], C[2] - P[2]] + distance = sqrt(CP[0] * CP[0] + CP[1] * CP[1] + CP[2] * CP[2]) + + return distance + + +def polygon_distance_D(verts, poly, D): + ''' The projection of the polygon center vector along the given direction ''' + C = polygon_center(verts, poly) + distance = C[0] * D[0] + C[1] * D[1] + C[2] * D[2] + + return distance + + +def polygon_normal_angle_P(verts, poly, P): + ''' The angle between the polygon normal and the vector from polygon center to given point ''' + N = polygon_normal(verts, poly) + C = polygon_center(verts, poly) + V = [P[0] - C[0], P[1] - C[1], P[2] - C[2]] + v1 = Vector(N) + v2 = Vector(V) + v1.normalize() + v2.normalize() + angle = acos(v1.dot(v2)) # the angle in radians + + return angle + + +def polygon_normal_angle_D(verts, poly, D): + ''' The angle between the polygon normal and the given direction ''' + N = polygon_normal(verts, poly) + v1 = Vector(N) + v2 = Vector(D) + v1.normalize() + v2.normalize() + angle = acos(v1.dot(v2)) # the angle in radians + + return angle + + +class SvPolygonSortNode(bpy.types.Node, SverchCustomTreeNode): + """ + Triggers: Polygon, Sorting + Tooltip: Sort the polygons by various criteria: distance, angle, area. + """ + bl_idname = 'SvPolygonSortNode' + bl_label = 'Polygon Sort' + + def sort_polygons(self, verts, polys, V): + ''' Sort polygons and return sorted polygons indices, poly & quantities ''' + + if self.mode == "D": + quantities = [polygon_distance_D(verts, poly, V) for poly in polys] + elif self.mode == "P": + quantities = [polygon_distance_P(verts, poly, V) for poly in polys] + elif self.mode == "A": + quantities = [polygon_area(verts, poly) for poly in polys] + elif self.mode == "NP": + quantities = [polygon_normal_angle_P(verts, poly, V) for poly in polys] + elif self.mode == "ND": + quantities = [polygon_normal_angle_D(verts, poly, V) for poly in polys] + + IQ = [(i, q) for i, q in enumerate(quantities)] + sortedIQs = sorted(IQ, key=lambda kv: kv[1], reverse=self.descending) + + sortedIndices = [IQ[0] for IQ in sortedIQs] + sortedQuantities = [IQ[1] for IQ in sortedIQs] + sortedPolys = [polys[i] for i in sortedIndices] + + return sortedIndices, sortedPolys, sortedQuantities + + def update_sockets(self, context): + ''' Swap sorting vector input socket to P/D based on selected mode ''' + + s = self.inputs[-1] + s.name = socket_names[self.mode] + s.prop_name = socket_props[self.mode] + + # keep the P/D props values synced when changing mode + if self.mode == "P": + self.point_P = self.point_D + else: # self.mode == "D" + self.point_D = self.point_P + + # update output "Quantities" socket with proper name for the mode + o = self.outputs[-1] + o.name = quantity_names[self.mode] + + def set_direction(self, operator): + self.direction = operator.direction + self.mode = "D" + return {'FINISHED'} + + def update_xyz_direction(self, context): + self.point_D = directions[self.direction] + + def update_mode(self, context): + if self.mode == self.last_mode: + return + + self.last_mode = self.mode + self.update_sockets(context) + updateNode(self, context) + + direction = StringProperty( + name="Direction", default="X", update=update_xyz_direction) + + mode = EnumProperty( + name="Mode", items=modeItems, default="D", update=update_mode) + + last_mode = EnumProperty( + name="Last Mode", items=modeItems, default="D") + + point_P = FloatVectorProperty( + name="Point P", description="Reference point for distance and angle calculation", + size=3, default=(1, 0, 0), update=updateNode) + + point_D = FloatVectorProperty( + name="Direction", description="Reference direction for projection and angle calculation", + size=3, default=(1, 0, 0), update=updateNode) + + descending = BoolProperty( + name="Descending", description="Sort in the descending order", + default=False, update=updateNode) + + def sv_init(self, context): + self.inputs.new('VerticesSocket', "Verts") + self.inputs.new('StringsSocket', "Polys") + self.inputs.new('VerticesSocket', "Direction").prop_name = "point_D" + self.outputs.new('VerticesSocket', "Vertices") + self.outputs.new('StringsSocket', "Polygons") + self.outputs.new('StringsSocket', "Indices") + self.outputs.new('StringsSocket', "Distances") + + def draw_buttons(self, context, layout): + col = layout.column(align=False) + + if not self.inputs[-1].is_linked: + row = col.row(align=True) + for direction in "XYZ": + op = row.operator("node.set_sort_direction", text=direction) + op.direction = direction + + col = layout.column(align=True) + row = col.row(align=True) + row.prop(self, "mode", expand=False, text="") + layout.prop(self, "descending") + + def process(self): + if not any(s.is_linked for s in self.outputs): + return + + inputs = self.inputs + input_v = inputs["Verts"].sv_get() + input_p = inputs["Polys"].sv_get() + input_r = inputs[-1].sv_get()[0] # reference: direction or point + + params = match_long_repeat([input_v, input_p, input_r]) + + iList, vList, pList, qList = [], [], [], [] + for v, p, r in zip(*params): + indices, polys, quantities = self.sort_polygons(v, p, r) + iList.append(indices) + vList.append(v) + pList.append(polys) + qList.append(quantities) + + if self.outputs['Vertices'].is_linked: + self.outputs['Vertices'].sv_set(vList) + if self.outputs['Polygons'].is_linked: + self.outputs['Polygons'].sv_set(pList) + if self.outputs['Indices'].is_linked: + self.outputs['Indices'].sv_set(iList) + if self.outputs[-1].is_linked: + self.outputs[-1].sv_set(qList) # sorting quantities + + +class SvSetSortDirection(bpy.types.Operator, SvGenericCallbackWithParams): + bl_label = "Set sort direction" + bl_idname = "node.set_sort_direction" + bl_description = "Set the sorting direction along X, Y or Z" + + direction = StringProperty(default="X") + fn_name = StringProperty(default="set_direction") + + +def register(): + bpy.utils.register_class(SvSetSortDirection) + bpy.utils.register_class(SvPolygonSortNode) + + +def unregister(): + bpy.utils.unregister_class(SvPolygonSortNode) + bpy.utils.unregister_class(SvSetSortDirection) -- GitLab From 987fc40264ae3a48e535cfb54848003f9019807b Mon Sep 17 00:00:00 2001 From: Dealga McArdle Date: Sun, 9 Dec 2018 15:20:03 +0100 Subject: [PATCH 14/23] fixes lobsided edges (#2330) --- nodes/generators_extended/smooth_lines.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nodes/generators_extended/smooth_lines.py b/nodes/generators_extended/smooth_lines.py index 10a9cc548..61b108401 100644 --- a/nodes/generators_extended/smooth_lines.py +++ b/nodes/generators_extended/smooth_lines.py @@ -37,6 +37,9 @@ def find_projected_arc_center(p1, p2, b, radius=0.5): b = Vector(b) c = Vector(p2) + a = (a-b).normalized() + b + c = (c-b).normalized() + b + focal = (a + c) / 2.0 focal_length = (b-focal).length -- GitLab From 14cf49786e685cd54f61a33947b02ab35a22e126 Mon Sep 17 00:00:00 2001 From: kalwalt Date: Thu, 13 Dec 2018 17:28:52 +0100 Subject: [PATCH 15/23] fixing a missed "resolving conflict" --- settings.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/settings.py b/settings.py index cffee0c70..575071d18 100644 --- a/settings.py +++ b/settings.py @@ -163,19 +163,6 @@ class SverchokPreferences(AddonPreferences): enable_live_objin: BoolProperty( description="Objects in edit mode will be updated in object-in Node") -<<<<<<< HEAD - render_scale = FloatProperty( - default=1.0, min=0.01, step=0.01, description='default render scale') - - render_location_xy_multiplier = FloatProperty( - default=1.0, min=0.01, step=0.01, description='default render location xy multiplier') - - stethoscope_view_scale = FloatProperty( - default=1.0, min=0.01, step=0.01, description='default stethoscope scale') - - index_viewer_scale = FloatProperty( - default=1.0, min=0.01, step=0.01, description='default index viewer scale') -======= ## BLF/BGL/GPU scale and location props render_scale: FloatProperty( @@ -192,7 +179,6 @@ class SverchokPreferences(AddonPreferences): default=1.0, min=0.01, step=0.01, description='default index viewer scale') ## ->>>>>>> 28046799183d21dfe88179f1d59efbfd079b6add datafiles = os.path.join(bpy.utils.user_resource('DATAFILES', path='sverchok', create=True)) defaults_location: StringProperty(default=datafiles, description='usually ..data_files\\sverchok\\defaults\\nodes.json') -- GitLab From 8273b681bb506360ca96a2881c27c0260acb9e1a Mon Sep 17 00:00:00 2001 From: kalwalt Date: Thu, 13 Dec 2018 17:32:29 +0100 Subject: [PATCH 16/23] should be fixed now --- settings.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/settings.py b/settings.py index 575071d18..5f0d92e29 100644 --- a/settings.py +++ b/settings.py @@ -279,15 +279,6 @@ class SverchokPreferences(AddonPreferences): row_sub1 = col.row().split(factor=0.5) box_sub1 = row_sub1.box() box_sub1_col = box_sub1.column(align=True) -<<<<<<< HEAD - box_sub1_col.label('Render Scale & Location') - box_sub1_col.prop(self, 'render_location_xy_multiplier', text='xy multiplier') - box_sub1_col.prop(self, 'render_scale', text='scale') - box_sub1_col.label('Stethoscope MK2 settings') - box_sub1_col.prop(self, 'stethoscope_view_scale', text='scale') - box_sub1_col.label('Index Viewer settings') - box_sub1_col.prop(self, 'index_viewer_scale', text='scale') -======= box_sub1_col.label('Render Scale & Location') box_sub1_col.prop(self, 'render_location_xy_multiplier', text='xy multiplier') @@ -299,7 +290,6 @@ class SverchokPreferences(AddonPreferences): box_sub1_col.label('Index Viewer') box_sub1_col.prop(self, 'stethoscope_view_xy_multiplier', text='xy multiplier') box_sub1_col.prop(self, 'index_viewer_scale', text='scale') ->>>>>>> 28046799183d21dfe88179f1d59efbfd079b6add col3 = row_sub1.split().column() col3.label(text='Location of custom defaults') -- GitLab From b063f52dac6502b4aa865fb11ef9fd27c56bb623 Mon Sep 17 00:00:00 2001 From: kalwalt Date: Fri, 14 Dec 2018 19:18:43 +0100 Subject: [PATCH 17/23] texture viewer working but not bw, why? --- nodes/viz/viewer_texture.py | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/nodes/viz/viewer_texture.py b/nodes/viz/viewer_texture.py index 76b0c4ccd..faf5fa500 100644 --- a/nodes/viz/viewer_texture.py +++ b/nodes/viz/viewer_texture.py @@ -121,10 +121,10 @@ def init_texture(width, height, texname, texture, clr): bgl.glBindTexture(bgl.GL_TEXTURE_2D, texname) bgl.glActiveTexture(bgl.GL_TEXTURE0) - # bgl.glTexParameterf(bgl.GL_TEXTURE_2D, bgl.GL_TEXTURE_WRAP_S, bgl.GL_CLAMP) - # bgl.glTexParameterf(bgl.GL_TEXTURE_2D, bgl.GL_TEXTURE_WRAP_T, bgl.GL_CLAMP) - # bgl.glTexParameterf(bgl.GL_TEXTURE_2D, bgl.GL_TEXTURE_MAG_FILTER, bgl.GL_LINEAR) - # bgl.glTexParameterf(bgl.GL_TEXTURE_2D, bgl.GL_TEXTURE_MIN_FILTER, bgl.GL_LINEAR) + bgl.glTexParameterf(bgl.GL_TEXTURE_2D, bgl.GL_TEXTURE_WRAP_S, bgl.GL_CLAMP_TO_EDGE) + bgl.glTexParameterf(bgl.GL_TEXTURE_2D, bgl.GL_TEXTURE_WRAP_T, bgl.GL_CLAMP_TO_EDGE) + bgl.glTexParameterf(bgl.GL_TEXTURE_2D, bgl.GL_TEXTURE_MAG_FILTER, bgl.GL_LINEAR) + bgl.glTexParameterf(bgl.GL_TEXTURE_2D, bgl.GL_TEXTURE_MIN_FILTER, bgl.GL_LINEAR) bgl.glTexImage2D( bgl.GL_TEXTURE_2D, @@ -140,24 +140,22 @@ def simple_screen(x, y, args): def draw_texture(x=0, y=0, w=30, h=10, texname=texname): # function to draw a texture - # bgl.glDisable(bgl.GL_DEPTH_TEST) + bgl.glDisable(bgl.GL_DEPTH_TEST) - # act_tex = bgl.Buffer(bgl.GL_INT, 1) - # bgl.glGetIntegerv(bgl.GL_TEXTURE_2D, act_tex) - # bgl.glEnable(bgl.GL_TEXTURE_2D) - # bgl.glActiveTexture(bgl.GL_TEXTURE0) - # bgl.glTexEnvf(bgl.GL_TEXTURE_ENV, bgl.GL_TEXTURE_ENV_MODE, bgl.GL_REPLACE) - # bgl.glBindTexture(bgl.GL_TEXTURE_2D, texname) - - # # restoring settings - # bgl.glBindTexture(bgl.GL_TEXTURE_2D, act_tex[0]) - # bgl.glDisable(bgl.GL_TEXTURE_2D) + act_tex = bgl.Buffer(bgl.GL_INT, 1) + bgl.glGetIntegerv(bgl.GL_TEXTURE_2D, act_tex) + bgl.glEnable(bgl.GL_TEXTURE_2D) bgl.glActiveTexture(bgl.GL_TEXTURE0) + #bgl.glTexParameterf(bgl.GL_TEXTURE_ENV, bgl.GL_TEXTURE_ENV_MODE, bgl.GL_REPLACE) bgl.glBindTexture(bgl.GL_TEXTURE_2D, texname) shader.bind() - shader.uniform_int("image", 0) + shader.uniform_int("image", act_tex) batch.draw(shader) + + # # restoring settings + bgl.glBindTexture(bgl.GL_TEXTURE_2D, act_tex[0]) + bgl.glDisable(bgl.GL_TEXTURE_2D) pass draw_texture(x=x, y=y, w=width, h=height, texname=texname) -- GitLab From 6fba12d06a799f4ec5765c5fa54c47ef6463caa1 Mon Sep 17 00:00:00 2001 From: kalwalt Date: Sun, 16 Dec 2018 18:09:13 +0100 Subject: [PATCH 18/23] added a custom shader - this work for BW textures --- nodes/viz/viewer_texture.py | 34 ++++++++++++++++++++++++++++++++-- ui/sv_shader.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 ui/sv_shader.py diff --git a/nodes/viz/viewer_texture.py b/nodes/viz/viewer_texture.py index faf5fa500..8430a5a7e 100644 --- a/nodes/viz/viewer_texture.py +++ b/nodes/viz/viewer_texture.py @@ -51,6 +51,7 @@ class SvTextureViewerDirSelect(bpy.types.Operator, SvGenericDirectorySelector): bl_label = "Pick directory" + size_tex_list = [ ('XS', 'XS', 'extra small squared tex: 64px', '', 64), ('S', 'S', 'small squared tex: 128px', '', 128), @@ -100,6 +101,34 @@ factor_buffer_dict = { 'RGBA': 4 # GL_RGBA } +vertex_shader = ''' + uniform mat4 ModelViewProjectionMatrix; +/* Keep in sync with intern/opencolorio/gpu_shader_display_transform_vertex.glsl */ + in vec2 texCoord; + in vec2 pos; + + out vec2 texCoord_interp; + + void main() + { + gl_Position = ModelViewProjectionMatrix * vec4(pos.xy, 0.0f, 1.0f); + gl_Position.z = 1.0; + texCoord_interp = texCoord; + } +''' + +fragment_shader = ''' + + in vec2 texCoord_interp; + out vec4 fragColor; + + uniform sampler2D image; + + void main() + { + fragColor = texture(image, texCoord_interp).rrrr; + } +''' def transfer_to_image(pixels, name, width, height, mode): # transfer pixels(data) from Node tree to image viewer @@ -360,7 +389,8 @@ class SvTextureViewerNode(bpy.types.Node, SverchCustomTreeNode): name = bgl.Buffer(bgl.GL_INT, 1) bgl.glGenTextures(1, name) self.texture[n_id] = name[0] - init_texture(width, height, name[0], texture, gl_color_constant) + #init_texture(width, height, name[0], texture, gl_color_constant) + init_texture(width, height, name[0], texture, bgl.GL_RED) multiplier, scale = self.get_preferences() x, y = [x * multiplier, y * multiplier] @@ -379,7 +409,7 @@ class SvTextureViewerNode(bpy.types.Node, SverchCustomTreeNode): def generate_batch_shader(self, args): x, y, w, h = args - shader = gpu.shader.from_builtin('2D_IMAGE') + shader = gpu.types.GPUShader(vertex_shader, fragment_shader) batch = batch_for_shader( shader, 'TRI_FAN', { diff --git a/ui/sv_shader.py b/ui/sv_shader.py new file mode 100644 index 000000000..3b9d7646f --- /dev/null +++ b/ui/sv_shader.py @@ -0,0 +1,31 @@ +vertex_shader = ''' + uniform mat4 ModelViewProjectionMatrix; +/* Keep in sync with intern/opencolorio/gpu_shader_display_transform_vertex.glsl */ + in vec2 texCoord; + in vec2 pos; + + out vec2 texCoord_interp; + + void main() + { + gl_Position = ModelViewProjectionMatrix * vec4(pos.xy, 0.0f, 1.0f); + gl_Position.z = 1.0; + texCoord_interp = texCoord; + } +''' + +fragment_shader = ''' + + in vec2 texCoord_interp; + out vec4 fragColor; + + uniform sampler2D image; + + void main() + { + # gl_FragColor = vec4(pos * brightness, 1.0); + vec4 col = texture(image, texCoord_interp); + fragColor = (col.x,col.x,col.x,0.0) + # fragColor = texture(image, texCoord_interp); + } +''' -- GitLab From 94cf9ce3fe7ca3d39100f9ecee9ad69cc26e7088 Mon Sep 17 00:00:00 2001 From: kalwalt Date: Sun, 16 Dec 2018 19:00:46 +0100 Subject: [PATCH 19/23] added GL_RED constant as BW in the dictionary --- nodes/viz/viewer_texture.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/nodes/viz/viewer_texture.py b/nodes/viz/viewer_texture.py index 8430a5a7e..c38311bc5 100644 --- a/nodes/viz/viewer_texture.py +++ b/nodes/viz/viewer_texture.py @@ -90,7 +90,7 @@ gl_color_list = [ ] gl_color_dict = { - 'BW': 6409, # GL_LUMINANCE + 'BW': 6403, # GL_RED 'RGB': 6407, # GL_RGB 'RGBA': 6408 # GL_RGBA } @@ -389,9 +389,8 @@ class SvTextureViewerNode(bpy.types.Node, SverchCustomTreeNode): name = bgl.Buffer(bgl.GL_INT, 1) bgl.glGenTextures(1, name) self.texture[n_id] = name[0] - #init_texture(width, height, name[0], texture, gl_color_constant) - init_texture(width, height, name[0], texture, bgl.GL_RED) - + init_texture(width, height, name[0], texture, gl_color_constant) + multiplier, scale = self.get_preferences() x, y = [x * multiplier, y * multiplier] width, height = [width * scale, height * scale] -- GitLab From bddf68ca4930bd044dcc94140f4a1a5292705606 Mon Sep 17 00:00:00 2001 From: kalwalt Date: Sun, 16 Dec 2018 19:09:39 +0100 Subject: [PATCH 20/23] added missed GL_RED comment and commenting GL stuff, we will see what to do --- nodes/viz/viewer_texture.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/nodes/viz/viewer_texture.py b/nodes/viz/viewer_texture.py index c38311bc5..99405ede4 100644 --- a/nodes/viz/viewer_texture.py +++ b/nodes/viz/viewer_texture.py @@ -96,7 +96,7 @@ gl_color_dict = { } factor_buffer_dict = { - 'BW': 1, # GL_LUMINANCE + 'BW': 1, # GL_RED 'RGB': 3, # GL_RGB 'RGBA': 4 # GL_RGBA } @@ -172,9 +172,9 @@ def simple_screen(x, y, args): bgl.glDisable(bgl.GL_DEPTH_TEST) act_tex = bgl.Buffer(bgl.GL_INT, 1) - bgl.glGetIntegerv(bgl.GL_TEXTURE_2D, act_tex) - bgl.glEnable(bgl.GL_TEXTURE_2D) - bgl.glActiveTexture(bgl.GL_TEXTURE0) + #bgl.glGetIntegerv(bgl.GL_TEXTURE_2D, act_tex) + #bgl.glEnable(bgl.GL_TEXTURE_2D) + #bgl.glActiveTexture(bgl.GL_TEXTURE0) #bgl.glTexParameterf(bgl.GL_TEXTURE_ENV, bgl.GL_TEXTURE_ENV_MODE, bgl.GL_REPLACE) bgl.glBindTexture(bgl.GL_TEXTURE_2D, texname) @@ -390,7 +390,7 @@ class SvTextureViewerNode(bpy.types.Node, SverchCustomTreeNode): bgl.glGenTextures(1, name) self.texture[n_id] = name[0] init_texture(width, height, name[0], texture, gl_color_constant) - + multiplier, scale = self.get_preferences() x, y = [x * multiplier, y * multiplier] width, height = [width * scale, height * scale] -- GitLab From b728543b7f8a15e5d721b9c7b545efaedc8fa7d1 Mon Sep 17 00:00:00 2001 From: kalwalt Date: Sun, 16 Dec 2018 21:02:01 +0100 Subject: [PATCH 21/23] added boolean but i get an error: TypeError: object of type 'int' has no len() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Do not touch the line above. # Everything below will be removed. # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # # Sul branch b28_prelease_master # Il tuo branch è avanti rispetto a 'origin/b28_prelease_master' di 1 commit. # # Changes to be committed: # # modified: nodes/viz/viewer_texture.py --- nodes/viz/viewer_texture.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/nodes/viz/viewer_texture.py b/nodes/viz/viewer_texture.py index 99405ede4..e3e2b4116 100644 --- a/nodes/viz/viewer_texture.py +++ b/nodes/viz/viewer_texture.py @@ -123,9 +123,14 @@ fragment_shader = ''' out vec4 fragColor; uniform sampler2D image; + uniform bool ColorMode; void main() { + if (ColorMode==true) + { + fragColor = texture(image, texCoord_interp); + } fragColor = texture(image, texCoord_interp).rrrr; } ''' @@ -180,6 +185,7 @@ def simple_screen(x, y, args): shader.bind() shader.uniform_int("image", act_tex) + shader.uniform_bool("ColorMode",cMode) batch.draw(shader) # # restoring settings @@ -371,6 +377,9 @@ class SvTextureViewerNode(bpy.types.Node, SverchCustomTreeNode): size_tex = 0 width = 0 height = 0 + cMode = 0 + if (self.color_mode == 'RGB' or 'RGBA'): + cMode = 1 if self.to_image_viewer: @@ -395,7 +404,7 @@ class SvTextureViewerNode(bpy.types.Node, SverchCustomTreeNode): x, y = [x * multiplier, y * multiplier] width, height = [width * scale, height * scale] - batch, shader = self.generate_batch_shader((x, y, width, height)) + batch, shader = self.generate_batch_shader((x, y, width, height, cMode)) draw_data = { 'tree_name': self.id_data.name[:], 'mode': 'custom_function', @@ -407,11 +416,12 @@ class SvTextureViewerNode(bpy.types.Node, SverchCustomTreeNode): nvBGL2.callback_enable(n_id, draw_data) def generate_batch_shader(self, args): - x, y, w, h = args + x, y, w, h, cMode = args shader = gpu.types.GPUShader(vertex_shader, fragment_shader) batch = batch_for_shader( shader, 'TRI_FAN', { + "ColorMode": cMode, "pos": ((x, y), (x + w, y), (x + w, y - h), (x, y - h)), "texCoord": ((0, 1), (1, 1), (1, 0), (0, 0)) }, -- GitLab From 3e2496e2422f3721e9ac18b45e226cfb848fb040 Mon Sep 17 00:00:00 2001 From: kalwalt Date: Mon, 17 Dec 2018 00:23:17 +0100 Subject: [PATCH 22/23] error not completely solved - now only BW --- nodes/viz/viewer_texture.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/nodes/viz/viewer_texture.py b/nodes/viz/viewer_texture.py index e3e2b4116..62b109333 100644 --- a/nodes/viz/viewer_texture.py +++ b/nodes/viz/viewer_texture.py @@ -127,13 +127,18 @@ fragment_shader = ''' void main() { - if (ColorMode==true) + if (ColorMode) { + fragColor = texture(image, texCoord_interp); + + }else{ + + fragColor = texture(image, texCoord_interp).rrrr; } - fragColor = texture(image, texCoord_interp).rrrr; } ''' +cMode = ((False,)) def transfer_to_image(pixels, name, width, height, mode): # transfer pixels(data) from Node tree to image viewer @@ -185,7 +190,7 @@ def simple_screen(x, y, args): shader.bind() shader.uniform_int("image", act_tex) - shader.uniform_bool("ColorMode",cMode) + shader.uniform_bool("ColorMode", cMode) batch.draw(shader) # # restoring settings @@ -377,9 +382,8 @@ class SvTextureViewerNode(bpy.types.Node, SverchCustomTreeNode): size_tex = 0 width = 0 height = 0 - cMode = 0 if (self.color_mode == 'RGB' or 'RGBA'): - cMode = 1 + cMode = ((True,)) if self.to_image_viewer: @@ -416,14 +420,13 @@ class SvTextureViewerNode(bpy.types.Node, SverchCustomTreeNode): nvBGL2.callback_enable(n_id, draw_data) def generate_batch_shader(self, args): - x, y, w, h, cMode = args + x, y, w, h, cM = args shader = gpu.types.GPUShader(vertex_shader, fragment_shader) batch = batch_for_shader( shader, 'TRI_FAN', { - "ColorMode": cMode, "pos": ((x, y), (x + w, y), (x + w, y - h), (x, y - h)), - "texCoord": ((0, 1), (1, 1), (1, 0), (0, 0)) + "texCoord": ((0, 1), (1, 1), (1, 0), (0, 0)), }, ) return batch, shader -- GitLab From 4c21f318aad91270e50aed040321a06624cc3aa2 Mon Sep 17 00:00:00 2001 From: kalwalt Date: Mon, 17 Dec 2018 13:52:55 +0100 Subject: [PATCH 23/23] now it output the correct color (BW, RGB, RGBA)! --- nodes/viz/viewer_texture.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/nodes/viz/viewer_texture.py b/nodes/viz/viewer_texture.py index 62b109333..e264d130c 100644 --- a/nodes/viz/viewer_texture.py +++ b/nodes/viz/viewer_texture.py @@ -175,9 +175,9 @@ def init_texture(width, height, texname, texture, clr): def simple_screen(x, y, args): # draw a simple scren display for the texture # border_color = (0.390805, 0.754022, 1.000000, 1.00) - texture, texname, width, height, batch, shader = args + texture, texname, width, height, batch, shader, cMod = args - def draw_texture(x=0, y=0, w=30, h=10, texname=texname): + def draw_texture(x=0, y=0, w=30, h=10, texname=texname, c=cMod): # function to draw a texture bgl.glDisable(bgl.GL_DEPTH_TEST) @@ -190,7 +190,7 @@ def simple_screen(x, y, args): shader.bind() shader.uniform_int("image", act_tex) - shader.uniform_bool("ColorMode", cMode) + shader.uniform_bool("ColorMode", c) batch.draw(shader) # # restoring settings @@ -198,7 +198,7 @@ def simple_screen(x, y, args): bgl.glDisable(bgl.GL_TEXTURE_2D) pass - draw_texture(x=x, y=y, w=width, h=height, texname=texname) + draw_texture(x=x, y=y, w=width, h=height, texname=texname, c=cMod) class SvTextureViewerNode(bpy.types.Node, SverchCustomTreeNode): @@ -382,8 +382,12 @@ class SvTextureViewerNode(bpy.types.Node, SverchCustomTreeNode): size_tex = 0 width = 0 height = 0 - if (self.color_mode == 'RGB' or 'RGBA'): + if self.color_mode in ('RGB', 'RGBA'): cMode = ((True,)) + else: + cMode = ((False,)) + + print(cMode) if self.to_image_viewer: @@ -414,7 +418,7 @@ class SvTextureViewerNode(bpy.types.Node, SverchCustomTreeNode): 'mode': 'custom_function', 'custom_function': simple_screen, 'loc': (x, y), - 'args': (texture, self.texture[n_id], width, height, batch, shader) + 'args': (texture, self.texture[n_id], width, height, batch, shader, cMode) } nvBGL2.callback_enable(n_id, draw_data) -- GitLab