Module geometry

Calculations on shapes and vectors.

class ectopylasm.geometry.Cone(height: float, radius: float, rot_x: float = 6.283185307179586, rot_y: float = 6.283185307179586, base_pos: ectopylasm.geometry.Point = Point(x=0, y=0, z=0))[source]

Bases: object

A cone.

The cone is defined mainly by its height and radius. When the other parameters are left at their default values, this will produce a cone with its axis along the z-axis and the center of its circular base at position (x, y, z) = (0, 0, 0).

Three optional parameters define its location and orientation: two rotation parameters rot_x and rot_y, giving respectively rotations around the x and y axes (the x rotation is performed first, then the y rotation) and one translation parameter called base_pos, which itself is a Point object, and which moves the position of the circular base of the cone.

apex_position()[source]

Get cone apex position from cone parameters.

axis()[source]

Get the cone’s axis unit vector from its rotation angles (radians).

classmethod from_fit_result(fit_result)[source]

Generate a Cone from fit_result, the output of fit.fit_cone.

classmethod from_points(points, **kwargs)[source]

Generate a Cone by fitting to a set of points.

The set of N point coordinates with shape (3, N) is given by points.

opening_angle()[source]

Twice the opening angle is the maximum angle between directrices.

class ectopylasm.geometry.ConeRegion[source]

Bases: enum.Enum

Class defining three regions in and around cones.

These regions are used in point_distance_to_cone to pass on information about which kind of region the point is in. This can be used in other functions (like filter_points_cone).

The three options are: - perpendicular: the point is at a location where its shortest distance to

the cone surface is perpendicular to that surface
  • above_apex: the point is somewhere above the apex of the cone, but not
    perpendicular to the surface
  • below_directrix: the point is not perpendicular to the surface and it is
    below the directrix
class ectopylasm.geometry.Plane(a, b, c, d)[source]

Bases: object

A plane.

The plane is defined by four parameters, a, b, c and d, which form the plane equation a*x + b*y + c*z + d = 0. Points (x, y, z) for which this equation applies are points on the plane.

On creation, the input parameters a, b and c are normalized. When seen as a vector n = (a, b, c), n is the normal vector to the plane, indicating its direction. This vector is normalized to have length one.

The fourth parameter d relates to the position of the plane in space. It can be calculated from a known point in the plane (X, Y, Z) as d = -a*X - b*Y - c*Z, but can also be given directly.

static d_from_point(point, normal)[source]

Calculate d factor in plane equation ax + by + cz + d = 0.

classmethod from_fit_result(fit_result)[source]

Generate a Plane from fit_result, the output of fit.fit_plane.

classmethod from_point(a, b, c, point)[source]

Plane constructor that uses a point on the plane as input instead of d.

classmethod from_points(points)[source]

Generate a Plane by fitting to a set of points.

The set of N point coordinates with shape (3, N) is given by points.

generate_point()[source]

Generate a point in the plane.

Calculate a point in the plane based on d at x,y=0,0 (could be anywhere); -cz = ax + by + d. If c happens to be zero, try x,z=0,0, and if b is zero as well, do y,z=0,0.

exception ectopylasm.geometry.PlaneSurfaceLimitException[source]

Bases: RuntimeError

Raised by plane_surface when the given limits are invalid.

exception ectopylasm.geometry.PlaneSurfaceNormalException[source]

Bases: RuntimeError

Raised by plane_surface when the normal is not compatible with the limits.

class ectopylasm.geometry.Point(x: float, y: float, z: float)[source]

Bases: object

A three dimensional point with x, y and z components.

to_array()[source]

Convert to a NumPy array np.array((x, y, z)).

ectopylasm.geometry.angle_between_two_vectors(a, b)[source]

Calculate the angle in radians between two vectors a and b.

Implementation credits to https://stackoverflow.com/a/13849249/1199693.

ectopylasm.geometry.cone_surface(cone: ectopylasm.geometry.Cone, n_steps=20)[source]

Calculate coordinates of the surface of a cone.

cone: a Cone object n_steps: number of steps in the parametric range used for drawing (more gives a

smoother surface, but may render more slowly)
ectopylasm.geometry.cone_sympy_model(cone: ectopylasm.geometry.Cone)[source]

Convert cone to a sympy based cone model.

Returns the model (first return value) and a dictionary with constituent symbols.

ectopylasm.geometry.filter_points_cone(points_xyz, cone: ectopylasm.geometry.Cone, thickness)[source]

Select the points that are within the thick cone.

points_xyz: a vector of shape (3, N) representing N points in 3D space cone: a Cone object thickness: distance between the two cone surfaces (i.e. their directrices)

