17. curve — Handling curves in pyFormex

This module defines classes and functions specialized for handling curves (one-dimensional geometrical objects) in pyFormex. They can be straight lines, polylines, higher order curves and collections thereof. In general, the curves are 3D, but special cases may be created for handling plane curves.

The most important curve type classes defined in this module are:

  • Curve: a virtual base class for all curve type classes. It can not be instantiated itself: always use one of the derived classes.

  • BezierSpline: curves consisting of a concatenation of Bezier polynomes of degree 1 to 3.

  • PolyLine: curves consisting of a concatenation of straight segments.

  • Arc: circles and arcs

Another important curve type, NurbsCurve, is defined in the plugins.nurbs module.

17.1. Classes defined in module curve

class curve.Curve[source]

Base class for curve type classes.

This is a virtual class that can not be instantiated. It defines the common definitions for all curve type subclasses. The subclasses define at least the following attributes:

coords

The coordinates of all the points needed to define the curve. These are not necessarily points on the curve.

Type

Coords

nparts

The number of parts comprising the curve. Subsequent parts of a curve (e.g. straight segments of a polyline), have a local parameter with values in the range 0..1. Any point on the curve can thus be identified by a float number, where the integer part is the curve number and the fractional part is the parameter value. See localParam().

Type

int

closed

Whether the curve is closed or not. A closed curve has coinciding begin and end points, making it a closed loop. An open curve with coinciding end points may look like a closed curve, but technically it is still open, and its behavior may be different from that of a closed curve.

Type

bool

class curve.BezierSpline(coords=None, *, control=None, degree=3, closed=False, tol=None, tangents=None, curl=0.3333333333333333, **kargs)[source]

A class representing a Bezier spline curve of degree 1, 2, 3 or higher.

A BezierSpline of degree d is a continuous curve consisting of nparts successive Bezier curves of the same degree. Successive means that the end point of one curve is also the starting point of the next curve.

A Bezier curve of degree d is determined by d+1 control points, of which the first and the last are on the curve (the endpoints), and the intermediate d-1 points are not. Since the end point of one part is the starting point of the next part, a BezierSpline is described by ncontrol = d * nparts + 1 control points. The number of points on the curve is npoints = nparts + 1.

The above holds for open curves. The BezierSpline class however distinguishes between open and closed curves. In a closed curve, the last point coincides with the first, and is not stored. If the last point is needed, it is obtained by wrapping around back to the first point. An open curve however may also have a last point coinciding with the first, so that the curve looks closed, but technically it remains an open curve.

The BezierSpline class provides ways to create a full set of control points. Often the off-curve control points can be generated automatically. The default degree (3) generates them from the requirement that the curve should be smooth, i.e. have a continuous tangent vector when moving from one part to the next.

Therefore there are basically two ways to create a BezierSpline: by specifying all control points, or by specifying only the points through which the curve should pass (and possibly the tangents at those points). The difference is made clear by using either the control or the coords parameter. Because in most quick application cases the use of the coords option is the likely choice, that parameter is made the (only) positional one. The coords and control parameters are mutually exclusive, but at least one of them must be used. Historically, there was an option to use both coords and control parameters, with the latter then only specifying the off-curve points, but this use case is now prohibited. See Notes for how to work around it.

Parameters
  • coords (coords_like (npoints, 3), optional) – A set of points through which the curve should pass. The number of points should at least be 2. An interpolation BezierSpline of the requested degree is then constructed. The result can be tuned by other parameters (tangents, curl). This is the only parameter that is allowed as a positional parameter It can not be combined with the control parameter. It can also not be used for degrees > 3 (which are seldomly used).

  • control (coords_like (ntrl, 3)) – The complete set of control points that define the BezierSpline. The number of points (nctrl) should be a multiple of degree, plus 1. This parameter can not be used if coords was provided.

  • degree (int) – The polynomial degree of the curve. The default value is 3. For curves of degree 1, the specialized PolyLine subclass may be more appropriate. For curves of degree 4 or higher the use of a NurbsCurve may be prefered.

  • closed (bool) – If True, a closed curve is created. If the distance from the last point to the first is less than tol, the last point is removed and the last segment is created between the penultimate point and the first point. Else, a segment is created between the last point and the first.

  • tol (float, optional) – Tolerance used in testing for end point coincidence when creating a closed curve. Only used if closed=True. If the distance between the end points is less than tol, they are considered coincident. Else, an extra curve segment is created between the last and the first point. A value 0.0 will force the creation of a extra segment. A value np.inf will never create an extra segment. If not provided, a default value 1.e-5 * coords.dsize() is used. This value ensures expected behavior in most use cases.

  • tangents (coords_like (npts, 3) or (2, 3), optional) – The imposed tangent vectors to the curve at the specified points coords. Can only be used with degree 2 or 3. The number of tangents should be equal to the number of points (after possibly removing one point for a closed curve). As a convenience, the tangents array may contain just two vectors if only the end tangents need to be specified. The provided vectors will get normalized, so their length is irrelevant. If not provided, tangents is set equal to PolyLine(coords).avgDirections(). The tangents array can contain np.nan values for points were the user does not want to prescribe a tangent direction. These will also be filled in with values from PolyLine(coords).avgDirections()

  • curl (float, optional) – The curl parameter can be set to influence the curliness of the curve between two subsequent curve endpoints. A value curl=0.0 results in straight segments. The higher the value, the more the curve becomes curled. Can only be used with degree 3.

  • kargs (deprecated keyword arguments) – For compatibility of legacy code, the ‘tangents’ parameter may also be called ‘deriv’.

