diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 540b7e2..80313f9 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -4,6 +4,7 @@ Changelog
 Version 3.2.0
 -------------
 
+- Fix ``neurom.morphmath.principal_direction_extent`` to calculate correctly the pca extent.
 - Fix ``neurom.features.neurite.segment_taper_rates`` to return signed taper rates.
 - Fix warning system so that it doesn't change the pre-existing warnings configuration
 - Fix ``neurom.features.bifurcation.partition_asymmetry`` Uylings variant to not throw
diff --git a/neurom/morphmath.py b/neurom/morphmath.py
index b01c516..41e090d 100644
--- a/neurom/morphmath.py
+++ b/neurom/morphmath.py
@@ -462,17 +462,17 @@ section_length = path_distance
 def principal_direction_extent(points):
     """Calculate the extent of a set of 3D points.
 
-    The extent is defined as the maximum distance between
-    the projections on the principal directions of the covariance matrix
-    of the points.
+    The extent is defined as the maximum distance between the projections on the principal
+    directions of the covariance matrix of the points.
 
-    Parameter:
-        points : a 2D numpy array of points
+    Args:
+        points : a 2D numpy array of points with 2 or 3 columns for (x, y, z)
 
     Returns:
         extents : the extents for each of the eigenvectors of the cov matrix
-        eigs : eigenvalues of the covariance matrix
-        eigv : respective eigenvectors of the covariance matrix
+
+    Note:
+        Direction extents are not ordered from largest to smallest.
     """
     # pca can be biased by duplicate points
     points = np.unique(points, axis=0)
@@ -483,14 +483,8 @@ def principal_direction_extent(points):
     # principal components
     _, eigv = pca(points)
 
-    extent = np.zeros(3)
-
-    for i in range(eigv.shape[1]):
-        # orthogonal projection onto the direction of the v component
-        scalar_projs = np.sort(np.array([np.dot(p, eigv[:, i]) for p in points]))
-        extent[i] = scalar_projs[-1]
-
-        if scalar_projs[0] < 0.:
-            extent -= scalar_projs[0]
+    # for each eigenvector calculate the scalar projection of the points on it (n_points, n_eigv)
+    scalar_projections = points.dot(eigv)
 
-    return extent
+    # and return the range of the projections (abs(max - min)) along each column (eigenvector)
+    return np.ptp(scalar_projections, axis=0)

diff --git a/tests/features/test_get_features.py b/tests/features/test_get_features.py
index 6661b89..a78efa7 100644
--- a/tests/features/test_get_features.py
+++ b/tests/features/test_get_features.py
@@ -793,18 +793,18 @@ def test_section_term_radial_distances():
 def test_principal_direction_extents():
     m = nm.load_morphology(SWC_PATH / 'simple.swc')
     principal_dir = features.get('principal_direction_extents', m)
-    assert_allclose(principal_dir, [14.736052694538641, 12.105102672688004])
+    assert_allclose(principal_dir, [10.99514 , 10.997688])
 
     # test with a realistic morphology
     m = nm.load_morphology(DATA_PATH / 'h5/v1' / 'bio_neuron-000.h5')
     p_ref = [
-        1672.969491,
-        142.437047,
-        224.607978,
-        415.50613,
-        429.830081,
-        165.954097,
-        346.832825,
+        1210.569727,
+        38.493958,
+        147.098687,
+        288.226628,
+        330.166506,
+        152.396521,
+        293.913857
     ]
     p = features.get('principal_direction_extents', m)
     assert_allclose(p, p_ref, rtol=1e-6)
diff --git a/tests/test_morphmath.py b/tests/test_morphmath.py
index 0188ccf..6119f7b 100644
--- a/tests/test_morphmath.py
+++ b/tests/test_morphmath.py
@@ -29,6 +29,7 @@
 from math import fabs, pi, sqrt
 
 import numpy as np
+from numpy import testing as npt
 from neurom import morphmath as mm
 from neurom.core.dataformat import Point
 from numpy.random import uniform
@@ -532,3 +533,59 @@ def test_spherical_coordinates():
 
         new_elevation, new_azimuth = mm.spherical_from_vector(vect)
         assert np.allclose([elevation, azimuth], [new_elevation, new_azimuth])
