# [PyVista] 绘制样条曲线和管状路径

Please refresh the page if equations are not rendered correctly.
---------------------------------------------------------------

"""
Create a spline/polyline from a numpy array of XYZ vertices using
:func:pyvista.Spline.
"""

import numpy as np
import pyvista as pv

###############################################################################
# Create a dataset to plot

def make_points():
"""Helper to make XYZ points"""
theta = np.linspace(-4 * np.pi, 4 * np.pi, 100)
z = np.linspace(-2, 2, 100)
r = z**2 + 1
x = r * np.sin(theta)
y = r * np.cos(theta)
return np.column_stack((x, y, z))

points = make_points()


## 1. 方法一 （会出现不连续片段）

###############################################################################
# Now let's make a function that can create line cells on a
# :class:pyvista.PolyData mesh given that the points are in order for the
# segments they make.

def lines_from_points(points):
"""
Given an array of points, make a line set.

Parameters
----------
points : np.ndarray
Array of XYZ points. Shape is n-by-3.

Returns
-------
poly : pyvista.PolyData
PolyData with lines cells.
"""
poly = pv.PolyData()
poly.points = points
# cells are a 2d numpy array of the point indices that make each cell
# the first value in each row is the number of points in the cell,
# followed by the first and the second point indices.
cells = np.full((len(points) - 1, 3), 2, dtype=np.int_)
cells[:, 1] = np.arange(0, len(points) - 1, dtype=np.int_)
cells[:, 2] = np.arange(1, len(points), dtype=np.int_)
poly.lines = cells
return poly

line = lines_from_points(points)
line["scalars"] = np.arange(line.n_points)


## 2. 方法二 （较好）

###############################################################################
# That tube has sharp edges at each line segment. This can be mitigated by
# creating a single PolyLine cell for all of the points
def polyline_from_points(points):
"""
Given an array of points, make a PolyLine.

Parameters
----------
points : np.ndarray
Array of XYZ points. Shape is n-by-3.

Returns
-------
poly : pyvista.PolyData
PolyData with PolyLine cell.
"""
poly = pv.PolyData()
poly.points = points
the_cell = np.arange(0, len(points), dtype=np.int_)
the_cell = np.insert(the_cell, 0, len(points))
poly.lines = the_cell
return poly

polyline = polyline_from_points(points)
polyline["scalars"] = np.arange(polyline.n_points)


## 3. 方法三 (最佳)

###############################################################################
# You could also interpolate those points onto a parametric spline

# Create spline with 1000 interpolation points
spline = pv.Spline(points, 1000)

# add scalars to spline and plot it
spline["scalars"] = np.arange(spline.n_points)

# The spline can also be plotted as a plain line
# generate same spline with 400 interpolation points
spline = pv.Spline(points, 400)
# plot without scalars
spline.plot(line_width=4, color="k")

# The radius of the tube can be modulated with scalars
spline["theta"] = 0.4 * np.arange(len(spline.points))