Returns

BezierSpline – A BezierSpline of the requested degree, passing through all points coords. If the degree is 3, or the degree is 2 and all points are lying in the same plane, the curve will have the provided (or computed) tangents at the given points. If the degree is 2 and the points are not coplanar, the curve may have discontinuities in the tangent at the points.

See also

PolyLine

a BezierSpline of degree 1 with more specialized methods

NurbsCurve

Industry standard curves with a finer control of smoothness.

Notes

The combined use of coords and control is no longer permitted. For such cases one can use the arraytools.interleave function to combine them into a single full set of control points. Thus, instead of:

BezierSpline(coords=Q, control=R, degree=2)
BezierSpline(coords=Q, control=R, degree=3)

use:

BezierSpline(control=at.interleave(Q, R[:,0]), degree=2)
BezierSpline(control=at.interleave(Q, R[:,0], R[:,1]), degree=3)

Examples

>>> B = BezierSpline('0123')
>>> print(B)
BezierSpline: degree=3, nparts=3, ncoords=10, open
  Control points:
[[ 0.    0.    0.  ]
 [ 0.32 -0.11  0.  ]
 [ 0.76 -0.24  0.  ]
 [ 1.    0.    0.  ]
 [ 1.24  0.24  0.  ]
 [ 1.24  0.76  0.  ]
 [ 1.    1.    0.  ]
 [ 0.76  1.24  0.  ]
 [ 0.32  1.11  0.  ]
 [ 0.    1.    0.  ]]
>>> print(B.pointsOn())
[[0. 0. 0.]
 [1. 0. 0.]
 [1. 1. 0.]
 [0. 1. 0.]]
>>> print(B.pointsOff())
[[[ 0.32 -0.11  0.  ]
  [ 0.76 -0.24  0.  ]]

 [[ 1.24  0.24  0.  ]
  [ 1.24  0.76  0.  ]]

 [[ 0.76  1.24  0.  ]
  [ 0.32  1.11  0.  ]]]
>>> print(B.points(1))
[[1.   0.   0.  ]
 [1.24 0.24 0.  ]
 [1.24 0.76 0.  ]
 [1.   1.   0.  ]]
>>> B = BezierSpline('0123', degree=1)
>>> print(B)
BezierSpline: degree=1, nparts=3, ncoords=4, open
  Control points:
[[0. 0. 0.]
 [1. 0. 0.]
 [1. 1. 0.]
 [0. 1. 0.]]
>>> B = BezierSpline('0123', degree=1, closed=True)
>>> print(B.ctype)
BezierSpline: degree=1, nparts=4, ncoords=4, closed
class curve.PolyLine(control=None, *, closed=False, tol=None, **kargs)[source]

A Curve consisting of a sequence of straight line segments.

This is implemented as a BezierSpline of degree 1, and in many cases one can just use that super class. The PolyLine class however provides a lot of extra functionality, which is only available for curves of degree 1.

Parameters
  • coords (coords_like) – An (npts,3) shaped array with the coordinates of the subsequent vertices of the PolyLine, or any other data accepted by the Coords initialization. Each vertex is connected to the next to form a PolyLine segment. For compatibility with the BezierSpline class, this argument can also be named control, and takes precedent if both are used.

  • closed (bool, optional) – If True, the PolyLine is closed by connecting the last vertex back to the first. The closed PolyLine thus has npts segments. The default (False) leaves the curve open with npts-1 segments.

  • control (optional) – This is an alias for the coords parameter. It exists only for symmetry with the BezierSpline class. If specified, control overrides the value of coords.