ectopylasm.geometry.filter_points_plane(points_xyz, plane: ectopylasm.geometry.Plane, plane_thickness)[source]

Select the points that are within the thick plane.

points_xyz: a vector of shape (3, N) representing N points in 3D space plane: a Plane object plane_thickness: the thickness of the plane (the distance between the two

composing planes)
ectopylasm.geometry.fit_cone(xyz, initial_guess_cone: ectopylasm.geometry.Cone = None)[source]

Fit a cone to the point coordinates in xyz.

Dev note: this fit is implemented with scipy instead of symfit. See https://github.com/tBuLi/symfit/issues/263 for the problem with using symfit for this one.

ectopylasm.geometry.fit_plane(xyz)[source]

Fit a plane to the point coordinates in xyz.

Dev note: An alternative implementation is possible that omits the f variable, and thus has one fewer degree of freedom. This means the fit is easier and maybe more precise. This could be tested. The notebook req4.1_fit_plane.ipynb in the explore repository (https://github.com/sundial-pointcloud-geometry/explore) has some notes on this. The problem with those models where f is just zero and the named symfit model is created for one of x, y or z instead is that you have to divide by one of the a, b or c parameters respectively. If one of these turns out to be zero, symfit will not find a fit. A solution would be to actually create three models and try another if one of them fails to converge.

ectopylasm.geometry.normalize_vector(vector)[source]

Input vector divided by its absolute size yields a vector of size 1.

ectopylasm.geometry.plane_surface(plane: ectopylasm.geometry.Plane, x_lim=None, y_lim=None, z_lim=None)[source]

Get plane surface coordinates.

Calculate coordinates of the part of a plane inside a cubical box. Two of the limited parameters are used to calculate the coordinates in the third direction.

Note that the first number in the pairs must be smaller than the second!

You only need to provide two pairs of coordinates, so only two of x_lim, y_lim and z_lim need to be defined. When all three are defined, the default is to use the x and y pairs. This option to choose is useful when you have a plane that has a zero normal component in one of the directions. In that case, you cannot use the limits in that direction, because the plane coordinates will involve a division by that normal component (which would give a division by zero error).

plane: a Plane object x_lim: iterable of the two extrema in the x direction. Default: None. y_lim: same as x, but for y z_lim: same as x, but for z limit_all: see explanation above. Default: False.

ectopylasm.geometry.point_distance_to_cone(point, cone: ectopylasm.geometry.Cone, return_extra=False)[source]

Get distance of point to cone.

Check whether for a point point, the shortest path to the cone is perpendicular to the cone surface (and if so, return it). If not, it is either “above” the apex and the shortest path is simply the line straight to the apex, or it is “below” the base, and the shortest path is the shortest path to the directrix (the base circle).

This function returns a second value depending on which of the three above cases is true for point point. If we’re using the perpendicular, it is True, if we’re above the apex it is False and if it is below the base, it is None.

Extra values can be returned to be reused outside the function by setting return_extra to True.

ectopylasm.geometry.point_distance_to_plane(point, plane: ectopylasm.geometry.Plane)[source]

Get signed distance of point to plane.

The sign of the resulting distance tells you whether the point is in the same or the opposite direction of the plane normal vector.

point: an iterable of length 3 representing a point in 3D space plane: a Plane object

ectopylasm.geometry.thick_cone_base_positions(cone: ectopylasm.geometry.Cone, thickness)[source]

Convert cone base position to two thick cone base positions.

Given the cone parameters, return two base positions along the cone axis that are a certain distance apart, such that the distance between the cone surfaces (the directrices) is thickness apart.

cone: a Cone object thickness: distance between the two cone surfaces (i.e. their directrices)

ectopylasm.geometry.thick_cone_cones(cone: ectopylasm.geometry.Cone, thickness) → Tuple[ectopylasm.geometry.Cone, ectopylasm.geometry.Cone][source]

Convert one Cone to two cones separated by thickness.

Given the cone parameters, return two cones, such that the distance between the cone surfaces (the directrices) is thickness apart.

cone: a Cone object thickness: distance between the two cone surfaces (i.e. their directrices)

ectopylasm.geometry.thick_plane_planes(plane: ectopylasm.geometry.Plane, thickness)[source]

Convert plane to two planes separated by thickness.

ectopylasm.geometry.thick_plane_points(plane: ectopylasm.geometry.Plane, thickness, plane_point=None)[source]

Convert plane point to two thick plane points.

Given a Plane and a thickness, return two points along the normal that are thickness apart. Optionally specify a specific point in the plane.