+
+
+def test_principal_direction_extent():
+
+    # test with points on a circle with radius 0.5, and center at 0.0
+    circle_points = np.array([
+        [ 5.0e-01,  0.0e+00,  0.0e+00],
+        [ 4.7e-01,  1.6e-01,  0.0e+00],
+        [ 3.9e-01,  3.1e-01,  0.0e+00],
+        [ 2.7e-01,  4.2e-01,  0.0e+00],
+        [ 1.2e-01,  4.8e-01,  0.0e+00],
+        [-4.1e-02,  5.0e-01,  0.0e+00],
+        [-2.0e-01,  4.6e-01,  0.0e+00],
+        [-3.4e-01,  3.7e-01,  0.0e+00],
+        [-4.4e-01,  2.4e-01,  0.0e+00],
+        [-5.0e-01,  8.2e-02,  0.0e+00],
+        [-5.0e-01, -8.2e-02,  0.0e+00],
+        [-4.4e-01, -2.4e-01,  0.0e+00],
+        [-3.4e-01, -3.7e-01,  0.0e+00],
+        [-2.0e-01, -4.6e-01,  0.0e+00],
+        [-4.1e-02, -5.0e-01,  0.0e+00],
+        [ 1.2e-01, -4.8e-01,  0.0e+00],
+        [ 2.7e-01, -4.2e-01,  0.0e+00],
+        [ 3.9e-01, -3.1e-01,  0.0e+00],
+        [ 4.7e-01, -1.6e-01,  0.0e+00],
+        [ 5.0e-01, -1.2e-16,  0.0e+00]
+    ])
+
+    npt.assert_allclose(
+        mm.principal_direction_extent(circle_points),
+        [1., 1., 0.], atol=1e-6,
+    )
+
+    # extent should be invariant to translations
+    npt.assert_allclose(
+        mm.principal_direction_extent(circle_points + 100.),
+        [1., 1., 0.], atol=1e-6,
+    )
+    npt.assert_allclose(
+        mm.principal_direction_extent(circle_points - 100.),
+        [1., 1., 0.], atol=1e-6,
+    )
+
+    cross_3D_points = np.array([
+        [-5.2, 0.0, 0.0],
+        [ 4.8, 0.0, 0.0],
+        [ 0.0,-1.3, 0.0],
+        [ 0.0, 4.7, 0.0],
+        [ 0.0, 0.0,-11.2],
+        [ 0.0, 0.0, 0.8],
+    ])
+
+    npt.assert_allclose(
+        sorted(mm.principal_direction_extent(cross_3D_points)),
+        [6.0, 10.0, 12.0], atol=0.1,
+    )

Bug in principal direction extent
There is a bug in the calculation of the principal direction extents:

https://github.com/BlueBrain/NeuroM/blob/fdf9f78be79508e34c0349936e233c184279c955/neurom/morphmath.py#L462-L496

At the point where the most negative value is subtracted from the entire extent:
```python
         if scalar_projs[0] < 0.: 
             extent -= scalar_projs[0] 
```
It shouldn't subtract from the entire extent, rather from the i-th component `extent[i]`.

Failing test example with points on a circle with radius `0.5`, and center at `0.0`:
```python
from neurom.morphmath import principal_direction_extent
from numpy import testing as npt

def test_principal_direction_extent():

    circle_points = np.array([
        [ 5.0e-01,  0.0e+00,  0.0e+00],
        [ 4.7e-01,  1.6e-01,  0.0e+00],
        [ 3.9e-01,  3.1e-01,  0.0e+00],
        [ 2.7e-01,  4.2e-01,  0.0e+00],
        [ 1.2e-01,  4.8e-01,  0.0e+00],
        [-4.1e-02,  5.0e-01,  0.0e+00],
        [-2.0e-01,  4.6e-01,  0.0e+00],
        [-3.4e-01,  3.7e-01,  0.0e+00],
        [-4.4e-01,  2.4e-01,  0.0e+00],
        [-4.9e-01,  8.2e-02,  0.0e+00],
        [-4.9e-01, -8.2e-02,  0.0e+00],
        [-4.4e-01, -2.4e-01,  0.0e+00],
        [-3.4e-01, -3.7e-01,  0.0e+00],
        [-2.0e-01, -4.6e-01,  0.0e+00],
        [-4.1e-02, -5.0e-01,  0.0e+00],
        [ 1.2e-01, -4.8e-01,  0.0e+00],
        [ 2.7e-01, -4.2e-01,  0.0e+00],
        [ 3.9e-01, -3.1e-01,  0.0e+00],
        [ 4.7e-01, -1.6e-01,  0.0e+00],
        [ 5.0e-01, -1.2e-16,  0.0e+00]
    ])

    npt.assert_allclose(
        principal_direction_extent(circle_points),
        [1., 1., 0.],
    )
```
Which returns instead `[1.49, 1.0, 0.0]`.

Thanks, @lidakanari for finding that there is something wrong with the principal direction extent function.

