Changelogs » Bezier



[![PyPI link to release 0.9.0](]( [![Documentation for release 0.9.0](](


-   Documenting the C ABI `libbezier` ([4608364]( Fixed [\63]( This [documentation]( contains a page for each "module" which corresponds to the underlying Fortran module. Each module documents the routines in the corresponding header file, e.g. the [surface]( document corresponds to the `bezier/surface.h` header. Fully working C examples have been added for each routine in `bezier/curve.h` and for the enum in `bezier/status.h`.
-   Adding section about environment variables to [development]( document ([5186e24]( Fixed [\78](
-   Remove dependency on `` ([04d0f8d]( The website is being turned down. Fixed [\130](
-   Renaming the "Native Libraries" document as "Binary Extension" ([f99db20]( In the process, changed most references to the "native" Python extension to instead call it a "binary" extension.
-   Added a "Cython `.pxd` [Declarations](" document ([f99db20]( Fixed [\122](
-   Moving all Python specific documentation under a specific URL path ([3db483b]( In particular, moving
-   `/reference/...` to `/python/reference/...`
-   `/python-binary-extension.html` to `/python/binary-extension.html`
-   `/pxd/...` to `/python/pxd/...`.
-   Moving all algorithm specific documentation under a specific URL path ([6e9c825]( In particular, moving
-   `/algorithm-helpers.html` to `/algorithms/helpers.html`
-   `/curve-curve-intersection.html` to `/algorithms/curve-curve-intersection.html`

ABI Changes

New Features

-   Added `surface.h::compute_area` helper that can be used to compute the area of both a surface and a curved polygon ([d4d7249](

Breaking Changes

-   Removing getters and setters for parameters used during curve-curve intersection ([2fda3ae](
-   `curve_intersection.h::set_max_candidates`
-   `curve_intersection.h::get_max_candidates`
-   Removing `dimension` from `curve.h::get_curvature` ([1e39c0c](

Python Changes

New Features

-   Added implementation for `Surface.area` [property]( and `CurvedPolygon.area` [property]( ([eb6077e](

Non-Public API

-   Removing getters and setters for parameters used during curve-curve intersection ([2fda3ae](
- `bezier._geometric_intersection.set_max_candidates()`
- `bezier._geometric_intersection.get_max_candidates()`
-   Removing cached values for `Curve.length` [property](, `Surface.area` [property]( and `Surface.is_valid` [property]( ([34d48d6](


-   Renaming `libbezier.dll` shared library to `bezier.dll` on Windows ([d17a9bc]( This follows the correct convention on Windows.
-   Adding Python 3.7 support and making it the default version used for testing ([e368e9f](
-   Dropping support for Python 3.5 ([f99db20](
-   Adding back `-march=native` for non-wheel builds ([1566019]( This way, when installing from source (either via a local checkout or from the source distribution on PyPI) the most optimal machine instructions will be produced. Fixed [\99](
-   Removing all traces of 32-bit support for OS X ([d7620ad]( This was driven by a [decision]( from the NumPy maintainers.


-   Adopted `black` [code formatter]( ([f21b52d](
-   Adding project URLs and keywords for PyPI ([cfb070d](
-   Added 20 new surface-surface functional tests ([9fd9c1e]( See [\121]( for more information.
-   Removed time and memory benchmarks due to flakiness and lack of an environment that could be used for benchmarking ([6a30dc2]( See [\125]( to follow discussion on re-enabling such benchmarks.
-   Using `DEBUG=True` environment variable when running unit tests and other related tests ([d84dffb](


[![PyPI link to release 0.8.0](]( [![Documentation for release 0.8.0](](

New Features

-   Adding support for surface-surface intersections that have coincident segments shared between each surface ([cfa2b93](, [0a9645c]( See cases:
-   4: [10Q-18Q](
-   5: [10Q-19Q](
-   43: [29Q-42Q](
-   44: [29Q-43Q](
-   45: [10Q-44Q](
-   46: [1Q-45Q](
-   47: [1Q-2C](
-   Adding support for curve-curve intersections that are also points of tangency. This was accomplished by five broad changes to the geometric intersection algorithm:
-   Checking if almost-linear curves have disjoint bounding boxes **before** intersecting the linearized segments ([05f0343](
-   Adding a "full" Newton iteration for finding `B1(s) = B2(t)` when known to be near a solution. In particular, this has **special** handling for tangencies, which cause a singular Jacobian and make convergence drop from quadratic to linear and stalls out convergence early ([13a5be5](, [4bac61a](
-   Changing how "bad" linearized segments are handled. After subdividing to approximately linear curve segments, there were two problems which are now being handled in the same way. If the line segments connecting the subdivided curve endpoints

-   are parallel, then the algorithm failed with a `PARALLEL` status
-   intersect outside of the unit interval (for either `s` or `t`), the curve-curve candidate was rejected (a small amount, `0.5^{16}`, of "wiggle" room was allowed outside of `[0, 1]`).

Now both cases are handled in the same way. First, the subdivided curve segments will have a convex hull check applied (which is more strict than a bounding box check). If their convex hulls do collide, they are treated as a normal intersection of curved segments ([4457f64](, [fe453c3](
-   Using the newly added "full" Newton's iteration for all intersections. Before, a single Newton step was applied after intersection the linearized segments ([d06430f](
-   Changing how a candidate pair of `s-t` parameters is added. ([c998445]( In the previous implementation, a pair was considered a duplicate only if there was a difference of at most 1 [ULP]( from an existing intersection (though this could be toggled via `set_similar_ulps()`). Now, the pair is "normalized" so that `s` and `t` are away from `0`. For example, if `s < 2^{-10}` then we use `1 - s` instead. (This is perfectly "appropriate" since evaluating a B&xe9;zier curve requires using both `s` and `1 - s`, so both values are equally relevant.) Once normalized, a relative error threshold is used.
-   Four curve-curve functional test cases have gone from failing to passing:
-   11: [14-15](
-   31: [38-39](
-   43: [58-59](
-   44: [60-59](

and two surface-surface cases have as well:

-   10: [20Q-21Q](
-   42: [41Q-21Q](

In order to support the aforementioned surface-surface cases, special support for "tangent corners" was added ([12b0de4](

ABI Changes

Breaking Changes

-   Removed `BAD_TANGENT` status enum ([b89b2b1]( The case where that failure was issued has now been handled as an acceptable `TANGENT_BOTH` classification for surface-surface intersection points. (See the `classify_intersection()` [function]( for an example.)
-   Adding `BAD_INTERIOR` status enum ([6348dc6]( (This is a **breaking** change rather than additive because it re-uses the enum value of `5` previously used by `BAD_TANGENT`.) This value is used by `interior_combine()` in the case that the curved polygon intersection(s) cannot be determined from the edge-edge intersections for a given surface-surface pair. See [101](
-   Removed `PARALLEL` status enum ([fe453c3]( Now when doing geometric curve-curve intersection, parallel linearized segments are handled by checking if the convex hulls collide and then (if they do) using a modifed Newton iteration to converge to a root.
-   Adding `BAD_MULTIPLICITY` status enum ([fe453c3]( (This is a **breaking** change rather than additive because it re-uses the enum value of `1` previously used by `PARALLEL`.) This is used when Newton's method fails to converge to either a simple intersection or a tangent intersection. Such failures to converge, when already starting near an intersection, may be caused by one of:
-   The intersection was of multiplicity greater than 2
-   The curves don't actually intersect, though they come very close
-   Numerical issues caused the iteration to leave the region of convergence
-   Removed `ulps_away()` ([c998445](
-   Removed `set_similar_ulps()` and `get_similar_ulps()` ([c998445](

Surface Changes

-   Added `SINGULAR` status enum for cases when a linear system can't be solved due to a singular matrix ([4457f64](
-   Adding `status` as a return value in `newton_refine_curve_intersect()`. This way, when the Jacobian is singular (which happens at points of tangency), the `SINGULAR` status can be returned ([4457f64]( The old behavior would've resulted in a division by zero.

Non-Public API

-   Adding custom linear solver for the `2 x 2` case ([a3fb476]( This is modelled after `dgesv` from LAPACK.

Python Changes

-   (**Bug fix**) The `0.7.0` release broke `Surface.plot()` and `CurvedPolygon.plot()` (when the nodes were transposed, the plotting helpers were not correctly updated). The `add_patch()` helper was fixed to account for the changes in data layout ([80bfaaa](
-   Added custom `UnsupportedDegree` [exception]( to be used by routines that have implementations that are hard-coded for specific degrees ([87a1f21]( See [103](
-   Removed `ulps_away()` ([c998445](
-   Removed `set_similar_ulps()` and `get_similar_ulps()` ([c998445](

Non-Public API

-   Returning `coincident` flag from curve-curve `all_intersections` ([ebe6617](
-   Adding a `TANGENT_BOTH` classification for surface-surface intersection points that are interior to both surfaces at the point of tangency ([b89b2b1]( This previously failed with a `NotImplementedError`.
-   Added `COINCIDENT` classification for surface-surface intersection points that occur on a segment that is coincident on an edges of each surface ([8b1c59d]( Such points previously failed classification because they were interpreted as being tangent and having the same curvature (because the segments are identical).
-   Added a `COINCIDENT_UNUSED` classification ([cfa2b93]( for cases where coincident segments are moving in opposite directions (i.e. the surfaces don't share a common interior). For example see case 44 ([29Q-43Q](
-   Adding custom linear solver for the `2 x 2` case ([764e56d]( This is modelled after `dgesv` from LAPACK.
-   Adding some support for B&xe9;zier clipping algorithm ([fbed62d](, [ada4ea3]( See the original [paper]( by Sederberg and Nishita for more information.


[![PyPI link to release 0.7.0](]( [![Documentation for release 0.7.0](](


-   Geometric curve-curve intersection has better handling for cases when the number of intersection candidates grows large (`MAX_CANDIDATES == 64`):
-   First tries to reduce the number of candidates by checking if the **actual** convex hulls of each segment in a candidate pair intersect. This is a much "finer" check than using the "blunt" bounding box check.
-   If the convex hull refinement fails, checks if the curves are coincident, i.e. different segments / parameterizations along the same algebraic curve. This is done by using the `Curve.locate()` [function]( to try to project each of the four endpoints onto the other curve and then re-parameterizing each curve onto a common interval.

Data Structures

-   Storing `xy`-points as columns (rather than rows). This was a **very** large and breaking change, started in [b44af8c]( See [51]( for more information.

Python Changes

Non-Public API

-   Requiring **contiguous** 1D arrays for Cython functions ([9ede37d](










































































































































































































[![PyPI link to release 0.6.4](]( [![Documentation for release 0.6.4](](

Python Changes

Surface Changes

-   Stopped raising `ValueError('At least one value outside of unit interval', s_val, t_val)` or `ValueError('outside of unit interval')` when a curve-curve intersection falls **barely** outside of the parameter space `[0, 1]` ([329a59a](

ABI Changes

Surface Changes

-   Removed `Status_WIGGLE_FAIL` enum and re-numbered all larger `Status` enum values (by subtracting one)
-   Changing "wiggle failure" in curve-curve intersection from a non-success status to be a dropped candidate for intersection




[![PyPI link to release 0.6.3](]( [![Documentation for release 0.6.3](](

Python Changes

Surface Changes

-   Changed `RuntimeError('Unexpected number of edges', 11)` to `RuntimeError('Unknown error has occured.')` in the speedup that does surface-surface intersection ([35ab5d5]( The old error message was a "copy-paste" artifact from the `basic_interior_combine()` Python helper.


-   Removed a flag (`-march=native`) from the build process for the `bezier._speedup` extension module ([e739429]( Using the flag caused the `manylinux` wheels to be "broken" (see [98](




[![PyPI link to release 0.6.2](]( [![Documentation for release 0.6.2](](

Python Changes


-   Converted `` file into a [releases]( docs page ([0027cd7](

Non-Public API

-   Collapsed all Cython-generated modules into a single `bezier._speedup` module ([8bcb319](
-   This is the change that **prompted the release**.
-   Dropped the five `bezier._HAS_*_SPEEDUP` members for a single `bezier._HAS_SPEEDUP` (this was the previous approach before `0.6.0`).
-   Renamed a few of the Cython helper functions to avoid name collision.
-   This was done to fix a bug and prevent future bugs. The issue was that a mutable Fortran global (`MAX_CANDIDATES`) was being included via an object file in **separate** extension modules. When one module updated the global, the other module never saw the update (because it was a different copy).




Python Changes


- Noting that `Surface.intersect()` can return a list of either `CurvedPolygon` or `Surface` instances ([`16e77d7`][0.6.1-5]).

Breaking Changes

- Removing `IntersectionClassification` enum from `_status.pxd` ([`4da969e`][0.6.1-4]).

Non-Public API

- Adding getters and setters for parameters used during curve-curve intersection ([`ef4ebc0`][0.6.1-7]):
- `bezier._geometric_intersection.set_max_candidates()`
- `bezier._geometric_intersection.get_max_candidates()`
- `bezier._geometric_intersection.set_similar_ulps()`
- `bezier._geometric_intersection.get_similar_ulps()`

ABI Changes

Surface Changes

- Switching from `int` to an actual enum for relevant functions with output values that are enums:
- In `surface_intersection.h::surface_intersections`, `contained` is now a `SurfaceContained` ([`0a9c0c3`][0.6.1-3]) and `status` is now a `Status` ([`c356c32`][0.6.1-2])
- In `curve_intersection.h::bbox_intersect`, `enum_` is now a `BoxIntersectionType` ([`ef856af`][0.6.1-1])
- In `curve_intersection.h::curve_intersections`, `status` is now a `Status` ([`ef856af`][0.6.1-1])
- Adding getters and setters for parameters used during curve-curve intersection ([`ef4ebc0`][0.6.1-7]):
- `curve_intersection.h::set_max_candidates`
- `curve_intersection.h::get_max_candidates`
- `curve_intersection.h::set_similar_ulps`
- `curve_intersection.h::get_similar_ulps`

Breaking Changes

- Removing inputs `curve_start / curve_end` and outputs `true_start / true_end` in `curve.h::specialize_curve` ([`959c547`][0.6.1-6])




Performance Optimizations

- Added recommended performance flags for `gfortran` based on [recommendations][0.6.0-3] on `` ([`3877982`][0.6.0-22]).
- Extensions can be compiled in debug mode by setting `DEBUG=True` ([`b62460b`][0.6.0-62]).
- Setting `BEZIER_NO_EXTENSIONS=True` will build pure-Python modules only ([`3f6280c`][0.6.0-25])
- Added [QUADPACK][0.6.0-86] to use in `curve.f90::compute_length` ([`985a4c0`][0.6.0-53]).
- Implemented curve-curve intersection completely in Fortran (e.g. [`4a8f801`][0.6.0-28]) which resulted in a 10x speedup when called from Python. Also implemented surface-surface intersection completely in Fortran, resulting in a 3x speedup.

Python Changes

New Features

- Added `CurvedPolygon._metadata` to track where edges originated, e.g. from a surface-surface intersection ([`871d23d`][0.6.0-45]). This is used for sanity checking in functional tests ([`e253da2`][0.6.0-78]).
- Made speedup checks specific to the module, not all four. I.e. `bezier._HAS_SPEEDUP` was dropped in favor of five members, e.g. `_HAS_CURVE_SPEEDUP` ([`d798f66`][0.6.0-73]).
- Added `bezier.__author__` and [`bezier.__version__`][0.6.0-87] attributes.
- Added [`bezier.get_dll()`][0.6.0-88] for Windows ([`699e39b`][0.6.0-34]).
- Added `bezier/` that adds `libbezier` to `%PATH%` on Windows ([`8538af4`][0.6.0-43]).
- Fortran / Cython speedups added:
- `_curve_speedup.pyx::subdivide_nodes`
- `_curve_speedup.pyx::newton_refine`
- `_curve_speedup.pyx::locate_point`
- `_curve_speedup.pyx::elevate_nodes`
- `_curve_speedup.pyx::get_curvature`
- `_curve_speedup.pyx::reduce_pseudo_inverse`
- `_curve_speedup.pyx::full_reduce`
- `_curve_speedup.pyx::compute_length`
- `_curve_intersection_speedup.pyx::all_intersections`
- `_curve_intersection_speedup.pyx::free_curve_intersections_workspace`
- `_helpers_speedup.pyx::contains_nd`
- `_helpers_speedup.pyx::vector_close`
- `_helpers_speedup.pyx::in_interval`
- `_helpers_speedup.pyx::ulps_away`
- `_surface_speedup.pyx::specialize_surface`
- `_surface_speedup.pyx::subdivide_nodes`
- `_surface_speedup.pyx::compute_edge_nodes`
- `_surface_intersection_speedup.pyx::newton_refine`
- `_surface_intersection_speedup.pyx::locate_point`
- `_surface_intersection_speedup.pyx::surface_intersections`
- `_surface_intersection_speedup.pyx::free_surface_intersections_workspace`

Breaking Changes

- [`Curve.intersect()`][0.6.0-89] returns `s-t` parameters rather than `x-y` values ([`c309998`][0.6.0-68]).
- [`Surface.intersect()`][0.6.0-90] returns a list with a single `Surface` when one of the two surfaces is contained in the other ([`05b1fd9`][0.6.0-6]).
- [`Surface.is_valid`][0.6.0-91] will only return `True` if the map `B(s, t)` determined by the surface has everywhere positive Jacobian. Previously a negative Jacobian was also allowed ([`260fb51`][0.6.0-1]).
- Removed data members from `Curve`:
- `edge_index` ([`b969488`][0.6.0-63])
- `next_edge` ([`28619e8`][0.6.0-16])
- `previous_edge` ([`28619e8`][0.6.0-16])
- `root` ([`db427f9`][0.6.0-75])
- `start` ([`39ee98b`][0.6.0-23])
- `end` ([`39ee98b`][0.6.0-23])
- Removed data members from `Surface`:
- `base_x` ([`dea75e3`][0.6.0-2])
- `base_y` ([`dea75e3`][0.6.0-2])
- `width` ([`dea75e3`][0.6.0-2])
- Remove `dimension` argument in `_curve_speedup.pyx::elevate_nodes` since it can be inferred from `nodes` ([`06501c5`][0.6.0-7]).

ABI Changes

New Features

- Fully implemented curve-curve intersection (as `curve_intersection.h::curve_intersections`) and surface-surface intersection (as `surface_intersection.h::surface_intersections`) at the ABI level.
- Added the `surface_intersection.h` header file and implementations for the described functions ([`fafd9ff`][0.6.0-84]).
- Newly added functions
- `curve.h::subdivide_nodes_curve` ([`efb3ce6`][0.6.0-82])
- `curve.h::newton_refine_curve` ([`2257344`][0.6.0-15])
- `curve.h::locate_point_curve` ([`2121101`][0.6.0-14], [`32b0fa9`][0.6.0-20])
- `curve.h::elevate_nodes_curve` ([`b03fc28`][0.6.0-60])
- `curve.h::get_curvature` ([`69cb2f8`][0.6.0-35])
- `curve.h::reduce_pseudo_inverse` ([`7c3db17`][0.6.0-39])
- `curve.h::full_reduce` ([`4abd309`][0.6.0-29])
- `curve.h::compute_length` ([`985a4c0`][0.6.0-53], [`7e71b20`][0.6.0-40])
- `curve_intersection.h::curve_intersections` ([`c92f98d`][0.6.0-96])
- `curve_intersection.h::free_curve_intersections_workspace` ([`c92f98d`][0.6.0-96])
- `helpers.h::contains_nd` ([`36f4b5e`][0.6.0-21])
- `helpers.h::vector_close` ([`9f3716a`][0.6.0-55])
- `helpers.h::in_interval` ([`3c0af5d`][0.6.0-24])
- `helpers.h::ulps_away` ([`0197237`][0.6.0-4])
- `surface.h::specialize_surface` ([`eb8693e`][0.6.0-81], [`fcd5bad`][0.6.0-85])
- `surface.h::subdivide_nodes_surface` ([`6027210`][0.6.0-32], [`4fc5f2a`][0.6.0-30], [`8beb1ac`][0.6.0-47], [`0b2b1f3`][0.6.0-8], [`d27b86f`][0.6.0-70], [`88c302b`][0.6.0-46])
- `surface.h::compute_edge_nodes` ([`2d02590`][0.6.0-17], [`f86649a`][0.6.0-83])
- `surface_intersection.h::newton_refine_surface` ([`93c288d`][0.6.0-50])
- `surface_intersection.h::locate_point_surface` ([`325ea47`][0.6.0-19], [`ca134e6`][0.6.0-69], [`bf69852`][0.6.0-65])
- `surface_intersection.h::surface_intersections`
- `surface_intersection.h::free_surface_intersections_workspace`
- Added [`status.h`][0.6.0-97] with an enum for failure states. Each Fortran procedure that returns a status documents the possible values and if each value is set directly or by a called procedure ([`9fc8575`][0.6.0-56], [`c2accf7`][0.6.0-67]).

Breaking Changes

- Removed functions
- `curve.h::specialize_curve_generic` ([`d52453b`][0.6.0-71])
- `curve.h::specialize_curve_quadratic` ([`d52453b`][0.6.0-71])
- `curve_intersection.h::from_linearized` ([`d62e462`][0.6.0-72])
- `curve_intersection.h::bbox_line_intersect` ([`72c0179`][0.6.0-37])
- `curve_intersection.h::linearization_error` ([`4a3378b`][0.6.0-27])
- `curve_intersection.h::segment_intersection` ([`4060590`][0.6.0-26])
- `curve_intersection.h::parallel_different` ([`df3e195`][0.6.0-76])
- Renamed functions
- `curve.h::newton_refine` to `newton_refine_curve` ([`194ce95`][0.6.0-11])
- `curve.h::elevate_nodes` to `elevate_nodes_curve` ([`194ce95`][0.6.0-11])
- `curve_intersection.h::newton_refine_intersect` to `newton_refine_curve_intersect` ([`a055525`][0.6.0-57])
- Replaced `degree` with `num_nodes (== degree + 1)` in functions that operate on curves:
- `curve.h::evaluate_curve_barycentric` ([`13eacdd`][0.6.0-10])
- `curve.h::evaluate_multi` ([`962c288`][0.6.0-52])
- `curve.h::specialize_curve` ([`ac86233`][0.6.0-59])
- `curve.h::evaluate_hodograph` ([`9170855`][0.6.0-49])
- `curve_intersection.h::newton_refine_curve_intersect` ([`80ec491`][0.6.0-42])


- Added documentation for "native extensions" in `DEVELOPMENT` ([`2f9f2c4`][0.6.0-92]).
- Overhauled [`native-libraries` doc][0.6.0-95] with subsections for OS X and Windows ([`bfa75ee`][0.6.0-66], [`72005fb`][0.6.0-94], etc.).
- Added Fortran unit tests ([`758bdd1`][0.6.0-38], [`e8afba7`][0.6.0-79], [`3164365`][0.6.0-18], etc.).
- Began testing in Mac OS X on Travis ([`9ac5e8e`][0.6.0-54], [`85f7619`][0.6.0-44], etc.).
- Added a workaround (`include/bezier/_bool_patch.h`) for the missing support for `bool` in old MSVC versions that are required to work with Python 2.7 ([`5577178`][0.6.0-93]).


Performance Optimizations
-   Change `wiggle_interval` to return `success` bool instead of raising an exception. This allows the implicitization approach to use it without having to use exceptions for flow-control. (Fixes [22][5].)
-   Switching Fortran speedups from `f2py` to Cython (this is because `f2py` artificially limits the feature set of Fortran, i.e. user defined types)
-   Moving some more code to Fortran (e.g. `bbox_line_intersect()` [`3dcf640`][11])

New Features
-   Making Fortran features available outside of Python (see [Native Libraries][1])
-   C headers for each Fortran module (via [`bezier.get_include()`][2])
-   Cython `.pxd` declarations for all Fortran modules
-   `libbezier` static library (via [`bezier.get_lib()`][3])
-   Implementing [`bezier_roots()`][13] polynomial root solver for polynomials written in Bernstein basis. ([`0dd6369`][12])

-   Getting `bezier` [published][10] in the Journal of Open Source Science (JOSS). See [review][9]. ([`e6c4536`][7] and [`975ac6b`][8])
-   Updating error message for `locate()` methods and adding a note that `locate()` / `evaluate*()` are (essentially) inverses. H/T to pdknsk [36][4]
-   Using Fortran-contiguous arrays in `_check_non_simple()`. ([`b06c78e`][6])
-   Moving most of `Curve.subdivide()` and `Surface.subdivide()` logic into helpers. This is part of an effort to make all helpers take low-level data types rather than `Curve`s, `Surface`s, etc. ([`34515bd`][14] and [`1fc80e5`][15])
-   Split `speedup.f90` into submodules `curve.f90`, `surface.f90`, etc. ([`75349b7`][16], [`dfd6bba`][17], [`7096a9d`][18], [`c326c00`][19])
-   Adding `BEZIER_JOURNAL` option to ``. This stores a record of compiler commands invoked during installation. See [Native Libraries][1] for more details. ([`3d832e7`][20] and [`c64a97a`][21])



Performance Optimizations
-   [Adding][29] Fortran [speedups][30] for many crucial computation helpers including
-   intersecting line segments
-   (vectorized) Horner's method for evaluating a B&xe9;zier curve at multiple parameters at once
-   (vectorized) Horner's method for evaluating a B&xe9;zier surface
-   computing "linearization error" (how close a curve is to a line)
-   specializing a B&xe9;zier curve to a sub-interval
-   using Newton's method to refine a curve-curve intersection
-   [Adding][10] `_verify` switch to [`Surface.locate()`][11] and [`Curve.intersect()`][14] to selectively disable overly defensive value checking. (Making sure to use this switch during "internal" computation.)
-   Making sure NumPy arrays are Fortran-contiguous as often as possible (e.g. snippets and source, via `np.asfortranarray()`). This is to avoid (and emphasize) a non-trivial overhead when passing a C-contiguous array to a Fortran function. ([`03a7242`][15], [`6064e4c`][16], [`f1804f4`][17])
-   Using Horner's method in `Curve.evaluate_multi()` and `Surface.evaluate_barycentric()`, rather than inferior (sometimes non-vectorized) approaches ([`dee8181`][18], [`2611e64`][19])
-   Made surface-surface intersection more resilient / lenient for corner intersections. For "nearby" intersections, parameter values can be rounded to `0` or `1`. ([`4a8458c`][25])

New Features
-   [Adding][23] optional `strategy` argument (one of geometric or algebraic) to [`Surface.intersect()`][24]
-   Added "algebraic" [`IntersectionStrategy`][20] via curve [implicitization][27] ([reference][28])
-   Adding [`Curve.reduce_()`][21] which acts as a partial inverse to [`Curve.elevate()`][31]. It is only a complete inverse when a curve is degree-elevated, otherwise it returns the "best" reduced form (in the least squares sense).

Interface Changes
-   (**Breaking change**) [Removing][5] `show` keyword from [`Curve.plot()`][2], [`Surface.plot()`][3] and [`CurvedPolygon.plot()`][4]
-   [Adding][32] `color` keyword to [`Curve.plot()`][2]
-   [Adding][26] `alpha` keyword to [`Curve.plot()`][2]
-   (**Breaking change**) [Splitting][6] the [`Surface.evaluate_multi()`][7] method into [`Surface.evaluate_barycentric_multi()`][8] and [`Surface.evaluate_cartesian_multi()`][9]
-   [Adding][22] `__dict__` helpers on `Curve`, `CurvedPolygon` and `Surface`. These are `property`s intended only for REPL use, since classes with `__slots__` no longer have a `__dict__` attribute.

-   Adding [`IntersectionClassification`][1] to docs ([ref][5])
-   [Moving][12] most plotting into a dedicated module. More importantly, importing plotting helpers at **run-time** rather at **import time**. So if computational code never plots, it won't eat the import cost of `matplotlib`. [Removing][13] `matplotlib` as a dependency.



Performance Optimizations
-   Adding `__slots__` for all classes
-   Removing all usage of `property` calls from internal callers (to avoid function call overhead)
-   Avoiding un-necessary data copying, e.g. `nodes[[0], :]` creates a copy but

nodes[0, :].reshape((1, 2))

does not ([more details](
-   Adding `_verify` switches to selectively disable overly defensive value checking. Added to [`CurvedPolygon`]( constructor, [`Surface.evaluate_barycentric()`](, [`Surface.evaluate_cartesian()`](, [`Surface.evaluate_multi()`]( and [`Surface.intersect()`]( Internal callers with already verified data now skip verification steps
-   [Bailing out early]( if surface bounding boxes are disjoint in [`Surface.intersect()`](

Breaking Changes
-   Requiring `degree` in [`Curve`]( and [`Surface`]( constructors, but adding [`Curve.from_nodes()`]( and [`Surface.from_nodes()`]( factories to accept nodes only (computing the degree in the constructor every time is a waste of flops, especially if the caller knows the degree)
-   [Removing]( public [`Curve.copy()`]( and [`Surface.copy()`](
-   [Removing]( custom equality checks for [`Curve`]( and [`Surface`]( objects. The previous implementation did not factor in all relevant values
-   Returning `1xD` arrays [instead of flattened]( `D`-dimensional 1D arrays from [`Curve.evaluate()`](, [`Surface.evaluate_barycentric()`](, [`Surface.evaluate_cartesian()`](, and related helpers
-   Renaming [`Intersection.left/right`]( as [`first/second`]( (They were poorly named originally, since "left" and "right" were in reference to where they were used **in code**, not geometry. This class is not part of the public interface, but it is documented.)

Bug Fixes
-   Handling cases where one corner of a surface touches another but their interiors don't intersect (in [`Surface.intersect()`]( Adding `ignored_corner` classification to handle these curve-curve intersecions that don't contribute to a surface-surface intersection
-   Throwing exception in [`Curve.locate()`]( when the subdivided intervals are very far apart (13)
-   Improving [`Surface.is_valid`]( by considering the signs of the Jacobian determinant at corner nodes (12)

-   Adding possible strategy to avoid linear convergence in [`newton_refine()`](
-   Adding AppVeyor configuration to make sure there are no Windows issues, testing exclusively with `conda` install
-   Updating generated images with `matplotlib` 2.0


- Added [`Curve.locate()`]( and [`_curve_helpers.newton_refine()`]( helper
- Adding optional `color` to [`Surface.plot()`](
- Adding [`Surface.elevate()`]( for degree elevation
- Fixing nodes defining the [self-intersecting curve]( in `curve-curve-intersection` (no code in `bezier` was broken / fixed, just "bad" docs)
- Allow wiggle outside of `[0, 1]` when intersecting linearizations in `from_linearized()`
- Collapsing almost-same parameter values in `intersect_one_round()` (via `from_linearized()`). Previously checked for bitwise equality and relied on checking values at the boundary of a subdivided interval
- Adding non-public `bezier._plot_helpers` module


- **Primary feature**: [`Surface.intersect()`]( added
- To support intersection, needed [`CurvedPolygon`](, i.e. an object defined only by its curved sides (whereas a `Surface` may have interior control points)
- [`Curve.specialize`]( for chopping a `Curve` at arbitrary parameter values (this is also used in surface-surface intersection)
- Added images to most documented functions and methods to illustrate the concept at hand. For example
[`classify_intersection`]( has **seven** images to enumerate all of the possible cases covered in the algorithm.
- Added [`Surface.locate()`](, made possible by [`newton_refine`](
- Added [Algorithm Helpers]( doc to try to explain some of the core algorithms at work (not all are documented yet). Some of this content was previously documented in the `bezier.curve` module, but was moved. Since, documentation has been added for `get_curvature`, `newton_refine` (for surfaces), `classify_intersection` (to determine how two curves interact while intersecting) and for some helper classes.
- Added `Surface.base_x`, `Surface.base_y` and `Surface.width` [properties]( to allow tracking a sub-surface during the subdivision process (this is an analogue to the `Curve.start` and `Curve.end` [properties](
- Added `Curve.edge_index`, `Curve.next_edge` and `Curve.previous_edge` [properties]( to allow tracking when curves are actually the sides of a `Surface`


- Adding [`Curve.elevate()`]( for degree elevation
- Upgrading curve-curve intersection algorithm to ignore parallel line segments that don't meet (rather than throwing `NotImplementedError`)
- Making [`segment_intersection()`]( return a `success` bool instead of raising `NotImplementedError` on failure
- Updating docs for [`newton_refine()`]( with two examples and making [`parallel_different()`]( a publicly documented function (as a partner to `segment_intersection()`)
- Adding some more examples / failures to `curve-curve-intersection` [doc](


Primary changes since previous release ([`0.0.1`]( are related to curve-curve intersection. See [the intersection docs]( for examples and more information.