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

Generalize Coons Patch node. #4853

Merged
merged 3 commits into from
Jan 2, 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
27 changes: 21 additions & 6 deletions docs/nodes/surface/coons_patch.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Surface from Four Curves
========================
Surface from Boundary Curves
============================

Functionality
-------------
Expand All @@ -18,6 +18,11 @@ That is, the second curve must begin at the point where the first curve ends;
and the third curve must begin at the point where the second curve ends; and so
on.

It is also possible to omit the fourth curve, thus to build a surface from
three boundary curves. If the third curve does not end in the same point where
the first curve started, the node will use a straight line segment as fourth
curve.

The surface is calculated as a Coons patch, see https://en.wikipedia.org/wiki/Coons_patch.

When all provided curves are NURBS or NURBS-like, then the node will try to
Expand All @@ -34,12 +39,13 @@ This node has the following inputs:

* **Curves**. The list of curves to build a surface form. This input can accept
data with nesting level of 1 or 2 (list of curves or list of lists of
curves). Each list of curves must have length of 4. This input is available
curves). Each list of curves must have length of 3 or 4. This input is available
and mandatory only if **Input** parameter is set to **List of Curves**.
* **Curve1**, **Curve2**, **Curve3**, **Curve4**. Curves to build surface from.
These inputs can accept data with nesting level 1 only (list of curves).
These inputs are available and mandatory only if **Input** parameter is set
to **4 Curves**.
These inputs are available only if **Input** parameter is set
to **4 Curves**. Inputs **Curve1**, **Curve2** and **Curve3** are mandatory.
**Curve4** input is optional.

Parameters
----------
Expand All @@ -49,7 +55,7 @@ This node has the following parameters:
* **Input**. This defines how the curves are provided. The following options are available:

* **List of curves**. All curves are provided in a single input **Curves**.
* **4 Curves**. Each curve is provided in separate input **Curve1** - **Curve4**.
* **Separate inputs**. Each curve is provided in separate input **Curve1** - **Curve4**.

The default value is **List of Curves**.

Expand Down Expand Up @@ -103,3 +109,12 @@ It is possible to use the node together with "Split Curve" node to generate a su

.. image:: https://user-images.githubusercontent.com/284644/82479760-3deb1f80-9aec-11ea-8411-22ffd273259f.png

It is possible to use only three boundary curves:

.. image:: https://user-images.githubusercontent.com/284644/210209607-113759b7-9992-4e5d-870d-6aa6dafe0c32.png

If the third curve end does not coincide with the beginning of the first curve,
the node will close the cycle with a straight line segment:

.. image:: https://user-images.githubusercontent.com/284644/210209611-052c63b6-ef39-4c12-a047-d7d369f3469c.png

36 changes: 28 additions & 8 deletions nodes/surface/coons_patch.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,17 @@

from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode, zip_long_repeat, ensure_nesting_level
from sverchok.utils.curve import SvCurve
from sverchok.utils.curve.core import SvCurve
from sverchok.utils.curve.primitives import SvLine, SvPointCurve
from sverchok.utils.surface.coons import coons_surface, GENERIC, NURBS_ONLY, NURBS_IF_POSSIBLE

class SvCoonsPatchNode(SverchCustomTreeNode, bpy.types.Node):
"""
Triggers: Coons Patch / Surface form four curves
Tooltip: Generate a surface (Coons Patch) from four boundary curves
Triggers: Coons Patch / Surface form three or four boundary curves
Tooltip: Generate a surface (Coons Patch) from three or four boundary curves
"""
bl_idname = 'SvCoonsPatchNode'
bl_label = 'Surface from Four Curves'
bl_label = 'Surface from Boundary Curves'
bl_icon = 'SURFACE_DATA'
sv_icon = 'SV_COONS_PATCH'

Expand Down Expand Up @@ -53,8 +54,8 @@ def update_sockets(self, context):
updateNode(self, context)

modes = [
('LIST', "List of curves", "Input is provided as a list of curves, which must have 4 items", 0),
('FOUR', "4 curves", "Four separate Curve inputs are provided", 1)
('LIST', "List of curves", "Input is provided as a list of curves, which must have 3 or 4 items", 0),
('FOUR', "Separate inputs", "Three or four separate Curve inputs are provided", 1)
]