@arnaudon @adrien-berchet 
tests/features/test_get_features.py::test_principal_direction_extents tests/test_morphmath.py::test_principal_direction_extent
tests/features/test_get_features.py::test_get_raises tests/features/test_get_features.py::test_register_existing_feature tests/features/test_get_features.py::test_number_of_sections tests/features/test_get_features.py::test_max_radial_distance tests/features/test_get_features.py::test_section_tortuosity tests/features/test_get_features.py::test_number_of_segments tests/features/test_get_features.py::test_number_of_neurites tests/features/test_get_features.py::test_number_of_bifurcations tests/features/test_get_features.py::test_number_of_forking_points tests/features/test_get_features.py::test_number_of_leaves tests/features/test_get_features.py::test_trunk_angles tests/features/test_get_features.py::test_neurite_lengths tests/features/test_get_features.py::test_segment_radii tests/features/test_get_features.py::test_segment_meander_angles tests/features/test_get_features.py::test_morphology_volume_density tests/features/test_get_features.py::test_section_lengths tests/features/test_get_features.py::test_section_path_distances tests/features/test_get_features.py::test_segment_lengths tests/features/test_get_features.py::test_local_bifurcation_angles tests/features/test_get_features.py::test_remote_bifurcation_angles tests/features/test_get_features.py::test_segment_radial_distances_origin tests/features/test_get_features.py::test_section_radial_distances_endpoint tests/features/test_get_features.py::test_section_radial_distances_origin tests/features/test_get_features.py::test_number_of_sections_per_neurite tests/features/test_get_features.py::test_trunk_origin_radii tests/features/test_get_features.py::test_trunk_section_lengths tests/features/test_get_features.py::test_soma_radius tests/features/test_get_features.py::test_soma_surface_area tests/features/test_get_features.py::test_sholl_frequency tests/features/test_get_features.py::test_bifurcation_partitions tests/features/test_get_features.py::test_partition_asymmetry tests/features/test_get_features.py::test_partition_asymmetry_length tests/features/test_get_features.py::test_section_strahler_orders tests/features/test_get_features.py::test_section_bif_radial_distances tests/features/test_get_features.py::test_section_term_radial_distances tests/features/test_get_features.py::test_total_width tests/features/test_get_features.py::test_total_height tests/features/test_get_features.py::test_total_depth tests/test_morphmath.py::test_vector tests/test_morphmath.py::test_linear_interpolate tests/test_morphmath.py::test_interpolate_radius_r1_g_r2 tests/test_morphmath.py::test_interpolate_radius_r2_g_r1 tests/test_morphmath.py::test_interpolate_radius_extreme_cases tests/test_morphmath.py::test_path_fraction_point_two_points tests/test_morphmath.py::test_path_fraction_three_symmetric_points tests/test_morphmath.py::test_path_fraction_many_points tests/test_morphmath.py::test_scalar_projection tests/test_morphmath.py::test_scalar_projection_collinear tests/test_morphmath.py::test_scalar_projection_perpendicular tests/test_morphmath.py::test_vector_projection tests/test_morphmath.py::test_vector_projection_collinear tests/test_morphmath.py::test_vector_projection_perpendicular tests/test_morphmath.py::test_dist_point_line tests/test_morphmath.py::test_point_dist2 tests/test_morphmath.py::test_segment_length2 tests/test_morphmath.py::test_point_dist tests/test_morphmath.py::test_angle_3points_half_pi tests/test_morphmath.py::test_angle_3points_quarter_pi tests/test_morphmath.py::test_angle_3points_three_quarter_pi tests/test_morphmath.py::test_angle_3points_equal_points_returns_zero tests/test_morphmath.py::test_angle_3points_opposing_returns_pi tests/test_morphmath.py::test_angle_3points_collinear_returns_zero tests/test_morphmath.py::test_angle_between_vectors tests/test_morphmath.py::test_polygon_diameter tests/test_morphmath.py::test_average_points_dist tests/test_morphmath.py::test_path_distance tests/test_morphmath.py::test_segment_area tests/test_morphmath.py::test_segment_volume tests/test_morphmath.py::test_segment_length tests/test_morphmath.py::test_segment_radius tests/test_morphmath.py::test_segment_x_coordinate tests/test_morphmath.py::test_segment_y_coordinate tests/test_morphmath.py::test_segment_z_coordinate tests/test_morphmath.py::test_segment_radial_dist tests/test_morphmath.py::test_taper_rate tests/test_morphmath.py::test_segment_taper_rate tests/test_morphmath.py::test_pca tests/test_morphmath.py::test_sphere_area tests/test_morphmath.py::test_interval_lengths tests/test_morphmath.py::test_spherical_coordinates