Examples

>>> P = PolyLine('0123')
>>> print(P)
PolyLine: nparts=3, ncoords=4, open
  Control points:
[[0. 0. 0.]
 [1. 0. 0.]
 [1. 1. 0.]
 [0. 1. 0.]]
>>> P.nparts
3
>>> P = PolyLine(P.coords, closed=True).nparts
>>> print(P)
4
class curve.Line(coords)[source]

A Line is a straight segment between two points.

Line is implemented as a :class:PolyLine with exactly two points.

Parameters

coords (array_like (2,3)) – The coordinates of begin and end point of the line.

Examples

>>> L = Line([[0.,0.,0.], [0.,1.,0.]])
>>> print(L)
Line: nparts=1, ncoords=2, open
  Control points:
[[0. 0. 0.]
 [0. 1. 0.]]
class curve.Contour(coords, elems)[source]

A class for storing a contour.

The Contour class stores a continuous (often closed, 2D) curve which consists of a sequence of strokes, each stroke being a Bezier curve. Generally the degree of the Bezier curves is in the range 1..3, but higher degrees are accepted. Each stroke is thus defined by 2, 3 or 4 (or more) points. Each strokes starts at the endpoint of the previous stroke. If the last point of the last stroke and the first point of the first stroke are the same point, the Contour is closed.

The Contour is defined by a list of points and a Varray of element connectivity. This format is well suited to store contours of scalable fonts (the contours are the normally 2D curves).

Parameters
  • coords (coords_like (npoints, 3)) – The coordinates of all points used in the definitions of the strokes.

  • elems (varray_like) – A Varray defining the strokes as a list of point indices. Each row should start with the same point that ended the previous stroke.

Examples

>>> X = Coords('0123')
>>> C = Contour(X, [(0,1), (1,2,3,0)])
>>> print(C)
Contour: nparts=2, ncoords=4, closed
Control points:
[[0. 0. 0.]
 [1. 0. 0.]
 [1. 1. 0.]
 [0. 1. 0.]]
Strokes: Varray (2, (2, 4))
  [0 1]
  [1 2 3 0]
>>> print(C.elems)
Varray (2, (2, 4))
  [0 1]
  [1 2 3 0]
>>> print(C.nparts)
2
>>> print(C.stroke(1))
BezierSpline: degree=3, nparts=1, ncoords=4, open
  Control points:
[[1. 0. 0.]
 [1. 1. 0.]
 [0. 1. 0.]
 [0. 0. 0.]]
>>> print(C.parts(1,2))
Contour: nparts=1, ncoords=4, open
  Control points:
[[0. 0. 0.]
 [1. 0. 0.]
 [1. 1. 0.]
 [0. 1. 0.]]
  Strokes: Varray (1, (4, 4))
  [1 2 3 0]
class curve.NaturalSpline(coords, closed=False, endzerocurv=False)[source]

A class representing a natural spline.

The use of this class is deprecated. For a closed curve or an open curve with endzerocurv=True, BezierSpline gives a good approximation. For an open curve with endzerocurv=False, a NurbsCurve obtained with nurbs.globalInterpolationCurve will do fine.

class curve.Arc(*, p3=None, cbe=None, center=(0.0, 0.0, 0.0), radius=1.0, normal=(0.0, 0.0, 1.0), angles=(0.0, 360.0), angle_spec=0.017453292519943295)[source]

A class representing a circular arc.

The arc can be specified in one of three ways:

  • by specifying 3 points on the arc: the begin point, an intermediate point and the end point. Use the p3 argument to use this method.

  • by specifying the center and the begin and end points: use the cbe argument.

  • by the general method specifying the center, radius, normal, and begin and end angles of the arc.

Parameters
  • p3 (coords_like (3,3)) – The coordinates of three subsequent points on the arc: the begin point, an intermediate point and the end point. The three points should obviously not be colinear, with one exception: if the begin and end points coincide, a full circle is created, and the normal argument is used to help with orienting the circle’s plane. If p3 is provided, all other arguments but normal are disregarded.

  • cbe (coords_like (3,3)) – The coordinates of the center and the begin and end points of the arc. The three points should not be colinear. Even then, the problem has always two solutions, depending on the choice of the positive normal on the plane of the three points. Those two solutions form a full circle. The chosen solution is the one that corresponds with a positive normal pointing to the same side of the plane as the specified normal vector. If cbe method is used (and no p3 is provided), all other arguments but normal are disregarded.

  • center (coords_like (3,)) – The center point of the arc.

  • radius (float) – The radius of the arc.

  • normal (coords_like (3,)) – The normal on the plane of the arc. The arc is constructed in the x,y plane and then the z-axis is rotated to the specified normal direction.

  • angles ((float, float)) – The start and end angles of the arc, by default in degrees.

  • angle_spec (float) – A multiplier that turns the angles into radians. The default expects the angles to be degrees.