input_mode : EnumProperty(
Expand Down Expand Up @@ -83,6 +84,8 @@ def run_check(self, curves):
pairs = list(zip(curves, curves[1:]))
pairs.append((curves[-1], curves[0]))
for idx, (curve1, curve2) in enumerate(pairs):
if curve1 is None or curve2 is None:
continue
_, t_max_1 = curve1.get_u_bounds()
t_min_2, _ = curve2.get_u_bounds()
end1 = curve1.evaluate(t_max_1)
Expand All @@ -91,24 +94,39 @@ def run_check(self, curves):
if distance > self.max_rho:
raise Exception("Distance between the end of {}'th curve and the start of {}'th curve is {} - too much".format(idx, idx+1, distance))

def make_closing_curve(self, first_curve, last_curve):
pt1 = last_curve.get_end_points()[1]
pt2 = first_curve.get_end_points()[0]
if np.linalg.norm(pt1 - pt2) < 1e-6:
return SvPointCurve(pt1)
else:
return SvLine.from_two_points(pt1, pt2)

def get_input(self):
if self.input_mode == 'LIST':
curve_list_s = self.inputs['Curves'].sv_get()
curve_list_s = ensure_nesting_level(curve_list_s, 2, data_types=(SvCurve,))
for curves in curve_list_s:
if len(curves) == 3:
curve4 = None
curves.append(curve4)
if len(curves) != 4:
raise Exception("List of curves must contain exactly 4 curve objects!")
yield curves
else:
curve1_s = self.inputs['Curve1'].sv_get()
curve2_s = self.inputs['Curve2'].sv_get()
curve3_s = self.inputs['Curve3'].sv_get()
curve4_s = self.inputs['Curve4'].sv_get()

curve1_s = ensure_nesting_level(curve1_s, 1, data_types=(SvCurve,))
curve2_s = ensure_nesting_level(curve2_s, 1, data_types=(SvCurve,))
curve3_s = ensure_nesting_level(curve3_s, 1, data_types=(SvCurve,))
curve4_s = ensure_nesting_level(curve4_s, 1, data_types=(SvCurve,))

if self.inputs['Curve4'].is_linked:
curve4_s = self.inputs['Curve4'].sv_get()
curve4_s = ensure_nesting_level(curve4_s, 1, data_types=(SvCurve,))
else:
curve4_s = [None]

for curve1, curve2, curve3, curve4 in zip_long_repeat(curve1_s, curve2_s, curve3_s, curve4_s):
yield [curve1, curve2, curve3, curve4]
Expand All @@ -121,6 +139,8 @@ def process(self):
for curves in self.get_input():
if self.check:
self.run_check(curves)
if curves[3] is None:
curves[3] = self.make_closing_curve(curves[0], curves[2])
surface = coons_surface(*curves, use_nurbs=self.use_nurbs)
surface_out.append(surface)

Expand Down
2 changes: 1 addition & 1 deletion utils/curve/bezier.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class SvBezierCurve(SvCurve, SvBezierSplitMixin):
Bezier curve of arbitrary degree.
"""
def __init__(self, points):
self.points = points
self.points = np.asarray(points)
self.tangent_delta = 0.001
n = self.degree = len(points) - 1
self.__description__ = "Bezier[{}]".format(n)
Expand Down
57 changes: 57 additions & 0 deletions utils/curve/primitives.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,63 @@ def get_polyline_vertices(self):
def is_closed(self, *args):
return False

def extrude_along_vector(self, vector):
return self.to_nurbs().extrude_along_vector(vector)

def make_revolution_surface(self, point, direction, v_min, v_max, global_origin):
return self.to_nurbs().make_revolution_surface(point, direction, v_min, v_max, global_origin)

def make_ruled_surface(self, curve2, vmin, vmax):
return self.to_nurbs().make_ruled_surface(curve2, vmin, vmax)

def extrude_to_point(self, point):
return self.to_nurbs().extrude_to_point(point)

def lerp_to(self, curve2, coefficient):
return self.to_nurbs().lerp_to(curve2, coefficient)

class SvPointCurve(SvCurve):
__description__ = "Single-Point"

def __init__(self, point):
self.point = np.asarray(point)

def evaluate(self, t):
return self.point

def evaluate_array(self, ts):
points = np.empty((len(ts),3))
points[:] = self.point
return points

def get_u_bounds(self):
return (0.0, 1.0)

def get_degree(self):
return 1

def to_bezier(self):
u_min, u_max = self.get_u_bounds()
p1 = self.evaluate(u_min)
p2 = self.evaluate(u_max)
return SvBezierCurve([p1, p2])

def to_bezier_segments(self):
return [self.to_bezier()]

def is_closed(self, *args):
return False

def concatenate(self, curve2, *args):
return curve2

def to_nurbs(self, implementation = SvNurbsMaths.NATIVE):
return self.to_bezier().to_nurbs()

def reverse(self):
return SvPointCurve(self.point)


def rotate_radius(radius, normal, thetas):
"""Internal method"""
ct = np.cos(thetas)[np.newaxis].T
Expand Down
5 changes: 3 additions & 2 deletions utils/surface/coons.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from sverchok.utils.curve import knotvector as sv_knotvector
from sverchok.utils.curve.core import UnsupportedCurveTypeException
from sverchok.utils.curve.nurbs import SvNurbsCurve
from sverchok.utils.curve.algorithms import reverse_curve, reparametrize_curve
from sverchok.utils.curve.algorithms import reverse_curve, reparametrize_curve, unify_curves_degree
from sverchok.utils.curve.nurbs_algorithms import unify_curves, unify_two_curves
from sverchok.utils.surface.core import SvSurface
from sverchok.utils.surface.nurbs import SvNurbsSurface
Expand Down Expand Up @@ -83,10 +83,11 @@ def coons_surface(curve1, curve2, curve3, curve4, use_nurbs=NURBS_IF_POSSIBLE):
return SvCoonsSurface(*curves)
try:
nurbs_curves = [c.reparametrize(0,1) for c in nurbs_curves]
degrees = [c.get_degree() for c in nurbs_curves]
implementation = nurbs_curves[0].get_nurbs_implementation()

nurbs_curves[0], nurbs_curves[2] = unify_curves_degree([nurbs_curves[0], nurbs_curves[2]])
nurbs_curves[0], nurbs_curves[2] = unify_curves([nurbs_curves[0], nurbs_curves[2]])
nurbs_curves[1], nurbs_curves[3] = unify_curves_degree([nurbs_curves[1], nurbs_curves[3]])
nurbs_curves[1], nurbs_curves[3] = unify_curves([nurbs_curves[1], nurbs_curves[3]])

degree_u = nurbs_curves[0].get_degree()
Expand Down