import bpy
import numpy as np
from numpy import cos, pi, sin, sqrt, tan
import ddg._error_messages as em
from ddg.arrays import CurveList, _parse_periodicity, _parse_points
from ddg.blender.mesh import mesh
from ddg.blender.object import blender_data_to_object, link
###################################################
# create and add generic curves, set properties
###################################################
def _insert(x, i, value):
n, m = x.shape
value_ = value if isinstance(value, np.ndarray) else value * np.ones((n, 1))
if i == m or i == -1:
return np.column_stack((x, value_))
else:
return np.insert(x, i, value_, 1)
def _homogenise(x, i):
return _insert(x, i, 1)
def _add_spline(curve, points, periodicity, /):
n = points.shape[0]
spline = curve.splines.new("POLY")
spline.points.add(n - 1)
points_with_weights = _homogenise(points, -1)
for i, p in enumerate(points_with_weights):
spline.points[i].co = p
spline.use_cyclic_u = periodicity
[docs]def curve(name, points, periodic):
"""Create a Blender curve from points.
Parameters
----------
name : str
The `name` attribute of the curve.
points : array_like of shape (num_points, 3)
The points of the curve. The dtype must be some kind of floating point
dtype.
periodic : bool
The curve is closed if and only if periodic is `True`.
Returns
-------
bpy.types.Curve
Raises
------
ValueError
If there already exists a curve with the given name.
"""
name_error = (
em.str_(f"bpy.data.curves already contains a curve whose name is '{name}'.")
if name in bpy.data.curves
else None
)
points_ = _parse_points(points, "points")
periodicity_ = _parse_periodicity(periodic, "periodic")
if (
isinstance(name_error, em.str_)
or isinstance(points_, em.str_)
or isinstance(periodicity_, em.str_)
):
raise ValueError(em.concatenate_messages(name_error, points_, periodicity_))
else:
curve = bpy.data.curves.new(name, "CURVE")
curve.dimensions = "3D"
_add_spline(curve, points_, periodicity_)
return curve
[docs]def disconnected_curve(name, curve_components, /):
"""Create a Blender curve from connected components.
Parameters
----------
name : str
The `name` attribute of the curve.
curve_components : ddg.arrays.DisconnectedCurve
Returns
-------
bpy.types.Curve
Raises
------
ValueError
If there already exists a curve with the given name.
"""
if name in bpy.data.curves:
raise ValueError(
f"bpy.data.curves already contains a curve whose name is '{name}'."
)
curve_ = CurveList(curve_components)
curve_data = bpy.data.curves.new(name, "CURVE")
curve_data.dimensions = "3D"
for c in curve_:
_add_spline(curve_data, c.points, c.periodicity)
return curve_data
[docs]def curve_as_mesh(name, points):
"""Create a Blender curve from points and convert it to a curve.
Parameters
----------
name : str
The `name` attribute of the mesh.
points : array_like of shape (num_points, 3)
The points of the curve/mesh. The dtype must be some kind of floating
point dtype.
Returns
-------
bpy.types.Mesh
See also
--------
ddg.blender.mesh.mesh
ddg.blender.curve.curve
"""
name_error = (
em.str_(f"bpy.data.curves already contains a curve whose name is '{name}'.")
if name in bpy.data.curves
else None
)
points_ = _parse_points(points, "points")
if isinstance(name_error, em.str_) or isinstance(points_, em.str_):
raise ValueError(em.concatenate_messages(name_error, points_))
n = points_.shape[0]
segments = np.column_stack((np.arange(n - 1), np.arange(1, n)))
return mesh(name, points, np.zeros((0, 2), int), segments)
[docs]def curve_object(
points,
periodic,
curve_and_object_name,
collection,
):
"""Create a curve from points and wrap it in a Blender object.
Parameters
----------
points : array_like of shape (num_points, 3)
The points of the curve. The dtype must be some kind of floating point
dtype.
periodic : bool
The curve is closed if and only if periodic is `True`.
curve_and_object_name : str or tuple[str, str]
The `name` attribute of the curve and the object. If only one string is
passed, then use the same name for both.
collection : bpy.types.Collection, str, None
If `None`, then the Blender object is not linked.
Otherwise, the Collection or name of collection to link the object to.
Returns
-------
bpy.types.Object
Raises
------
ValueError
If there already exists a Blender curve or Blender object with the same name.
"""
if isinstance(curve_and_object_name, str):
c_name = curve_and_object_name
object_name = curve_and_object_name
else:
c_name, object_name = curve_and_object_name
curve_name_error = (
em.str_(f"bpy.data.curves already contains a curve whose name is '{c_name}'.")
if c_name in bpy.data.curves
else None
)
object_name_error = (
em.str_(
f"bpy.data.objects already contains a object whose name is '{object_name}'."
)
if object_name in bpy.data.objects
else None
)
points_ = _parse_points(points, "points")
periodicity_ = _parse_periodicity(periodic, "periodic")
if (
isinstance(curve_name_error, em.str_)
or isinstance(object_name_error, em.str_)
or isinstance(points_, em.str_)
or isinstance(periodicity_, em.str_)
):
raise ValueError(
em.concatenate_messages(
curve_name_error, object_name_error, points_, periodicity_
)
)
else:
c = curve(c_name, points_, periodicity_)
bobj = blender_data_to_object(object_name, c)
if collection is not None:
link(bobj, collection)
return bobj
[docs]def disconnected_curve_object(curve_components, curve_and_object_name, collection, /):
"""Create a Blender curve from connected components.
Parameters
----------
curve_components : sequence of ddg.arrays.Curve
curve_and_object_name : str or tuple[str, str]
The `name` attribute of the curve and the object. If only one string is
passed, then use the same name for both.
collection : bpy.types.Collection, str, None
If `None`, then the Blender object is not linked.
Otherwise, the Collection or name of collection to link the object to.
Returns
-------
bpy.types.Object
Raises
------
ValueError
If there already exists a Blender curve or Blender object with the same name.
"""
if isinstance(curve_and_object_name, str):
c_name = curve_and_object_name
object_name = curve_and_object_name
else:
c_name, object_name = curve_and_object_name
curve_name_error = (
em.str_(f"bpy.data.curves already contains a curve whose name is '{c_name}'.")
if c_name in bpy.data.curves
else None
)
object_name_error = (
em.str_(
f"bpy.data.objects already contains a object whose name is '{object_name}'."
)
if object_name in bpy.data.objects
else None
)
curve_components_ = curve_components
if isinstance(curve_name_error, em.str_) or isinstance(object_name_error, em.str_):
raise ValueError(em.concatenate_messages(curve_name_error, object_name_error))
else:
c = disconnected_curve(c_name, curve_components_)
bobj = blender_data_to_object(object_name, c)
if collection is not None:
link(bobj, collection)
return bobj
[docs]def create_curve(
coordinates, name="Curve", type_="POLY", with_weights=False, cyclic=None, curve=None
):
"""Create a curve object from given coordinates.
Parameters
----------
coordinates : numpy.ndarray of shape (n,i) or list of such numpy.ndarray
Array containing the coordinates of the splines.
* A single 2D array means a single spline, a list of 2D arrays means a
collection of splines.
* `i` is 4 if `type` is "POLY" and `with_weights` is True. Otherwise
`i` is 3.
* `n` is the number of (control) points of each curve. Can be different
for each curve.
name : str (default="Curve")
Name of the new curve object.
type_ : {"POLY", "BEZIER", "NURBS"}, (default="POLY")
Type of the created splines.
with_weights : bool (default=False)
Specify whether weights are given.
* This is only checked if `type` is "POLY".
* If False, the weights will default to 1.0.
cyclic : list of bool (default=None)
Specify which splines are cyclic (defaulting to non-cyclic).
curve : bpy.types.Curve (default=None)
When given, the curve object to store the created data
Returns
-------
bpy.types.Curve
"""
if curve is None:
curvedata = bpy.data.curves.new(name=name, type="CURVE")
curvedata.dimensions = "3D"
else:
curvedata = curve
curvedata.splines.clear()
# determine whether coordinates are given for multiple curves
# We can't do len(np.shape(coordinates)) == 2 because coordinates might be
# a ragged array and creating an array from ragged data without specifying
# dtype=object is deprecated since numpy 1.19.0. The previous solution
# probably only worked accidentally because the ragged array will have
# shape (n,) and not (n,m,i) as was probably assumed.
if len(np.shape(coordinates[0])) == 1:
coordinates = [coordinates]
if cyclic is None:
cyclic = [False for i in range(len(coordinates))]
for curve_coords, cyc in zip(coordinates, cyclic):
spline = curvedata.splines.new(type_)
if type_ == "BEZIER":
spline.bezier_points.add(len(curve_coords) - 1)
for i, c in enumerate(curve_coords):
if with_weights:
c = c[0:3]
bezier_point = spline.bezier_points[i]
bezier_point.co = c
bezier_point.handle_left_type = "AUTO"
bezier_point.handle_right_type = "AUTO"
else:
spline.points.add(len(curve_coords) - 1)
for i, c in enumerate(curve_coords):
if not with_weights:
c = np.append(c, 1.0)
spline.points[i].co = c
spline.use_cyclic_u = cyc
if type_ == "NURBS":
spline.order_u = len(spline.points) - 1
spline.use_endpoint_u = True
return curvedata
[docs]def add_curve(curvedata, name="Curve", location=(0.0, 0.0, 0.0), collection=None):
"""Add a `bpy.types.Curve` object to a collection.
Parameters
----------
curvedata : bpy.types.Curve
Curve to add.
name : str (default="Curve")
Name of the `bpy.types.Object` object.
location : triple of float (default=(0.0, 0.0, 0.0))
Location of the new `bpy.types.Object` object.
collection : bpy.types.Collection, optional
Collection to add the curve to.
Returns
-------
bpy.types.Object
The new created object containing the curve.
Notes
-----
* The `collection` option defaults to `bpy.context.scene.collection`.
"""
objectdata = bpy.data.objects.new(name, curvedata)
objectdata.location = location
if not collection:
collection = bpy.context.scene.collection
collection.objects.link(objectdata)
return objectdata
[docs]def new_bezier_curve(ctr_pts, handles, closed=False, name="Curve", collection=None):
"""Create a new bezier curve.
Parameters
----------
ctr_pts : Iterable of triples of float
Control points of the bezier curve.
handles : Iterable of tuples of triples of float
Left and right handles at the control points.
closed : bool (default=False)
Specify whether curve is closed.
name : str (default="Curve")
Name of the `bpy.types.Curve` and `bpy.types.Object` object.
collection : bpy.types.Collection (default=None)
Collection the curve shall be linked to.
Returns
-------
bpy.types.Curve or (bpy.types.Object, bpy.types.Curve)
The created curve object if `collection` is None. Else the
curve object and the object linked to `collection` containing it.
See Also
--------
new_bezier_circle, new_bezier_arc
"""
curve = bpy.data.curves.new(name, "CURVE")
curve.dimensions = "3D"
spline = curve.splines.new("BEZIER")
spline.use_cyclic_u = closed
add_bezier_points(spline, ctr_pts, handles, new=True)
if collection:
obj = bpy.data.objects.new(name, curve)
collection.objects.link(obj)
collection.update()
return (obj, curve)
else:
return curve
[docs]def set_curve_properties(curve, **properties):
"""Set curve properties for a Blender curve.
Parameters
----------
curve : bpy.types.Object or bpy.types.Curve
Curve to set properties.
**properties : keyword arguments
Properties to set.
"""
if not isinstance(curve, bpy.types.Curve):
curve = curve.data
for option in properties:
setattr(curve, option, properties[option])
###################################################
# create specific curves
###################################################
[docs]def create_circle_data(
name="Circle", radius=1.0, location=(0.0, 0.0, 0.0), rotation=(0.0, 0.0, 0.0)
):
"""Create a new bezier circle using standard `bpy.ops` methods.
Creates a new `bpy.types.Curve` of bezier type, wraps it in a
`bpy.types.Object` and links this object to `bpy.context.scene.collection`.
Parameters
----------
name : str (default="Circle")
Name of the created objects.
radius : float (default=1.0)
Radius of the circle.
location : triple of float (default=(0.0, 0.0, 0.0))
Center of the `bpy.types.Object` object.
rotation : triple of float (default=(0.0, 0.0, 0.0))
Rotation of the `bpy.types.Object` object.
Returns
-------
bpy.types.Curve
"""
bpy.ops.curve.primitive_bezier_circle_add(
radius=radius, location=location, rotation=rotation
)
circleObject = bpy.context.active_object
circleGeometry = circleObject.data
bpy.data.objects.remove(circleObject)
circleGeometry.name = name
circleGeometry.materials.append(None)
return circleGeometry
[docs]def new_bezier_circle(trafo=np.eye(4), collection=None):
"""Create a new bezier circle.
Parameters
----------
trafo : numpy ndarray (default=numpy.eye(4))
Similarity transformation applied to the circle before adding.
collection : bpy.types.Collection (default=None)
Collection the curve shall be linked to.
Returns
-------
bpy.types.Curve or (bpy.types.Object, bpy.types.Curve)
The created curve object if `collection` is None. Else the
curve object and the object linked to `collection` containing it.
See Also
--------
new_bezier_arc
Notes
-----
* The circle lies in the xy-plane before applying `trafo`.
* The first control point will be `(1, 0, 0)` before applying `trafo`.
* The circle is oriented in counterclockwise direction per default.
"""
ctr_pts = [(1, 0, 0), (0, 1, 0), (-1, 0, 0), (0, -1, 0)]
tmp = 4 * (sqrt(2) - 1) / 3
handles = [
[(1, -tmp, 0), (1, tmp, 0)],
[(tmp, 1, 0), (-tmp, 1, 0)],
[(-1, tmp, 0), (-1, -tmp, 0)],
[(-tmp, -1, 0), (tmp, -1, 0)],
]
ctr_pts = [np.dot(trafo, v) for v in ctr_pts]
handles = [[np.dot(trafo, left), np.dot(trafo, right)] for left, right in handles]
return new_bezier_curve(
ctr_pts, handles, closed=True, name="Circle", collection=collection
)
[docs]def new_bezier_arc(
phi, nbr_ctr_pts="AUTO", trafo=np.eye(4), reverse=False, collection=None
):
"""Create a circular arc bezier curve.
Parameters
----------
phi : float
Angle of the circular arc.
nbr_ctr_pts : int (default="AUTO")
Number of control points evenly spaced on the circular arc.
trafo : numpy.ndarray (default=numpy.eye(4))
Similarity transformation applied to the circle before adding.
reverse : bool (default=False)
Reverse the orientation of added circle.
collection : bpy.types.Collection, optional
Collection the curve shall be linked to.
Returns
-------
bpy.types.Curve or (bpy.types.Object, bpy.types.Curve)
The created curve object if `collection` is None. Else the
curve object and the object linked to `collection` containing it.
See Also
--------
new_bezier_circle
Notes
-----
* The circle lies in the xy-plane before applying `trafo`.
* The first control point will be `(1,0,0)` before applying `trafo`.
* The circle is oriented in counterclockwise direction per default.
"""
if nbr_ctr_pts == "AUTO":
nbr_ctr_pts = int((2 * phi) / pi) + 2
ctr_pts, handles, handle_dist = [], [], (4 / 3) * tan(phi / (2 * nbr_ctr_pts))
for phi in np.linspace(0, phi, nbr_ctr_pts):
v = np.array((cos(phi), sin(phi), 0))
n = np.cross(v, np.array((0, 0, 1)))
handle_left = v - handle_dist * n
handle_right = v + handle_dist * n
ctr_pts.append(v)
handles.append((handle_left, handle_right))
handles[0][0], handles[-1][-1] = ctr_pts[0], ctr_pts[-1]
ctr_pts = [np.dot(trafo, v) for v in ctr_pts]
handles = [[np.dot(trafo, left), np.dot(trafo, right)] for left, right in handles]
if reverse:
ctr_pts = ctr_pts[::-1]
handles = [handle[::-1] for handle in handles[::-1]]
return new_bezier_curve(ctr_pts, handles, name="Arc", collection=collection)
###################################################
# modify generic curves
###################################################
[docs]def add_bezier_points(spline, ctr_pts, handles, new=False):
"""Add control points to a bezier spline.
Parameters
----------
spline : bpy.types.Spline
Spline to operate on.
ctr_pts : Iterable of triple of float
Control points to add.
handles : Iterable of tuple of triple of float
Left and right handles at the control points.
new : bool (default=False)
Is the spline newly created and contains no old data.
See Also
--------
glue_bezier_points
Notes
-----
* This function CHANGES the given spline `spline`.
* The control points and handles are paired by index.
* The first item of a handle is the left handle and the second is the right.
"""
if new:
old_len = 0
spline.bezier_points.add(len(ctr_pts) - 1)
else:
old_len = len(spline.bezier_points)
spline.bezier_points.add(len(ctr_pts))
for pt, co, handle in zip(spline.bezier_points[old_len:], ctr_pts, handles):
pt.co = co
pt.handle_left, pt.handle_right = handle
[docs]def glue_bezier_points(spline, ctr_pts, handles):
"""Add control points to a spline and match the handles.
Parameters
----------
spline : bpy.types.Spline
Spline to operate on.
ctr_pts : Iterable of triple of float
Control points to add.
handles : Iterable of tuple of triple of float
Left and right handles at the control points.
See Also
--------
add_bezier_points
Notes
-----
* This function CHANGES the given spline `spline`.
* The spline `spline` has to contain at least one control point.
* The function overrides the right handle of the last control point
of `spline`.
* `handles` has to contain one more item then `ctr_pts`.
"""
bez_pts, old_len = spline.bezier_points, len(spline.bezier_points)
bez_pts.add(len(ctr_pts))
bez_pts[old_len - 1].handle_right = handles[0][1]
for pt, co, handle in zip(bez_pts[old_len:], ctr_pts, handles[1:]):
pt.co = co
pt.handle_left, pt.handle_right = handle
###################################################
# modify specific curves
###################################################
[docs]def add_circle_points(spline, nbr_ctr_pts=4, trafo=np.eye(4), reverse=False):
"""Add a circle to a spline.
Parameters
----------
spline : bpy.types.Spline
Spline to operate on.
nbr_ctr_pts : int (default=4)
Number of control points evenly spaced on the circle.
trafo : numpy.ndarray (default=numpy.eye(4))
Similarity 4x4 transformation applied to the circle before adding.
reverse : bool (default=False)
Reverse the orientation of added circle.
See Also
--------
add_arc_points, glue_circle_points
Notes
-----
* This function CHANGES the given spline `spline`.
* The circle lies in the xy-plane before applying `trafo`.
* The first control point will be `(1,0,0)` before applying `trafo`.
* The circle is oriented in counterclockwise direction per default.
"""
ctr_pts, handles, handle_dist = [], [], (4 / 3) * tan(pi / (2 * nbr_ctr_pts))
for phi in np.linspace(0, 2 * pi, nbr_ctr_pts + 1):
v = np.array((cos(phi), sin(phi), 0))
n = np.cross(v, np.array((0, 0, 1)))
handle_left = v - handle_dist * n
handle_right = v + handle_dist * n
ctr_pts.append(v)
handles.append((handle_left, handle_right))
handles[0][0], handles[-1][-1] = ctr_pts[0], ctr_pts[-1]
ctr_pts = [np.dot(trafo, v) for v in ctr_pts]
handles = [[np.dot(trafo, left), np.dot(trafo, right)] for left, right in handles]
if reverse:
ctr_pts = ctr_pts[::-1]
handles = [handle[::-1] for handle in handles[::-1]]
add_bezier_points(spline, ctr_pts, handles)
[docs]def add_arc_points(spline, phi, nbr_ctr_pts="AUTO", trafo=np.eye(4), reverse=False):
"""Add a circular arc to a spline.
Parameters
----------
spline : bpy.types.Spline
Spline to operate on.
phi : float
Angle of the circular arc.
nbr_ctr_pts : int (default="AUTO")
Number of control points evenly spaced on the circular arc.
trafo : numpy.ndarray (default=numpy.eye(4))
Similarity transformation applied to the circle before adding.
reverse : bool (default=False)
Reverse the orientation of added circle.
See Also
--------
add_circle_points, glue_arc_points
Notes
-----
* This function CHANGES the given spline `spline`.
* The circle lies in the xy-plane before applying `trafo`.
* The first control point will be `(1,0,0)` before applying `trafo`.
* The circle is oriented in counterclockwise direction per default.
"""
if nbr_ctr_pts == "AUTO":
nbr_ctr_pts = int((2 * phi) / pi) + 2
ctr_pts, handles, handle_dist = [], [], (4 / 3) * tan(phi / (2 * nbr_ctr_pts))
for phi in np.linspace(0, phi, nbr_ctr_pts):
v = np.array((cos(phi), sin(phi), 0))
n = np.cross(v, np.array((0, 0, 1)))
handle_left = v - handle_dist * n
handle_right = v + handle_dist * n
ctr_pts.append(v)
handles.append((handle_left, handle_right))
handles[0][0], handles[-1][-1] = ctr_pts[0], ctr_pts[-1]
ctr_pts = [np.dot(trafo, v) for v in ctr_pts]
handles = [[np.dot(trafo, left), np.dot(trafo, right)] for left, right in handles]
if reverse:
ctr_pts = ctr_pts[::-1]
handles = [handle[::-1] for handle in handles[::-1]]
add_bezier_points(spline, ctr_pts, handles)
[docs]def glue_circle_points(spline, nbr_ctr_pts=4, trafo=np.eye(4), reverse=False):
"""Add a circle to a spline and match the handles.
Parameters
----------
spline : bpy.types.Spline
Spline to operate on.
nbr_ctr_pts : int (default=4)
Number of control points evenly spaced on the circel.
trafo : numpy ndarray (default=numpy.eye(4))
Similarity transformation applied to the circle before adding.
reverse : bool (default=False)
Reverse the orientation of added circle.
See Also
--------
glue_arc_points, add_circle_points
Notes
-----
* This function CHANGES the given spline `spline`.
* The circle lies in the xy-plane before applying `trafo`.
* The first control point will be `(1,0,0)` before applying `trafo`.
* The circle is oriented in counterclockwise direction per default.
"""
ctr_pts, handles, handle_dist = [], [], (4 / 3) * tan(pi / (2 * nbr_ctr_pts))
for phi in np.linspace(0, 2 * pi, nbr_ctr_pts + 1):
v = np.array((cos(phi), sin(phi), 0))
n = np.cross(v, np.array((0, 0, 1)))
handle_left = v - handle_dist * n
handle_right = v + handle_dist * n
ctr_pts.append(v)
handles.append((handle_left, handle_right))
handles[0][0], handles[-1][-1] = ctr_pts[0], ctr_pts[-1]
ctr_pts = [np.dot(trafo, v) for v in ctr_pts]
handles = [[np.dot(trafo, left), np.dot(trafo, right)] for left, right in handles]
glue_bezier_points(spline, ctr_pts[1:], handles)
[docs]def glue_arc_points(spline, phi, nbr_ctr_pts="AUTO", trafo=np.eye(4), reverse=False):
"""Add a circular arc to a spline and match the handles.
Parameters
----------
spline : bpy.types.Spline
Spline to operate on.
phi : float
Angle of the circular arc.
nbr_ctr_pts : int (default="AUTO")
Number of control points evenly spaced on the circular arc.
trafo : numpy.ndarray (default=numpy.eye(4))
Similarity transformation applied to the circle before adding.
reverse : bool (default=False)
Reverse the orientation of added circle.
See Also
--------
glue_circle_points, add_arc_points
Notes
-----
* This function CHANGES the given spline `spline`.
* The circle lies in the xy-plane before applying `trafo`.
* The first control point will be `(1,0,0)` before applying `trafo`.
* The circle is oriented in counterclockwise direction per default.
"""
if nbr_ctr_pts == "AUTO":
nbr_ctr_pts = int((2 * phi) / pi) + 2
ctr_pts, handles, handle_dist = [], [], (4 / 3) * tan(phi / (2 * nbr_ctr_pts))
for phi in np.linspace(0, phi, nbr_ctr_pts):
v = np.array((cos(phi), sin(phi), 0))
n = np.cross(v, np.array((0, 0, 1)))
handle_left = v - handle_dist * n
handle_right = v + handle_dist * n
ctr_pts.append(v)
handles.append((handle_left, handle_right))
handles[0][0], handles[-1][-1] = ctr_pts[0], ctr_pts[-1]
ctr_pts = [np.dot(trafo, v) for v in ctr_pts]
handles = [[np.dot(trafo, left), np.dot(trafo, right)] for left, right in handles]
if reverse:
ctr_pts = ctr_pts[::-1]
handles = [handle[::-1] for handle in handles[::-1]]
glue_bezier_points(spline, ctr_pts[1:], handles)
[docs]def clear(curves=None, do_unlink=True):
"""Delete all given curves.
Parameters
----------
curves : Iterable of bpy.type.Curve (default=True)
Curves to be deleted. If argument is not provided or None all curves
will be deleted.
do_unlink : bool (default=True)
Unlink curves from their scenes, if needed, before deleting them.
"""
if curves is None:
curves = bpy.data.curves
curves = list(curves)
while curves:
bpy.data.curves.remove(curves.pop(), do_unlink=do_unlink)