Curves
Introduction
The engeom
library has both a 2D and 3D curve type. This type represents a 1-dimensional manifold that consists of
a sequence of vertices in \(\mathbb{R}^n\) space that are connected by line-segment edges, sometimes referred to as a
polyline.
Each curve entity consists of a single contiguous sequence of vertices with a clear start and end point. In the case of
the 2D Curve2
type, the curve can also be "closed", meaning that the end point is connected to the start point to
form a closed loop and so operations on the manifold that cross the end point will wrap around to the start point,
and vice versa.
While the fundamental operations on a curve, such as distance queries and manifold traversal are the same or similar for the 2D and 3D curve types, the 2D curve type has many more features that are made possible by the 2D plane.
2D Curves
The 2D curve type, Curve2
, is a more feature-rich type than the 3D curve type, because the nature of the 2D plane
means that a 2D polyline is more conceptually related to a 3D Mesh
object than it is to a 3D polyline. A Curve2
object can represent a boundary/surface with a clear sense of "inside" and "outside", and if it forms a closed loop
it can model a partitioning of the \(\mathbb{R}^2\) plane into an interior and exterior region.
In 2D, a curve has concept of a surface normal direction, which is built from the concept inside/outside defined through the winding order of the vertices. By convention, the segment from vertex \(i\) to vertex \(i+1\) defines the space to its "right" as being outside the curve, and the space to its "left" as being inside the curve, resulting in a counter-clockwise winding order defining a positive convex shape.
Creation
Creation of a Curve2
object is done by passing in a list of ordered vertices that define the curve. The vertices
will be interpreted in sequential order, so that each vertex will be connected to the next vertex in the list.
Adjacent vertices that are within a distance tolerance from each other will be de-duplicated, to prevent the curve from having zero-length segments. Additionally, if the first and last point are within the distance tolerance, the curve will be considered "closed" and algorithms which do manifold traversal will wrap between the first and last points.
However, because of the importance of winding order on 2D curves, the Curve2
type must also be constructed with the
vertices in the specific order that matches the intended definition of "inside" vs "outside" for the curve.
While the space occupied by the curve might be exactly the same whether the vertices are constructed in forward or reverse order, the inside vs outside will be opposite.
There are three ways that winding order can be specified during construction of a Curve2
object:
-
You can prepare the list of vertices so that they are in the correct order and then pass them into the constructor. Counter-clockwise order of vertices for positive convex shapes/regions, and clockwise order for negative convex shapes/regions.
-
If you know that the curve is meant to represent a positive (convex) shape overall, you can set the
hull_ccw=True
flag. The constructor will build the convex hull of the vertices you provide and reverse their order if the hull sequence does not match the input sequence. -
You may provide the constructor with an array of surface normals that correspond with the vertices. There must be one normal per vertex in the input list, and it must be pointing in the direction that you intend to be the "outside" of the surface. The constructor will reverse the input order if the majority of normals are not pointing in the same direction as the winding order would imply.
The input arguments for the constructor are:
Argument | Type | Description |
---|---|---|
vertices |
numpy.ndarray |
A 2D array of shape (n, 2) where n is the number of vertices in the curve. The columns are the x and y components of the vertices |
normals |
numpy.ndarray OPTIONAL |
A 2D array of shape (n, 2) where n is the number of vertices in the curve. The columns are the x and y components of the normals at each vertex. Default is None . |
tol |
float OPTIONAL |
A tolerance distance, below which points are considered to be the same and de-duplicated. Default is 1e-6 |
force_closed |
bool OPTIONAL |
If True , the curve will be guaranteed to be closed. If the first and last point are more than tol distance apart, an additional vertex will be added to the end that overlaps with the beginning. Default is False |
hull_ccw |
bool OPTIONAL |
If True , the vertices will be re-ordered to match the convex hull of the vertices. Default is False . This will be ignored if normals is not None |
import numpy
from engeom.geom2 import Curve2
# These are the corners of an open unit square
vertices = numpy.array([[0.0, 0], [1, 0], [1, 1], [0, 1]])
# Create a curve with these vertices and nothing else
c1 = Curve2(vertices)
print(c1) # <Curve2 n=4, l=3 (open)>
# Force the curve to be closed
c2 = Curve2(vertices, force_closed=True)
print(c2) # <Curve2 n=5, l=4 (closed)>
Stations
Because a Curve2
object is a 1D manifold, every unique position along the curve can be represented by a single scalar
value, which is the length from the start of the curve. Each unique position along a 2D curve has several geometric
properties which are useful. These properties are bundled in a CurveStation2
object, which is a lightweight data
object that represents a single position on the manifold.
CurveStation2
objects are not created directly, but are retrieved from a Curve2
object through one of several different possible queries on the manifold.
The CurveStation2
object has the following properties:
Property | Type | Description |
---|---|---|
.point |
Point2 |
The 2D position in space that corresponds with the station on the manifold. |
.direction |
Vector2 |
The vector pointing in the direction of positive distance along the curve. Typically this is the vector from the last vertex to the next vertex. |
.normal |
Vector2 |
The vector pointing in the direction of the surface normal at the station. This is the direction vector rotated by \(-90°\). |
.direction_point |
SurfacePoint2 |
A convenience surface point that combines the point position and direction vector. |
.surface_point |
SurfacePoint2 |
A convenience surface point that combines the point position and normal vector. |
.index |
int |
The index of the previous vertex on the curve, at or before the station. |
.length_along |
float |
The distance along the curve from the start to the station. This is the manifold domain. |