Коммит 16cb41f9 создал по автору satabol's avatar satabol
Просмотр файлов

Merge branch 'master' into update_menu_nodes_comments_v14

владельцы 9f88c7b4 2a31dd17
......@@ -90,6 +90,10 @@ class FileEvent:
pass
class UndoEvent:
"""Undo handler was executed"""
class TreesGraphEvent:
"""It indicates that something was changed in trees relations defined via
group nodes"""
......
......@@ -50,6 +50,14 @@ def control_center(event):
elif type(event) is ev.TreesGraphEvent:
trees_graph.is_updated = False
# nodes will have another hash id and the comparison method will decide that
# all nodes are new, and won't be able to detect changes, and will update all
# Unlike main trees, groups can't do this via GroupTreeEvent because it
# should be called only when a group is edited by user
elif type(event) is ev.UndoEvent:
for gt in BlTrees().sv_group_trees:
GroupUpdateTree.get(gt).is_updated = False
else:
was_executed = False
......
......@@ -65,6 +65,8 @@ def sv_handler_undo_post(scene):
undo_handler_node_count['sv_groups'] = 0
handle_event(ev.UndoEvent())
@persistent
def sv_update_handler(scene):
......@@ -162,9 +164,9 @@ def sv_post_load(scene):
with catch_log_error():
if any(not n.is_registered_node_type() for ng in BlTrees().sv_trees for n in ng.nodes):
old_nodes.register_all()
old_nodes.mark_all()
dummy_nodes.register_all()
dummy_nodes.mark_all()
old_nodes.mark_all()
dummy_nodes.mark_all()
with catch_log_error():
settings.apply_theme_if_necessary()
......
......@@ -476,6 +476,10 @@ class UpdateTree(SearchTree):
"""Remove tree data or data of all trees from the cache"""
if tree is not None and tree.tree_id in cls._tree_catch:
del cls._tree_catch[tree.tree_id]
# reset nested trees too
for group in (n for n in tree.nodes if hasattr(n, 'node_tree')):
UpdateTree.reset_tree(group.node_tree)
else:
cls._tree_catch.clear()
......
......@@ -10,6 +10,7 @@ This node generates a spherical Surface object. Several parametrizations are ava
* Equirectangular
* Lambert
* Gall Stereographic
* NURBS Sphere
For formulas, please refer to Wikipedia: https://en.wikipedia.org/wiki/List_of_map_projections
......
......@@ -82,6 +82,10 @@ The parameters of the node are (in this order):
* **Display Control Net**, **Control Net Color**, **Control Net Line Width**.
Control display of surface's control net (edges between control points), for
NURBS and NURBS-like surfaces. Control net is not displayed by default.
* **Display Node Lines**, **Node Lines Color**, **Node Lines Width**. Conrtol
display of node lines, i.e. isolines at U and V parameters according to node
values of knotvectors along U and V parameter directions. Node lines are not
displayed by default.
.. image:: https://user-images.githubusercontent.com/14288520/190234804-8789ca5c-d35a-46f3-9c6c-95cc440ce394.png
:target: https://user-images.githubusercontent.com/14288520/190234804-8789ca5c-d35a-46f3-9c6c-95cc440ce394.png
......
......@@ -81,6 +81,10 @@ class Sv3DviewPropsNode(bpy.types.Node, SverchCustomTreeNode):
row = layout.row(align=True)
row.prop(prefs.inputs, 'view_rotate_method', text='orbit', expand=True)
def process(self):
pass
return
def register():
bpy.utils.register_class(Sv3DviewPropsNode)
......
......@@ -7,7 +7,8 @@ from bpy.props import FloatProperty, EnumProperty
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode, zip_long_repeat, ensure_nesting_level
from sverchok.utils.surface import SvLambertSphere, SvEquirectSphere, SvGallSphere, SvDefaultSphere
from sverchok.utils.surface.sphere import SvLambertSphere, SvEquirectSphere, SvGallSphere, SvDefaultSphere
from sverchok.utils.surface.algorithms import build_nurbs_sphere
class SvSphereNode(bpy.types.Node, SverchCustomTreeNode):
"""
......@@ -22,7 +23,8 @@ class SvSphereNode(bpy.types.Node, SverchCustomTreeNode):
('DEFAULT', "Default", "Based on spherical coordinates", 0),
('EQUIRECT', "Equirectangular", "Equirectangular (geographic) projection", 1),
('LAMBERT', "Lambert", "Lambert cylindrical equal-area projection", 2),
('GALL', "Gall Stereographic", "Gall stereographic projection", 3)
('GALL', "Gall Stereographic", "Gall stereographic projection", 3),
('NURBS', "NURBS Sphere", "NURBS Sphere", 4)
]
def update_sockets(self, context):
......@@ -79,8 +81,12 @@ class SvSphereNode(bpy.types.Node, SverchCustomTreeNode):
surface = SvEquirectSphere(np.array(center), radius, theta1)
elif self.projection == 'LAMBERT':
surface = SvLambertSphere(np.array(center), radius)
else:
elif self.projection == 'GALL':
surface = SvGallSphere(np.array(center), radius)
elif self.projection == 'NURBS':
surface = build_nurbs_sphere(np.array(center), radius)
else:
raise Exception("Unsupported projection type")
surfaces_out.append(surface)
self.outputs['Surface'].sv_set(surfaces_out)
......
......@@ -56,6 +56,12 @@ def draw_surfaces(context, args):
if node.draw_edges:
draw_edges(e_shader, item.points_list, item.edges, node.edges_line_width, node.edges_color)
if node.draw_node_lines and item.node_u_isoline_data is not None:
for line in item.node_u_isoline_data:
draw_edges(e_shader, line.points, line.edges, node.node_lines_width, node.node_lines_color)
for line in item.node_v_isoline_data:
draw_edges(e_shader, line.points, line.edges, node.node_lines_width, node.node_lines_color)
if node.draw_control_net and item.cpts_list is not None:
draw_edges(e_shader, item.cpts_list, item.control_net, node.control_net_line_width, node.control_net_color)
......@@ -182,6 +188,23 @@ class SvSurfaceViewerDrawNode(bpy.types.Node, SverchCustomTreeNode):
min = 1, default = 3,
update = updateNode)
draw_node_lines : BoolProperty(
name = "Display node lines",
default = False,
update = updateNode)
node_lines_color: FloatVectorProperty(
name = "Node Lines Color",
default = (0.2, 0.0, 0.0, 1.0),
size = 4, min = 0.0, max = 1.0,
subtype = 'COLOR',
update = updateNode)
node_lines_width : IntProperty(
name = "Node Lines Width",
min = 1, default = 2,
update = updateNode)
light_vector: FloatVectorProperty(
name='Light Direction', subtype='DIRECTION', min=0, max=1, size=3,
default=(0.2, 0.6, 0.4), update=updateNode)
......@@ -220,6 +243,11 @@ class SvSurfaceViewerDrawNode(bpy.types.Node, SverchCustomTreeNode):
row.prop(self, 'control_net_color', text="")
row.prop(self, 'control_net_line_width', text="px")
row = grid.row(align=True)
row.prop(self, 'draw_node_lines', icon='EVENT_N', text='')
row.prop(self, 'node_lines_color', text="")
row.prop(self, 'node_lines_width', text="px")
row = layout.row(align=True)
row.scale_y = 4.0 if self.prefs_over_sized_buttons else 1
self.wrapper_tracked_ui_draw_op(row, SvBakeSurfaceOp.bl_idname, icon='OUTLINER_OB_MESH', text="B A K E")
......
......@@ -46,16 +46,16 @@ def is_old(node_info: Union[str, bpy.types.Node]):
raise TypeError(f"String or Node is expected, {node_info} is given")
def mark_old(node):
def mark_old(node, text="Deprecated node!"):
"""Create a frame node around given one with deprecated label"""
if node.parent and node.parent.label == "Deprecated node!":
if node.parent and node.parent.label == text:
return
ng = node.id_data
frame = ng.nodes.new("NodeFrame")
if node.parent:
frame.parent = node.parent
node.parent = frame
frame.label = "Deprecated node!"
frame.label = text
frame.use_custom_color = True
frame.color = (.8, 0, 0)
frame.shrink = True
......@@ -67,6 +67,22 @@ def mark_all():
if node.bl_idname in old_bl_idnames:
mark_old(node)
def has_old_nodes(tree) -> bool:
"""Recursive search of deprecated nodes"""
for n in tree.nodes:
if n.bl_idname in old_bl_idnames:
return True
elif hasattr(n, 'node_tree') and has_old_nodes(n.node_tree):
return True
return False
# mark group nodes if they have deprecated nodes inside
for node in (n for t in BlTrees().sv_main_trees for n in t.nodes):
if not hasattr(node, 'node_tree'):
continue
if has_old_nodes(node.node_tree):
mark_old(node, text="Has deprecated nodes!")
def register_old(bl_id):
"""Register old node class"""
......
......@@ -96,6 +96,12 @@ def average(knotvectors):
kvs = np.array(knotvectors)
return kvs.mean(axis=0)
def calc_nodes(degree, n_cpts, knotvector):
nodes = np.zeros((n_cpts,))
for i in range(n_cpts):
nodes[i] = knotvector[i+1:i+degree+1].mean()
return nodes
def to_multiplicity(knot_vector, tolerance=1e-6):
count = 0
prev_u = None
......
......@@ -332,14 +332,8 @@ class SvNurbsCurve(SvCurve):
raise Exception("Not implemented!")
def calc_greville_ts(self):
cpts = self.get_control_points()
n = len(cpts)
p = self.get_degree()
gps = np.zeros((n,))
kv = self.get_knotvector()
for i in range(n):
gps[i] = kv[i+1:i+p+1].mean()
return gps
n = len(self.get_control_points())
return sv_knotvector.calc_nodes(self.get_degree(), n, self.get_knotvector())
def calc_greville_points(self):
return self.evaluate_array(self.calc_greville_ts())
......@@ -973,7 +967,7 @@ class SvGeomdlCurve(SvNurbsCurve):
r.u_bounds = self.u_bounds
return r
def remove_knot(self, u, count=1, target=None, if_possible=False):
def remove_knot(self, u, count=1, target=None, if_possible=False, tolerance=None):
if (count is None) == (target is None):
raise Exception("Either count or target must be specified")
......
......@@ -168,7 +168,7 @@ def from_homogenous(control_points):
elif control_points.ndim == 3: # surface
weights = control_points[:,:,3]
weighted = control_points[:,:,0:3]
points = weighted / weights[np.newaxis].T
points = weighted / np.transpose(weights[np.newaxis], axes=(1,2,0))
return points, weights
else:
raise Exception(f"control_points have ndim={control_points.ndim}, supported are only 2 and 3")
......
......@@ -30,6 +30,7 @@ from sverchok.utils.curve.algorithms import (
from sverchok.utils.surface.core import SvSurface, UnsupportedSurfaceTypeException
from sverchok.utils.surface.nurbs import SvNurbsSurface
from sverchok.utils.surface.data import *
from sverchok.utils.nurbs_common import SvNurbsBasisFunctions
from sverchok.utils.logging import info, debug
class SvInterpolatingSurface(SvSurface):
......@@ -1376,3 +1377,91 @@ def remove_excessive_knots(surface, direction, tolerance=1e-6):
return surface
def build_nurbs_sphere(center, radius):
"""
Generate NURBS Surface representing a sphere.
Sphere is defined here as a surface of revolution of
half a circle.
"""
vectorx = np.array([0.0, 0.0, radius])
axis = np.array([0.0, 0.0, 1.0])
normal = np.array([1.0, 0.0, 0.0])
matrix = SvCircle.calc_matrix(normal, vectorx)
matrix = Matrix.Translation(center) @ Matrix(matrix).to_4x4()
arc = SvCircle(matrix=matrix, radius=radius, normal=normal, vectorx=vectorx)
arc.u_bounds = (0.0, pi)
return nurbs_revolution_surface(arc.to_nurbs(), center, axis, 0, 2*pi, global_origin=True)
def deform_nurbs_surface(src_surface, uknots, vknots, points):
"""
Move some control points of a NURBS surface so that at
given parameter values it passes through the given points.
NB: rational surfaces are not supported yet.
Parameters:
* src_surface - SvNurbsSurface instance
* uknots, vknots - np.array of shape (n,): U and V coordinates
of points to be moved
* points: np.array of shape (n,3): desired locations of surface points.
Output:
* SvNurbsSurface instance.
"""
n = len(points)
if len(uknots) != n or len(vknots) != n:
raise Exception("Number of points, uknots and vknots must be equal")
if src_surface.is_rational():
raise UnsupportedSurfaceTypeException("Rational surfaces are not supported yet")
ndim = 3
knotvector_u = src_surface.get_knotvector_u()
knotvector_v = src_surface.get_knotvector_v()
basis_u = SvNurbsBasisFunctions(knotvector_u)
basis_v = SvNurbsBasisFunctions(knotvector_v)
degree_u = src_surface.get_degree_u()
degree_v = src_surface.get_degree_v()
ncpts_u, ncpts_v,_ = src_surface.get_control_points().shape
nsu = np.array([basis_u.derivative(i, degree_u, 0)(uknots) for i in range(ncpts_u)])
nsv = np.array([basis_v.derivative(i, degree_v, 0)(vknots) for i in range(ncpts_v)])
nsu_t = np.transpose(nsu[np.newaxis], axes=(1,0,2)) # (ncpts_u, 1, n)
nsv_t = nsv[np.newaxis] # (1, ncpts_v, n)
ns_t = nsu_t * nsv_t # (ncpts_u, ncpts_v, n)
denominator = ns_t.sum(axis=0).sum(axis=0)
n_equations = n*ndim
n_unknowns = ncpts_u * ncpts_v * ndim
#print(f"Eqs: {n_equations}, Unk: {n_unknowns}")
A = np.zeros((n_equations, n_unknowns))
for u_idx in range(ncpts_u):
for v_idx in range(ncpts_v):
cpt_idx = ncpts_v * u_idx + v_idx
for pt_idx in range(n):
alpha = nsu[u_idx][pt_idx] * nsv[v_idx][pt_idx] / denominator[pt_idx]
for dim_idx in range(ndim):
A[ndim*pt_idx + dim_idx, ndim*cpt_idx + dim_idx] = alpha
src_points = src_surface.evaluate_array(uknots, vknots)
B = np.zeros((n_equations,1))
for pt_idx, point in enumerate(points):
B[pt_idx*3:pt_idx*3+3,0] = point[np.newaxis] - src_points[pt_idx][np.newaxis]
if n_equations == n_unknowns:
print("Well-determined", n_equations)
A1 = np.linalg.inv(A)
X = (A1 @ B).T
elif n_equations < n_unknowns:
print("Underdetermined", n_equations, n_unknowns)
A1 = np.linalg.pinv(A)
X = (A1 @ B).T
else: # n_equations > n_unknowns
print("Overdetermined", n_equations, n_unknowns)
X, residues, rank, singval = np.linalg.lstsq(A, B)
d_cpts = X.reshape((ncpts_u, ncpts_v, ndim))
cpts = src_surface.get_control_points()
surface = SvNurbsSurface.build(src_surface.get_nurbs_implementation(),
degree_u, degree_v, knotvector_u, knotvector_v,
cpts + d_cpts)
return surface
......@@ -12,6 +12,8 @@ import bpy
from sverchok.utils.modules.polygon_utils import pols_normals
from sverchok.utils.modules.vertex_utils import np_vertex_normals
from sverchok.utils.math import np_dot
from sverchok.utils.curve.algorithms import SvIsoUvCurve
from sverchok.utils.curve.bakery import CurveData
def make_quad_edges(n_u, n_v):
edges = []
......@@ -87,6 +89,14 @@ def calc_surface_data(light_vector, surface_color, n_u, n_v, points):
return tris, colors
class SurfaceData(object):
class IsoCurveConfig(object):
def __init__(self):
self.draw_line = True
self.draw_verts = False
self.draw_control_polygon = False
self.draw_control_points = False
self.draw_nodes = False
def __init__(self, node, surface, resolution_u, resolution_v):
self.node = node
self.surface = surface
......@@ -111,6 +121,17 @@ class SurfaceData(object):
else:
self.cpts_list = None
if hasattr(surface, 'calc_greville_us'):
nodes_u = surface.calc_greville_us()
nodes_v = surface.calc_greville_vs()
node_u_isolines = [SvIsoUvCurve(surface, 'U', u) for u in nodes_u]
node_v_isolines = [SvIsoUvCurve(surface, 'V', v) for v in nodes_v]
cfg = SurfaceData.IsoCurveConfig()
self.node_u_isoline_data = [CurveData(cfg, isoline, resolution_v) for isoline in node_u_isolines]
self.node_v_isoline_data = [CurveData(cfg, isoline, resolution_u) for isoline in node_v_isolines]
else:
self.node_u_isoline_data = node_v_isoline_data = None
self.edges = make_quad_edges(resolution_u, resolution_v)
self.tris, self.tri_colors = calc_surface_data(node.light_vector, node.surface_color, resolution_u, resolution_v, self.points)
......
......@@ -119,6 +119,23 @@ class SvNurbsSurface(SvSurface):
def iso_curve(self, fixed_direction, param):
raise Exception("Not implemented")
def is_rational(self, tolerance=1e-4):
weights = self.get_weights()
w, W = weights.min(), weights.max()
return (W - w) > tolerance
def calc_greville_us(self):
n = self.get_control_points().shape[0]
p = self.get_degree_u()
kv = self.get_knotvector_u()
return sv_knotvector.calc_nodes(p, n, kv)
def calc_greville_vs(self):
n = self.get_control_points().shape[1]
p = self.get_degree_v()
kv = self.get_knotvector_v()
return sv_knotvector.calc_nodes(p, n, kv)
def get_homogenous_control_points(self):
"""
......
Поддерживает Markdown
0% или .
You are about to add 0 people to the discussion. Proceed with caution.
Сначала завершите редактирование этого сообщения!
Пожалуйста, зарегистрируйтесь или чтобы прокомментировать