Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix #5015 Increase performance of some Generators #5016

Merged
merged 4 commits into from
Oct 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 31 additions & 19 deletions nodes/generator/circle.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import (fullList, match_long_repeat, updateNode)
import numpy as np


class SvCircleNode(SverchCustomTreeNode, bpy.types.Node):
Expand Down Expand Up @@ -54,43 +55,54 @@ def draw_buttons(self, context, layout):
layout.prop(self, "mode_", text="Mode")

def make_verts(self, Angle, Vertices, Radius):

if Angle < 360:
theta = Angle/(Vertices-1)
else:
theta = Angle/Vertices
listVertX = []
listVertY = []
for i in range(Vertices):
listVertX.append(Radius*cos(radians(theta*i)))
listVertY.append(Radius*sin(radians(theta*i)))

_n1 = np.arange(Vertices, dtype=np.int32)
_theta = _n1*theta
_x = Radius * np.cos(np.radians(_theta))
_y = Radius * np.sin(np.radians(_theta))

if Angle < 360 and self.mode_ == 0:
sigma = radians(Angle)
listVertX[-1] = Radius*cos(sigma)
listVertY[-1] = Radius*sin(sigma)
_x[-1] = Radius*cos(sigma)
_y[-1] = Radius*sin(sigma)
elif Angle < 360 and self.mode_ == 1:
listVertX.append(0.0)
listVertY.append(0.0)
_x = np.hstack( (_x, [0.0]) )
_y = np.hstack( (_y, [0.0]) )
_points = np.column_stack((_x, _y))
_points = np.insert(_points, 2, [0], axis=1)
_list_points = _points.tolist()

points = list((x,y,0) for x,y in zip(listVertX, listVertY) )
return points
return _list_points

def make_edges(self, Angle, Vertices):
listEdg = [(i, i+1) for i in range(Vertices-1)]

_n = np.arange(Vertices, dtype=np.int32)
_edges = np.column_stack( (_n[:-1], _n[1:]) )

if Angle < 360 and self.mode_ == 1:
listEdg.append((0, Vertices))
listEdg.append((Vertices-1, Vertices))
# Close circle like Packman (throw center of circle)
_edges = np.vstack( (_edges, (Vertices-1, Vertices) ))
_edges = np.vstack( (_edges, (Vertices , 0) ))
else:
listEdg.append((Vertices-1, 0))
return listEdg
# Close circle from last point to first point
_edges = np.vstack( (_edges, (Vertices-1, 0)))

_list_edges = _edges.tolist()
return _list_edges

def make_faces(self, Angle, Vertices):
listPlg = list(range(Vertices))

_arr_indexes = np.arange(Vertices, dtype=np.int32)
if Angle < 360 and self.mode_ == 1:
listPlg.insert(0, Vertices)
return [listPlg]
_arr_indexes = np.hstack( (Vertices, _arr_indexes) )
_list_indexes = _arr_indexes.tolist()

return [_list_indexes]

def process(self):

Expand Down
178 changes: 93 additions & 85 deletions nodes/generator/cylinder_mk2.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def resample_1D_array(profile, samples, cyclic):
return resampled_profile