Examples

Three ways to construct a full unit circle in the x,y plane:

>>> A = Arc(center=[0.,0.,0.], radius=1., angles=(0., 360.))
>>> B = Arc(cbe=[[0.,0.,0.], [1.,0.,0.], [1.,0.,0.]])
>>> C = Arc(p3=[[1.,0.,0.], [-1.,0.,0.], [1.,0.,0.]])
>>> print(A,B,C)
Arc
  Center [0. 0. 0.], Radius 1., Normal [0. 0. 1.]
  Angles=(0.0, 360.0)
  P0=[1. 0. 0.]; P1=[-1.  0.  0.]; P2=[ 1. -0.  0.]
Arc
  Center [0. 0. 0.], Radius 1., Normal [0. 0. 1.]
  Angles=(0.0, 360.0)
  P0=[1. 0. 0.]; P1=[-1.  0.  0.]; P2=[1. 0. 0.]
Arc
  Center [0. 0. 0.], Radius 1., Normal [0. 0. 1.]
  Angles=(0.0, 360.0)
  P0=[1. 0. 0.]; P1=[-1.  0.  0.]; P2=[1. 0. 0.]

Three ways to constructing the right half of that circle:

>>> A = Arc(center=[0.,0.,0.], radius=1., angles=(-90., 90.))
>>> B = Arc(cbe=[[0.,0.,0.], [0.,-1.,0.], [0.,1.,0.]])
>>> C = Arc(p3=[[0.,-1.,0.], [1.,0.,0.], [0.,1.,0.]])
>>> print(A,B,C)
Arc
  Center [0. 0. 0.], Radius 1., Normal [0. 0. 1.]
  Angles=(270.0, 450.0)
  P0=[-0. -1.  0.]; P1=[ 1. -0.  0.]; P2=[0. 1. 0.]
Arc
  Center [0. 0. 0.], Radius 1., Normal [0. 0. 1.]
  Angles=(270.0, 450.0)
  P0=[ 0. -1.  0.]; P1=[ 1. -0.  0.]; P2=[0. 1. 0.]
Arc
  Center [0. 0. 0.], Radius 1., Normal [0. 0. 1.]
  Angles=(270.0, 450.0)
  P0=[ 0. -1.  0.]; P1=[1. 0. 0.]; P2=[0. 1. 0.]

17.2. Functions defined in module curve

curve.circle()[source]

Create a BezierSpline approximation of a circle.

Returns

BezierSpline – A closed BezierSpline through 8 points lying on a circle x,y plane, with its center at (0,0,0) and having a radius 1.

Notes

The result can easily be scaled, translated or rotated to create other circles. It is adequate for drawing circles, though it doesn’t exactly represent a circle.

See also

Arc

create exact representation of circles and arcs.

curve.arc2points(x0, x1, R, pos='-')[source]

Create an arc between two points

Given two points x0 and x1, this constructs an arc with radius R through these points. The two points should have the same z-value. The arc will be in a plane parallel with the x-y plane and wind positively around the z-axis when moving along the arc from x0 to x1.

If pos == ‘-‘, the center of the arc will be at the left when going along the chord from x0 to x1, creating an arc smaller than a half-circle. If pos == ‘+’, the center of the arc will be at the right when going along the chord from x0 to x1, creating an arc larger than a half-circle.

If R is too small, an exception is raised.

curve.deCasteljau(P, u)[source]

Compute points on a Bezier curve using deCasteljau algorithm

Parameters:

P is an array with n+1 points defining a Bezier curve of degree n. u is a single parameter value between 0 and 1.

Returns:

A list with point sets obtained in the subsequent deCasteljau approximations. The first one is the set of control points, the last one is the point on the Bezier curve.

This function works with Coords as well as Coords4 points.

curve.splitBezier(P, u)[source]

Split a Bezier curve at parametric values

Parameters:

P is an array with n+1 points defining a Bezier curve of degree n. u is a single parameter value between 0 and 1.

Returns two arrays of n+1 points, defining the Bezier curves of degree n obtained by splitting the input curve at parametric value u. These results can be used with the control argument of BezierSpline to create the corresponding curve.

This works for u < 0 and u > 1, to extend the curve. If u < 0, the left part is returned reverse, if u