Source/Objects/Curve

Curve Object
Curve data is a collection of `Spline` objects with the same attribute types and names. Most data and functionality is in splines, but this contains some helpers for working with them as a group.

As of December 2021 Curve code is going through a transition from the `Curve` data type to `CurveEval`. `CurveEval` simplifies code and supports generic attributes. Most operations on `CurveEval` are defined as geometry nodes.

A `CurveEval` corresponds to the `Curve` object data. The name is different for clarity, since more of the data is stored in the splines, but also just to be different than the name in DNA.

`Curve` cannot store generic attributes, but `CurveEval` can. Attributes on the control points of splines in `CurveEval` uphold a few invariants that can be checked with `assert_valid_point_attributes`:
 * The same set of attributes exists on every spline.
 * Attributes with the same name have the same type on every spline.
 * Attributes are in the same order on every spline.

Splines
A spline is an abstraction of a single branch-less curve section, its evaluation methods, and data. The spline data itself is just control points and a set of attributes by the set of "evaluated" data is often used instead. Conceptually, the derived vs. original data is an essential distinction. Derived data is usually calculated lazily and cached on the spline.

Any derived class of Spline has to manage two things:
 * 1) Interpolating arbitrary attribute data from the control points to evaluated points.
 * 2) Evaluating the positions based on the stored control point data.

Beyond that, everything is the base class's responsibility, with minor exceptions. Further evaluation happens in a layer on top of the evaluated points generated by the derived types.

There are a few methods to evaluate a spline:
 * 1) `evaluated_positions` and `interpolate_to_evaluated` give data for the initial evaluated points, depending on the resolution.
 * 2) `lookup_evaluated_factor` and `lookup_evaluated_factor` are meant for one-off lookups along the length of a curve.
 * 3) `sample_uniform_index_factors` returns an array that stores uniform-length samples along the spline which can be used to interpolate data from method 1.

Commonly used evaluated data is stored in caches on the spline itself so that operations on splines don't need to worry about taking ownership of evaluated data when they don't need to.

Bezier Spline
A Bézier spline (`BezierSpline`) is made up of a many curve segments, possibly achieving continuity of curvature by constraining the alignment of curve handles. Evaluation stores the positions and a map of factors and indices in a list of floats, which is then used to interpolate any other data.

NURB Splines
Data for Non-Uniform Rational B-Splines (`NURBSpline`). The mapping from control points to evaluated points is influenced by a vector of knots, weights for each point, and the order of the spline. Every mapping of data to evaluated points is handled the same way, but the positions are cached in the spline.

Poly Spline
A Poly spline (`PolySpline`) is like a Bézier spline with a resolution of one. The main reason to distinguish the two is for reduced complexity and increased performance, since interpolating data to control points does not change it.

Poly spline code is very simple, since it doesn't do anything that the base #Spline doesn't handle. So mostly it just worries about storing the data used by the base class.