def make_verts(rt, rb, np, nm, h, t, ph, s, profile_p, profile_m, flags):
def make_verts(rt, rb, npar, nmer, h, t, ph, s, profile_p, profile_m, flags):
"""
Generate cylinder vertices for the given parameters
rt : top radius
Expand All @@ -71,102 +71,110 @@ def make_verts(rt, rb, np, nm, h, t, ph, s, profile_p, profile_m, flags):
h = h * s

if len(profile_p) < 2: # no profile given (make profile all ones)
resampled_profile_p = [1] * nm
resampled_profile_p = [1] * nmer
else: # resample PARALLELS profile to nm parallel points [0-1]
samples = [m / nm for m in range(nm + 1)]
samples = [m / nmer for m in range(nmer + 1)]
resampled_profile_p = resample_1D_array(profile_p, samples, cyclic)

if len(profile_m) < 2: # no profile given (make profile all ones)
resampled_profile_m = [1] * np
resampled_profile_m = [1] * npar
else: # resample MERIDIANS profile to np meridian points [0-1)
samples = [p / (np - 1) for p in range(np)]
samples = [p / (npar - 1) for p in range(npar)]
resampled_profile_m = resample_1D_array(profile_m, samples, False)

dA = 2.0 * pi / nm # angle increment from one meridian to the next
dH = h / (np - 1) # height increment from one parallel to the next
dT = t / (np - 1) # twist increment from one parallel to the next
dA = 2.0 * pi / nmer # angle increment from one meridian to the next
dH = h / (npar - 1) # height increment from one parallel to the next
dT = t / (npar - 1) # twist increment from one parallel to the next
dZ = - h / 2 if center else 0 # center offset

vert_list = []
add_verts = vert_list.append if separate else vert_list.extend
for p in range(np): # for every point on a meridian (traverse the parallels)
f = p / (np - 1) # interpolation factor between rb and rt
r = rb * (1 - f) + rt * f # interpolated radius between bottom and top radii
rp = r * resampled_profile_m[p] # modulate radius by meridian profile
z = dZ + dH * p
phase = ph + dT * p # parallel's total phase (phase + delta twist)

verts = []
for m in range(nm): # for every point on a parallel (traverse the meridians)
rpm = rp * resampled_profile_p[m] # modulate radius by parallel profile
a = phase + dA * m
x = rpm * cos(a)
y = rpm * sin(a)
verts.append([x, y, z])

add_verts(verts)

return vert_list


def make_edges(P, M, separate):
"""
Generate the cylinder edges for the given parameters
P : number of parallels (= number of points in a meridian)
M : number of meridians (= number of points in a parallel)
separate: split the parallels into separate edge lists
"""
edge_list = []

if separate: # replicate edges in one parallel for every meridian point
edge_list = [get_edge_loop(M)] * P
else:
add_edge = edge_list.append
# generate PARALLELS edges (close paths)
for i in range(P): # for every point on a meridian
for j in range(M - 1): # for every point on a parallel (minus last)
add_edge([i * M + j, i * M + j + 1])
add_edge([(i + 1) * M - 1, i * M]) # close the path

# generate MERIDIANS edges (open paths)
for j in range(M): # for every point on a parallel
for i in range(P - 1): # for every point on a meridian (minus last)
add_edge([i * M + j, (i + 1) * M + j])

return edge_list

_p = np.arange(npar)
_f = _p / (npar - 1) # interpolation factor between rb and rt
_r = rb + (rt-rb)*_f # interpolated radius between bottom and top radii
_res_par, _res_mer = np.meshgrid( resampled_profile_p[:-1], resampled_profile_m, indexing='xy') # get matrix resampled profiles parallels x meridian
_rpm = np.meshgrid(np.arange(nmer), _r)[1] * _res_par*_res_mer # get r for any point
_nmer, _npar = np.meshgrid( np.arange(nmer), np.arange(npar), indexing='xy') # get indices for meridian and parallels
_phase = ph + dT * _npar
_a = dA*_nmer + _phase
_x = _rpm*np.cos(_a) # is a matrix now
_y = _rpm*np.sin(_a)
_z = dZ + dH * _npar
_verts = np.dstack( (_x, _y, _z) ) # combine all three matrixes in a single matrix with x/y/z in one cell [[[x1,y1,z1], ...]]
_verts = _verts.reshape(-1,3)
if separate:
_verts = np.array(np.split( _verts, npar ))

_list_verts = _verts.tolist()
return _list_verts

def make_polys(P, M, cap_bottom, cap_top, separate):
def make_edges_and_faces(P, M, cap_bottom, cap_top, flags, get_edges, get_faces):
"""
Generate the cylinder polygons for the given parameters
P : number of parallels (= number of points in a meridian)
M : number of meridians (= number of points in a parallel)
cap_bottom : turn on/off the bottom cap generation
cap_top : turn on/off the top cap generation
separate: split the parallels into separate poly lists
flags : separate (split the parallels into separate poly lists), cyclic of parallels
get_edges, get_faces: boolean what generate: edges or faces or both? (to decrease memory by arr_vert)
"""
poly_list = []

if separate:
poly_list = [[list(range(M))]] * P
else:
add_poly = poly_list.append
for i in range(P - 1):
for j in range(M - 1):
add_poly([i * M + j, i * M + j + 1, (i + 1) * M + j + 1, (i + 1) * M + j])
add_poly([(i + 1) * M - 1, i * M, (i + 1) * M, (i + 2) * M - 1])

if cap_bottom:
cap = [j for j in reversed(range(M))]
add_poly(cap)

if cap_top:
offset = (P - 1) * M
cap = [offset + j for j in range(M)]
add_poly(cap)

return poly_list
separate, center, cyclic = flags

N1 = M # Y
N2 = P # X

if get_edges or get_faces:
steps = np.arange( N1*N2 )
_arr_verts = np.array ( np.split(steps, N2 ) ) # split array by rows (V)
if cyclic:
_arr_verts = np.hstack( (_arr_verts, np.array([_arr_verts[:,0]]).T ) ) # append first row to bottom to horizontal circle


_list_edges = []
if get_edges:
if separate: # replicate edges in one parallel for every meridian point
_list_edges = [get_edge_loop(M)] * P
else:
_arr_h_edges = np.empty((N2, N1, 2), 'i' ) if cyclic else np.empty((N2, N1-1, 2), 'i' )
_arr_h_edges[:, :, 0] = _arr_verts[ : , :-1 ] # hor_edges
_arr_h_edges[:, :, 1] = _arr_verts[ : , 1: ] # hor_edges
_arr_h_edges = _arr_h_edges.reshape(-1,2)

_arr_v_edges = np.empty( (N2-1, N1, 2), 'i' ) if cyclic else np.empty( (N2-1, N1-1, 2), 'i' )
_arr_v_edges[:, :, 0] = _arr_verts[ :-1, :-1] # vert_edges
_arr_v_edges[:, :, 1] = _arr_verts[1: , :-1] # vert_edges
_arr_v_edges = _arr_v_edges.reshape(-1,2)

_edges = np.concatenate( ( _arr_h_edges, _arr_v_edges, ) )
_list_edges = _edges.tolist()


_list_faces = []

if get_faces:
if separate:
_list_faces = [[list(range(M))]] * P
else:
steps = np.arange( N1*N2 )
_arr_verts = np.array ( np.split(steps, N2 ) ) # split array by rows (V)
if cyclic:
_arr_verts = np.hstack( (_arr_verts, np.array([_arr_verts[:,0]]).T ) ) # append first column to the left to horizontal circle

_arr_faces = np.empty((N2-1, N1, 4), 'i' ) if cyclic else np.empty((N2-1, N1-1, 4), 'i' )
_arr_faces[:, :, 0] = _arr_verts[ 1: , :-1 ]
_arr_faces[:, :, 1] = _arr_verts[ 1: , 1: ]
_arr_faces[:, :, 2] = _arr_verts[ :-1, 1: ]
_arr_faces[:, :, 3] = _arr_verts[ :-1, :-1 ]
_arr_faces_res = _arr_faces.reshape(-1,4)
_list_faces = _arr_faces_res.tolist()
if cap_top:
l_top = [_arr_verts[-1].tolist()] # cyclic do not apply on the top and the bottom
_list_faces.extend(l_top)

if cap_bottom:
l_bottom = [_arr_verts[0].tolist()]
l_bottom.extend(_list_faces)
_list_faces = l_bottom

return _list_edges, _list_faces

class SvCylinderNodeMK2(SverchCustomTreeNode, bpy.types.Node):
"""
Expand Down Expand Up @@ -369,12 +377,12 @@ def process(self):
if verts_output_linked:
verts = make_verts(rt, rb, np, nm, h, t * au, ph * au, s, profile_p, profile_m, flags)
verts_list.append(verts)
if edges_output_linked:
edges = make_edges(np, nm, self.separate)
edges_list.append(edges)
if polys_output_linked:
polys = make_polys(np, nm, self.cap_bottom, self.cap_top, self.separate)
polys_list.append(polys)
if edges_output_linked or polys_output_linked:
edges, polys = make_edges_and_faces(np, nm, self.cap_bottom, self.cap_top, flags, edges_output_linked, polys_output_linked)
if edges_output_linked:
edges_list.append(edges)
if polys_output_linked:
polys_list.append(polys)

# outputs
if verts_output_linked:
Expand Down
Loading