polliwog

Polygonal chains

class polliwog.Polyline(v, is_closed=False)

An immutable polygonal chain in 3-space, which may be open or closed.

There are no constraints on the geometry. For example, the chain may be simple or self-intersecting, and the points need not be unique.

The methods do not mutate; they create new polylines which exhibit the requested mutation.

POSITION_DTYPE

alias of numpy.float64

aligned_along_subsegment(p1, p2)

Flip an open polyline if necessary so the point nearest to p1 is ordered before the point nearest to p2.

Flip a closed polyline if necessary to keep the path from the point nearest p1 to the point nearest p2 _shorter_. This path may wrap around the end.

Note

In conjunction with .sliced_at_points(), this method is useful for selecting a particular subsection of a path whose orientation through the region of interest is unknown.

aligned_with(vector)

Flip the polyline if necessary, so it’s aligned with the given vector rather than against it. Works on open polylines and considers only the two end vertices.

apex(axis)

Find the most extreme point in the direction of the axis provided.

axis: A vector, which is an 3x1 np.array.

property bounding_box

The bounding box which encompasses the entire polyline.

classmethod deserialize(data)

Create a Polyline from the given JSON representation.

The schema is defined in types/src/schema.json.

Parameters

data (dict) – The JSON representation.

Returns

The deserialized polyline.

Return type

Polyline

flipped()

Flip the polyline from end to end. Return a new polyline.

flipped_if(condition)

Conditionally flip the polyline from end to end, returning self or a new polyline.

index_of_vertex(point, atol=1e-08)

Return the index of the vertex with the given point. If there are coincident vertices at that point, return the one at the lowest index.

intersect_plane(plane, ret_edge_indices=False)

Returns the points of intersection between the plane and any of the edges of polyline, which should be an instance of Polyline.

TODO: This doesn’t correctly handle vertices which lie on the plane.

classmethod join(*polylines, is_closed=False)

Join together a stack of open polylines end-to-end into one contiguous polyline. The last vertex of the first polyline is connected to the first vertex of the second polyline, and so on.

nearest(points, ret_segment_indices=False, ret_distances=False, ret_t_values=False)

For the given query point or points, return the nearest point on the polyline. With ret_segment_indices=True, also return the segment indices of those points. With ‘ret_distances=True, also return the distance to the nearest point.

property num_e

Return the number of segments in the polyline.

property num_v

Return the number of vertices in the polyline.

property path_centroid

The weighted average of all the points along the edges of the polyline.

point_along_path(fraction_of_total)

Selects a point the given fraction of the total length of the polyline. For example, to find the halfway point, pass fraction_of_total=0.5. Also works with stacked values, e.g. fraction_of_total=np.linspace(0, 1, 11).

Parameters

fraction_of_total (object) – Fraction of the total length, from 0 to 1

Returns (object):

A point on the polyline that is the given fraction of the total length from the starting point to the endpoint. For stacked fractions, return the points.

rolled(index, ret_edge_mapping=False)

Return a new Polyline which reindexes the callee polyline, which much be closed, so the vertex with the given index becomes vertex 0.

ret_edge_mapping: if True, return an array that maps from old edge

indices to new.

rounded(decimals=None)

Return a copy of this polyline, with vertices rounded to the specified precision.

Parameters
  • decimals (int) – The desired number of decimal places. The default is

  • DEFAULT_DECIMALS.

Returns

The rounded polyline.

Return type

Polyline

sectioned(section_breakpoints, copy_vs=False)

Section the given open polyline at the given breakpoints, which indicate where one segment ends and the next one starts. Each of the breakpoint vertices is included as an endpoint in one section and a start point in the next section.

Parameters
  • breakpoints (np.arraylike) – The indices of the breakpoints.

  • copy_vs (bool) – When True, copy the vertices into the new polylines. When False, return polylines with views for vertex arrays.

Returns

A list of the sectioned polylines.

Return type

list

property segment_lengths

The lengths of each segment.

property segment_vectors

Vectors spanning each segment.

property segments

Coordinate pairs for each segment.

serialize(decimals=None)

Return a JSON representation of this polyline, with vertices rounded to the specified precision.

The schema is defined in types/src/schema.json.

Parameters
  • decimals (int) – The desired number of decimal places. The default is

  • DEFAULT_DECIMALS.

Returns

The JSON representation.

Return type

dict

sliced_at_indices(start, stop)

Take an slice of the given polyline starting at the start vertex index and ending just befeor reaching the stop vertex index. Always returns an open polyline.

When called on a closed polyline, the indies can wrap around the end.

sliced_at_points(start_point, end_point, atol=1e-08)

Take a slice of the given polyline at the given start and end points. These are expected to be on a vertex or on a segment. If on a segment (or near to but not directly on a segment) a new point is inserted at exactly the given point.

sliced_by_plane(plane)

Return a new Polyline which keeps only the part that is in front of the given plane.

For open polylines, the plane must intersect the polyline exactly once.

For closed polylines, the plane must intersect the polyline exactly twice, leaving a single contiguous segment in front.

subdivided_by_length(max_length, edges_to_subdivide=None, ret_indices=False)

Subdivide each line segment longer than max_length with equal-length segments, such that none of the new segments are longer than max_length. Returns a new Polyline.

Parameters
  • max_length (float) – The maximum lenth of a segment.

  • edges_to_subdivide (np.arraylike) – An optional boolean mask the same length as the number of edges. Only the edges marked True are subdivided. The default is to subdivide all edges longer than max_length.

  • ret_indices (bool) – When True, also returns the indices of the original vertices.

property total_length

The total length of all the segments.

classmethod validate(data)

Validate a polyline JSON representation.

The schema is defined in types/src/schema.json.

Parameters

data (dict) – The JSON representation.

with_insertions(points, indices, ret_new_indices=False)

Return a new polyline with the given points inserted before the given indices.

With ret_new_indices=True, also returns the new indices of the original vertices and the new indices of the inserted points.

with_segments_bisected(segment_indices, ret_new_indices=False)

Return a new polyline with the given segments cut in half.

With ret_new_indices=True, also returns the new indices of the original vertices and the new indices of the inserted points.

Polygonal chain functions

polliwog.polyline.edges_for(num_v, is_closed)

Compute edges of a polyline, as an array of vertex indices for each edge.

Parameters
  • num_v (int) – The number of vertices.

  • is_closed (bool) – Whether or not the polyline is closed.

Returns

The kx2 edges array.

Return type

np.ndarray

polliwog.polyline.inflection_points(points, rise_axis, run_axis)

Find the list of vertices that preceed inflection points in a curve. The curve is differentiated with respect to the coordinate system defined by rise_axis and run_axis.

Interestingly, lambda x: 2*x + 1 should have no inflection points, but almost every point on the line is detected. It’s because a zero or zero crossing in the second derivative is necessary but not sufficient to detect an inflection point. You also need a higher derivative of odd order that’s non-zero. But that gets ugly to detect reliably using sparse finite differences. Just know that if you’ve got a straight line this method will go a bit haywire.

rise_axis: A vector representing the vertical axis of the coordinate system. run_axis: A vector representing the the horiztonal axis of the coordinate system.

returns: a list of points in space corresponding to the vertices that immediately preceed inflection points in the curve

polliwog.polyline.point_of_max_acceleration(points, rise_axis, run_axis, subdivide_by_length=None)

Find the point on a curve where the curve is maximally accelerating in the direction specified by rise_axis. run_axis is the horizontal axis along which slices are taken.

Parameters
  • points (np.arraylike) – A stack of points, as kx3. For best results, trim these to the area of interest before calling.

  • rise_axis (np.arraylike) – The vertical axis, as a 3D vector.

  • run_axis (np.arraylike) – The horizonal axis, as a 3D vector.

  • subdivide_by_length (float) – When provided, the maximum space between each point. The idea is keep the slice width small, however this constraint is applied in 3D space, not along the run_axis. For best results pass a value that is small relative to the changes in the geometry. When None, the points are used without modification.

Planes

class polliwog.Plane(reference_point, normal, direction_decimals=None)

An immutable 2D plane (not a hyperplane) in 3D space.

Parameters
  • reference_point (np.ndarray) – A reference point on the plane, as a NumPy array with three coordinates.

  • normal (np.ndarray) – The plane normal vector, with unit length, as a NumPy array with three coordinates.

  • direction_decimals (int) – The desired number of decimal places for validating that normal has unit length. The default is DEFAULT_DIRECTION_DECIMALS.

Note

To construct a plane from a non-unit length normal, use Plane.from_point_and_normal().

POSITION_DTYPE

alias of numpy.float64

property canonical_point

A canonical point on the plane, the one at which the normal would intersect the plane if drawn from the origin (0, 0, 0).

This is computed by projecting the reference point onto the normal.

This is useful for partitioning the space between two planes, as we do when searching for planar cross sections.

classmethod deserialize(data)

Create a Plane from the given JSON representation.

The schema is defined in types/src/schema.json.

Parameters

data (dict) – The JSON representation.

Returns

The deserialized plane.

Return type

Plane

property equation

Returns parameters A, B, C, D as a 1x4 np.array, where

Ax + By + Cz + D = 0

defines the plane.

classmethod fit_from_points(points)

Fits a plane whose normal is orthgonal to the first two principal axes of variation in the data and centered on their centroid.

flipped()

Creates a new Plane with an inverted orientation.

flipped_if(condition)

Conditionally flip the plane, returning self or a new Plane with an inverted orientation.

classmethod from_point_and_normal(reference_point, normal, direction_decimals=None)

Create a plane using the given reference point and normal vector, which will be normalized for you.

Parameters
  • reference_point (np.ndarray) – A reference point on the plane, as a NumPy array with three coordinates.

  • normal (np.ndarray) – The plane normal vector, with unit length, as a NumPy array with three coordinates.

  • direction_decimals (int) – The desired number of decimal places for validating that normal has unit length. The default is DEFAULT_DIRECTION_DECIMALS.

Returns

The requested plane.

Return type

Plane

classmethod from_points(p1, p2, p3)

If the points are oriented in a counterclockwise direction, the plane’s normal extends towards you.

classmethod from_points_and_vector(p1, p2, vector, direction_decimals=None)

Compute a plane which contains two given points and the given vector. Its reference point will be p1.

For example, to find the vertical plane that passes through two landmarks:

from_points_and_normal(p1, p2, vector)

Another way to think about this: identify the plane to which your result plane should be perpendicular, and specify vector as its normal vector.

Parameters

direction_decimals (int) – The desired number of decimal places for validating that normal has unit length. The default is DEFAULT_DIRECTION_DECIMALS.

mirror_point(points)

Mirror a point (or stack of points) to the opposite side of the plane.

points_in_front(points, inverted=False, ret_indices=False)

Given an array of points, return the points which lie in the half-space in front of it (i.e. in the direction of the plane normal).

Parameters
  • points (np.arraylikw) – An array of points.

  • inverted (bool) – When True, return the points which lie on or behind the plane instead.

  • ret_indices (bool) – When True, return the indices instead of the points themselves.

Note

Use points_on_or_in_front() for points which lie either on the plane or in front of it.

points_on_or_in_front(points, inverted=False, ret_indices=False)

Given an array of points, return the points which lie either on the plane or in the half-space in front of it (i.e. in the direction of the plane normal).

Parameters
  • points (np.arraylikw) – An array of points.

  • inverted (bool) – When True, return the points behind the plane instead.

  • ret_indices (bool) – When True, return the indices instead of the points themselves.

Note

Use points_in_front() to get points which lie only in front of the plane.

project_point(points)

Project a given point (or stack of points) to the plane.

rounded(position_decimals=None, direction_decimals=None)

Return a copy of this plane, with the reference point and normal rounded to the specified precision.

Parameters
  • position_decimals (int) – The desired number of decimal places for the reference point. The default is DEFAULT_POSITION_DECIMALS.

  • direction_decimals (int) – The desired number of decimal places for the normal. The default is DEFAULT_DIRECTION_DECIMALS.

Returns

The rounded plane.

Return type

Plane

serialize(position_decimals=None, direction_decimals=None)

Return a JSON representation of this plane, with the reference point and normal rounded to the specified precision.

The schema is defined in types/src/schema.json.

Parameters
  • position_decimals (int) – The desired number of decimal places for the reference point. The default is DEFAULT_POSITION_DECIMALS.

  • direction_decimals (int) – The desired number of decimal places for the normal. The default is DEFAULT_DIRECTION_DECIMALS.

Returns

The JSON representation.

Return type

dict

sign(points)

Given an array of points, return an array with +1 for points in front of the plane (in the direction of the normal), -1 for points behind the plane (away from the normal), and 0 for points on the plane.

signed_distance(points)

Returns the signed distances to the given points or the signed distance to a single point.

Parameters

points (np.arraylike) – A 3D point or a kx3 stack of points.

Returns

  • Given a single 3D point, the distance as a NumPy scalar.

  • Given a kx3 stack of points, an k array of distances.

Return type

depends

tilted(new_point, coplanar_point)

Create a new plane, tilted so it passes through new_point. Also specify a coplanar_point which the old and new planes should have in common.

Parameters
  • new_point (np.arraylike) – A point on the desired plane, with shape (3,).

  • coplanar_point (np.arraylike) – The (3,) point which the old and new planes have in common.

Returns

The adjusted plane.

Return type

Plane

classmethod validate(data)

Validate a plane JSON representation.

The schema is defined in types/src/schema.json.

Parameters

data (dict) – The JSON representation.

Named coordinate planes

polliwog.Plane.xy = <Plane of [0. 0. 1.] through [0. 0. 0.]>

The xy-plane.

polliwog.Plane.xz = <Plane of [0. 1. 0.] through [0. 0. 0.]>

The xz-plane.

polliwog.Plane.yz = <Plane of [1. 0. 0.] through [0. 0. 0.]>

The yz-plane.

Plane functions

polliwog.plane.intersect_segment_with_plane(start_points, segment_vectors, points_on_plane, plane_normals)

Check for intersections between a line segment and a plane, or pairwise between a stack of line segments and a stack of planes.

polliwog.plane.mirror_point_across_plane(points, plane_equations)

Mirror each point to the corresponding point on the opposite side of the plane.

polliwog.plane.normal_and_offset_from_plane_equations(plane_equations)

Given A, B, C, D of the plane equation Ax + By + Cz + D = 0, return the plane normal vector which is [A, B, C] and the offset D.

polliwog.plane.plane_equation_from_points(points)

Given many sets of three points, return a stack of plane equations [A, B, C, D] which satisfy Ax + By + Cz + D = 0. Also works on three points to return a single plane equation.

These coefficients can be decomposed into the plane normal vector which is [A, B, C] and the offset D, either by the caller or by using normal_and_offset_from_plane_equations().

polliwog.plane.plane_normal_from_points(points, normalize=True)

Given a set of three points, compute the normal of the plane which passes through them. Also works on stacked inputs (i.e. many sets of three points).

This is the same as polliwog.tri.functions.surface_normals, to which this delegates.

polliwog.plane.project_point_to_plane(points, plane_equations)

Project each point to the corresponding plane.

polliwog.plane.signed_distance_to_plane(points, plane_equations)

Return the signed distances from each point to the corresponding plane.

For convenience, can also be called with a single point and a single plane.

polliwog.plane.slice_triangles_by_plane(vertices, faces, plane_reference_point, plane_normal, faces_to_slice=None, ret_face_mapping=False)

Slice the given triangles by the given plane.

  • Triangles partially in front of the plane are sliced.

  • Triangles fully in front of the plane are kept as is.

  • Triangles fully behind the plane are culled.

Returns a tuple: vertices, faces.

Triangles

polliwog.tri.FACE_DTYPE

alias of numpy.int64

polliwog.tri.barycentric_coordinates_of_points(vertices_of_tris, points)

Compute barycentric coordinates for the projection of a set of points to a given set of triangles specfied by their vertices.

These barycentric coordinates can refer to points outside the triangle. This happens when one of the coordinates is negative. However they can’t specify points outside the triangle’s plane. (That requires tetrahedral coordinates.)

The returned coordinates supply a linear combination which, applied to the vertices, returns the projection of the original point the plane of the triangle.

Parameters
  • vertices_of_tris (np.arraylike) – A set of triangle vertices as kx3x3.

  • points (np.arraylike) – Coordinates of points as kx3.

Returns

Barycentric coordinates as kx3

Return type

np.ndarray

See also

polliwog.tri.edges_of_faces(faces, normalize=True)

Given a stack of triangles expressed as vertex indices, return a normalized array of edges. When normalize=True, sort the edges so they more easily can be compared.

polliwog.tri.quads_to_tris(quads, ret_mapping=False)

Convert quad faces to triangular faces.

quads: An nx4 array. ret_mapping: A bool.

When ret_mapping is True, return a 2nx3 array of new triangles and a 2nx3 array mapping old quad indices to new trangle indices.

When ret_mapping is False, return the 2nx3 array of triangles.

polliwog.tri.sample(vertices_of_tris, num_samples, rng=None, weights=None, ret_points=True, ret_face_indices=False)

Generate points sampled across the surface of the triangles provided.

By default, triangles are weighted by area, and the random number seed is fixed, making this function deterministic.

Parameters
  • vertices_of_tris (np.arraylike) – A set of triangle vertices as kx3x3.

  • num_samples (int) – The number of samples desired.

  • rng (np.random.Generator) – A NumPy random number generator. To obtain random results for each invocation, pass np.random.default_rng().

  • weights (np.arraylike) – kx3 weights. By default, triangles are weighted by area.

  • ret_face_indices (bool) – When True, return the face indices from which each point was taken.

Returns

The sampledpoints, or a tuple containing the sampled points and their corresponding face indices.

Return type

object

polliwog.tri.surface_area(vertices_of_tris)

Compute the surface area of a triangle or triangles.

Parameters
  • vertices_of_tris (np.arraylike) – A set of triangle vertices as 3x3 or

  • kx3x3.

Returns

For 3x3 input, a float containing the area. For kx3x3 input, an array containing the area of each triangle.

Return type

object

polliwog.tri.surface_normals(points, normalize=True)

Compute the surface normal of a triangle. The direction of the normal follows conventional counter-clockwise winding and the right-hand rule.

Also works on stacked inputs (i.e. many sets of three points).

polliwog.tri.tri_contains_coplanar_point(a, b, c, point)

Assuming point is coplanar with the triangle ABC, check if it lies inside it.

Geometric transformations

High-level API

class polliwog.CompositeTransform

Composite transform using homogeneous coordinates.

Example

>>> transform = CompositeTransform()
>>> transform.uniform_scale(10)
>>> transform.reorient(up=[0, 1, 0], look=[-1, 0, 0])
>>> transform.translate([0, -2.5, 0])
>>> transformed_scan = transform(scan_v)
>>> # ... register the scan here ...
>>> untransformed_alignment = transform(alignment_v, reverse=True)

See also

__call__(points, from_range=None, reverse=False, discard_z_coord=False, treat_input_as_vector=False)
Parameters
  • points (np.arraylike) – Points to transform, as a 3xn array.

  • from_range (tuple) – The indices of the subset of the transformations to apply. e.g. (0, 2), (2, 4). When None, which is the default, apply them all.

  • reverse (bool) – When True applies the selected transformations in reverse. This has no effect on how range is interpreted, only whether the selected transformations apply in the forward or reverse mode.

  • discard_z_coord (bool) – When True, discard the z coordinate of the result. This is useful when applying viewport transformations.

  • treat_input_as_vector (bool) – When True, transform a vector instead of a point.

append_transform(forward, reverse=None)

Append an arbitrary transformation, defined by 4x4 forward and reverse matrices.

The new transformation is added to the end. Return its index.

convert_units(from_units, to_units)

Convert the mesh from one set of units to another.

These calls are equivalent:

>>> composite.convert_units(from_units='cm', to_units='m')
>>> composite.uniform_scale(.01)

Supports the length units from Ounce: https://github.com/lace/ounce/blob/master/ounce/core.py#L26

flip(dim)

Flip about one of the axes.

Parameters

dim (int) – The axis to flip about: 0 for x, 1 for y, 2 for z.

non_uniform_scale(x_factor, y_factor, z_factor, allow_flipping=False)

Scale by the given factors along x, y, and z.

Parameters
  • x_factor (float) – The scale factor to be applied along the x axis.

  • y_factor (float) – The scale factor to be applied along the y axis.

  • z_factor (float) – The scale factor to be applied along the z axis.

See also

uniform_scale()

reorient(up, look)

Reorient using up and look.

rotate(rotation)

Rotate by the given 3x3 rotation matrix or a Rodrigues vector.

transform_matrix_for(from_range=None, reverse=False)

Return a 4x4 transformation matrix representation.

range: The min and max indices of the subset of the transformations to

apply. e.g. (0, 2), (2, 4). Inclusive of the min value, exclusive of the max value. The default is to apply them all.

reverse: When True returns a matrix for the inverse transform.

This has no effect on how range is interpreted, only whether the forward or reverse matrices are used.

translate(translation)

Translate by the vector provided.

Parameters

vector (np.arraylike) – A 3x1 vector.

uniform_scale(factor, allow_flipping=False)

Scale by the given factor.

Parameters

factor (float) – The scale factor.

See also

non_uniform_scale()

class polliwog.CoordinateManager

Example

>>> coordinate_manager = CoordinateManager()
>>> coordinate_manager.tag_as('source')
>>> coordinate_manager.translate(-cube.floor_point)
>>> coordinate_manager.uniform_scale(2)
>>> coordinate_manager.tag_as('floored_and_scaled')
>>> coordinate_manager.translate(np.array([0., -4., 0.]))
>>> coordinate_manager.tag_as('centered_at_origin')
>>> coordinate_manager.source = cube
>>> centered_mesh = coordinate_manager.centered_at_origin
__setattr__(name, points)

value: An nx3 array of points or an instance of Mesh.

tag_as(name)

Give a name to the current state.

Transform functions

polliwog.transform.apply_transform(transform)

Wrap the given transformation matrix with a function which conveniently can be invoked with either points or a single point, returning the same. It applies the transformation to those points using homogeneous coordinates.

Parameters

points (np.ndarray) – The point (3,) or points kx3 to transform.

Returns

A function which accepts an np.ndarray containing a point (3,) or points kx3 to transform, and returns an ndarray of the same shape. Also accepts two kwargs. The first is discard_z_coord. When True, discard the z coordinate of the result. This is useful when applying viewport transformations. The second is treat_input_as_vectors which does not use the homogeneous coordinate, and therefore ignores translation.

Return type

func

polliwog.transform.cv2_rodrigues(r, calculate_jacobian=False)

cv2_rodrigues is a wrapped function designed to be API compatible with OpenCV’s cv2.Rodrigues.

If it is given a rotation matrix, it returns a Rodrigues vector.

If it is given a Rodrigues vector, it returns a rotation matrix.

To make your code clearer, call rodrigues_vector_to_rotation_matrix or rotation_matrix_to_rodrigues_vector directly, which makes the intent of your code clearer.

polliwog.transform.euler(xyz, order='xyz', units='deg')

Convert a Euler angle representation of 3D rotations to a 3x3 rotation matrix.

Euler angles are a way of representing 3D rotations as a sequence of rotations about the axes. Conceptually, think of euler([10, 20, 30]) as “Rotate 10 degrees around the x axis, then 20 degrees around the y axis, then 30 degrees around the z axis” (that ordering can be changed with the order argument, and the units can be given in degrees or radians by setting units to ‘deg’ or ‘rad’).

Euler angles are a problematic representation of rotation for numerical methods, as there are multiple possible representations for a given rotation. But they are a very intuitive and readable way to initialize a rotation matrix.

polliwog.transform.rodrigues_vector_to_rotation_matrix(r, calculate_jacobian=False)

Convert a 3x1 or 1x3 Rodrigues vector to a 3x3 rotation matrix.

A Rodrigues vector is a 3 element vector representing a 3D rotation. Its direction represents the axis about which to rotate and its magnitude represents the amount to rotate by.

All of SO3 (that is, all 3D rotations) can be uniquely represented by a Rodrigues vector, and it does not suffer from the multiple representation and gimbal locking problems that Euler angle representations do.

If calculate_jacobian is passed, then the derivative of the rotation is also computed. Note that the derivative is undefined for a Rodrigues vector of [0,0,0] (that is, no rotation).

polliwog.transform.rotation_from_up_and_look(up, look)

Rotation matrix to rotate a mesh into a canonical reference frame. The result is a rotation matrix that will make up along +y and look along +z (i.e. facing towards a default opengl camera).

up: The direction you want to become +y. look: The direction you want to become +z.

polliwog.transform.rotation_matrix_to_rodrigues_vector(r, calculate_jacobian=False)

Convert a 3x3 rotation matrix to a 3x1 or 1x3 Rodrigues vector.

A Rodrigues vector is a 3 element vector representing a 3D rotation. Its direction represents the axis about which to rotate and its magnitude represents the amount to rotate by.

All of SO3 (that is, all 3D rotations) can be uniquely represented by a Rodrigues vector, and it does not suffer from the multiple representation and gimbal locking problems that Euler angle representations do.

If calculate_jacobian is passed, then the derivative of the rotation is also computed. Note that the derivative is undefined for a Rodrigues vector of [0,0,0] (that is, no rotation).

polliwog.transform.transform_matrix_for_non_uniform_scale(x_factor, y_factor, z_factor, allow_flipping=False, ret_inverse_matrix=False)

Create a transformation matrix that scales by the given factors along x, y, and z.

Forward:

[[ s_0, 0, 0, 0 ], [ 0, s_1, 0, 0 ], [ 0, 0, s_2, 0 ], [ 0, 0, 0, 1 ]]

Reverse:

[[ 1/s_0, 0, 0, 0 ], [ 0, 1/s_1, 0, 0 ], [ 0, 0, 1/s_2, 0 ], [ 0, 0, 0, 1 ]]

Parameters
  • x_factor (float) – The scale factor to be applied along the x axis, which should be positive.

  • y_factor (float) – The scale factor to be applied along the y axis, which should be positive.

  • z_factor (float) – The scale factor to be applied along the z axis, which should be positive.

  • allow_flipping (bool) – When True, allows scale factors to be positive or negative, though not zero.

  • ret_inverse_matrix (bool) – When True, also returns a matrix which provides the inverse transform.

polliwog.transform.transform_matrix_for_rotation(rotation, ret_inverse_matrix=False)

Create a transformation matrix from the given 3x3 rotation matrix or a Rodrigues vector.

With ret_inverse_matrix=True, also returns a matrix which provides the reverse transform.

polliwog.transform.transform_matrix_for_translation(translation, ret_inverse_matrix=False)

Create a transformation matrix which translates by the provided displacement vector.

Forward:

[[ 1, 0, 0, v_0 ], [ 0, 1, 0, v_1 ], [ 0, 0, 1, v_2 ], [ 0, 0, 0, 1 ]]

Reverse:

[[ 1, 0, 0, -v_0 ], [ 0, 1, 0, -v_1 ], [ 0, 0, 1, -v_2 ], [ 0, 0, 0, 1 ]]

Parameters

vector (np.arraylike) – A 3x1 vector.

polliwog.transform.transform_matrix_for_uniform_scale(scale_factor, allow_flipping=False, ret_inverse_matrix=False)

Create a transformation matrix that scales by the given factor.

Forward:

[[ s_0, 0, 0, 0 ], [ 0, s_1, 0, 0 ], [ 0, 0, s_2, 0 ], [ 0, 0, 0, 1 ]]

Reverse:

[[ 1/s_0, 0, 0, 0 ], [ 0, 1/s_1, 0, 0 ], [ 0, 0, 1/s_2, 0 ], [ 0, 0, 0, 1 ]]

Parameters
  • factor (float) – The scale factor.

  • ret_inverse_matrix (bool) – When True, also returns a matrix which provides the inverse transform.

polliwog.transform.view_to_orthographic_projection(width, height, near=0.1, far=2000, inverse=False)

Create an orthographic projection matrix with the given parameters, which maps points from world space to coordinates in the normalized view volume. These coordinates range from -1 to 1 in x, y, and z with (-1, -1, -1) at the bottom-left of the near clipping plane, and (1, 1, 1) at the top-right of the far clipping plane.

Parameters
  • width (float) – Width of the window, in pixels. (FIXME: Is this really correct?)

  • height (float) – Height of the window, in pixels. (FIXME: Is this really correct?)

  • near (float) – Near clipping plane. (FIXME: Clarify!)

  • far (float) – Far clipping plane. (FIXME: Clarify!)

  • inverse (bool) – When True, return the inverse transform instead.

Returns

The 4x4 transformation matrix, which can be used with polliwog.transform.apply_transform().

Return type

np.ndarray

polliwog.transform.viewport_transform(x_right, y_bottom, x_left=0, y_top=0, inverse=False)

Create a matrix which transforms from the normalized view volume to screen coordinates, with a depth value ranging from 0 in front to 1 in back.

No clipping is performed.

Parameters
  • x_right (int) – The x coordinate of the right of the viewport. (usually the width).

  • y_bottom (int) – The y coordinate of the bottom of the viewport (usually the height).

  • x_left (int) – The x coordinate of the left of the viewport (usually zero).

  • y_top (int) – The y coordinate of the top of the viewport (usually zero).

  • inverse (bool) – When True, return the inverse transform instead.

Returns

The 4x4 transformation matrix, which can be used with polliwog.transform.apply_transform().

Return type

np.ndarray

polliwog.transform.world_to_canvas_orthographic_projection(width, height, position, target, zoom=1, inverse=False)

Create a transformation matrix which composes camera, orthographic projection, and viewport transformations into a single operation.

Parameters
  • width (float) – Width of the window, in pixels. (FIXME: Is this really correct?)

  • height (float) – Height of the window, in pixels. (FIXME: Is this really correct?)

  • position (np.ndarray) – The camera’s position in world coordinates.

  • target (np.ndarray) – The camera’s target in world coordinates. target - position is the “look at” vector.

  • inverse (bool) – When True, return the inverse transform instead.

Returns

The 4x4 transformation matrix, which can be used with polliwog.transform.apply_transform().

Return type

np.ndarray

polliwog.transform.world_to_view(position, target, up=array([0., 1., 0.]), inverse=False)

Create a transform matrix which sends world-space coordinates to view-space coordinates.

Parameters
  • position (np.ndarray) – The camera’s position in world coordinates.

  • target (np.ndarray) – The camera’s target in world coordinates. target - position is the “look at” vector.

  • up (np.ndarray) – The approximate up direction, in world coordinates.

  • inverse (bool) – When True, return the inverse transform instead.

Returns

The 4x4 transformation matrix, which can be used with polliwog.transform.apply_transform().

Return type

np.ndarray

Lines

class polliwog.Line(point, along, assume_normalized=False)
intersect_line(other)

Find the intersection with another line.

project(points)

Project a given point (or stack of points) to the plane.

property reference_points

Return two reference points on the line.

polliwog.line.coplanar_points_are_on_same_side_of_line(a, b, p1, p2)

Test if the given points are on the same side of the given line.

Parameters
  • a (np.arraylike) – The first 3D point of interest.

  • b (np.arraylike) – The second 3D point of interest.

  • p1 (np.arraylike) – A first point which lies on the line of interest.

  • p2 (np.arraylike) – A second point which lies on the line of interest.

Returns

True when a and b are on the same side of the line defined by p1 and p2.

Return type

bool

polliwog.line.intersect_2d_lines(p0, q0, p1, q1)

Intersect two lines: (p0, q0) and (p1, q1). Each should be a 2D point.

polliwog.line.intersect_lines(p0, q0, p1, q1)

Intersect two lines in 3d: (p0, q0) and (p1, q1). Each should be a 3D point.

polliwog.line.project_point_to_line(points, reference_points_of_lines, vectors_along_lines)

Project a point to a line, or pairwise project a stack of points to a stack of lines.

Line segments

polliwog.segment.closest_point_of_line_segment(points, start_points, segment_vectors, ret_t_values=False)

Compute pairwise the point on each line segment that is nearest to the corresponding query point.

polliwog.segment.subdivide_segment(p1, p2, num_points, endpoint=True)

For two points in n-space, return an np.ndarray of equidistant partition points along the segment determined by p1 & p2.

The total number of points returned will be n_samples. When n_samples is 2, returns the original points.

When endpoint is True, p2 is the last point. When false, p2 is excluded.

Partition order is oriented from p1 to p2.

Parameters
  • p1 – 1 x N vectors

  • p2 – 1 x N vectors

  • partition_size – size of partition. should be >= 2.

polliwog.segment.subdivide_segments(v, num_subdivisions=5)
params:
v:

V x N np.array of points in N-space

partition_size:

how many partitions intervals for each segment?

Fill in the line segments determined by v with equally spaced points - the space for each segment is determined by the length of the segment and the supplied partition size.

Boxes

class polliwog.Box(origin, size)

An axis-aligned cuboid or rectangular prism. It’s defined by an origin point, which is its minimum point in each dimension, and non-negative size (length, width, and depth).

Parameters
  • origin (np.arraylike) – The x, y, and z coordinate of the origin, the minimum point in each dimension.

  • size (np.arraylike) – An array containing the width (dx), height (dy), and depth (dz), which must be non-negative.

property center_point

The box’s geometric center.

contains(point, atol=None)

Test whether the box contains the given point. When atol is provided, returns True for points inside the box and points whose coordinates are all within atol of the box boundary.

property depth

The box’s depth. Same as max_z - min_z.

property floor_point

The center of the side of the box having the minimum y coordinate. This is center_point projected to the the level of min_y.

classmethod from_points(points)

The smallest box which spans the given points.

Parameters

points (np.arraylike) – A kx3 array of points.

Returns

The smallest box which spans the given points.

Return type

Box

property height

The box’s height. Same as max_y - min_y.

property max_x

The box’s maximum x coordinate.

property max_x_plane

The plane facing the inside of the box, aligned with its maximum x coordinate.

property max_y

The box’s maximum y coordinate.

property max_y_plane

The plane facing the inside of the box, aligned with its maximum y coordinate.

property max_z

The box’s maximum z coordinate.

property max_z_plane

The plane facing the inside of the box, aligned with its maximum z coordinate.

property mid_x

The x coordinate of the box’s center.

property mid_y

The y coordinate of the box’s center.

property mid_z

The z coordinate of the box’s center.

property min_x

The box’s minimum x coordinate.

property min_x_plane

The plane facing the inside of the box, aligned with its minimum x coordinate.

property min_y

The box’s minimum y coordinate.

property min_y_plane

The plane facing the inside of the box, aligned with its minimum y coordinate.

property min_z

The box’s minimum z coordinate.

property min_z_plane

The plane facing the inside of the box, aligned with its minimum z coordinate.

property ranges

Ranges for each coordinate axis as a 3x2 np.ndarray.

property surface_area

The box’s surface area.

property v

Corners of the box as an 8x3 array of coordinates.

property volume

The box’s volume.

property width

The box’s width. Same as max_x - min_x.

Point clouds

Functions for working with point clouds (i.e. unstructured sets of 3D points).

polliwog.pointcloud.extent(points, ret_indices=False)

Find the distance between the two farthest-most points.

Parameters
  • points (np.arraylike) – A kx3 stack of points.

  • ret_indices (bool) – When True, return the indices along with the distance.

Returns

With ret_indices=False, the distance; with ret_indices=True a tuple (distance, first_index, second_index).

Return type

object

Note

This is implemented using a brute-force method.

polliwog.pointcloud.percentile(points, axis, percentile)

Given a cloud of points and an axis, find a point along that axis from the centroid at the given percentile.

Parameters
  • points (np.arraylike) – A kx3 stack of points.

  • axis (np.arraylike) – A 3D vector specifying the direction of interest.

  • percentile (float) – The desired percentile.

Returns

A 3D point at the requested percentile.

Return type

np.ndarray

Tesselated shapes

Functions for creating sets of triangles to model 3D shapes.

These functions have two possible return types:

  • When ret_unique_vertices_and_faces=True, they return a vertex array (with each vertex listed once) and a face array (i.e. an array of triples of vertex indices). This is ideal when using with a mesh library like Lace (https://github.com/lace/lace/) or Trimesh (https://trimsh.org/) or when you care about the topology.

  • When ret_unique_vertices_and_faces=False, they return a flattened array of triangle coordinates with each vertex repeated. This is useful for computation that use flattened triangle coordinates, such as the functions in polliwog.tri.

polliwog.shapes.cube(origin, size, ret_unique_vertices_and_faces=False)

Tesselate an axis-aligned cube. One vertex is origin. The diametrically opposite vertex is size units along +x, +y, and +z.

Parameters
  • origin (np.ndarray) – A 3D point vector containing the point on the prism with the minimum x, y, and z coords.

  • size (float) – The length, width, and height of the cube, which should be positive.

  • ret_unique_vertices_and_faces (bool) – When True return a vertex array containing the unique vertices and an array of faces (i.e. vertex indices). When False, return a flattened array of triangle coordinates.

Returns

  • With ret_unique_vertices_and_faces=True: a tuple containing an 8x3 array of vertices and a 12x3 array of triangle faces.

  • With ret_unique_vertices_and_faces=False: a 12x3x3 matrix of flattened triangle coordinates.

Return type

object

polliwog.shapes.rectangular_prism(origin, size, ret_unique_vertices_and_faces=False)

Tesselate an axis-aligned rectangular prism. One vertex is origin. The diametrically opposite vertex is origin + size.

Parameters
  • origin (np.ndarray) – A 3D point vector containing the point on the prism with the minimum x, y, and z coords.

  • size (np.ndarray) – A 3D vector specifying the prism’s length, width, and height, which should be positive.

  • ret_unique_vertices_and_faces (bool) – When True return a vertex array containing the unique vertices and an array of faces (i.e. vertex indices). When False, return a flattened array of triangle coordinates.

Returns

  • With ret_unique_vertices_and_faces=True: a tuple containing an 8x3 array of vertices and a 12x3 array of triangle faces.

  • With ret_unique_vertices_and_faces=False: a 12x3x3 matrix of flattened triangle coordinates.

Return type

object

polliwog.shapes.triangular_prism(p1, p2, p3, height, ret_unique_vertices_and_faces=False)

Tesselate a triangular prism whose base is the triangle p1, p2, p3. If the vertices are oriented in a counterclockwise direction, the prism extends from behind them.

Parameters
  • p1 (np.ndarray) – A 3D point on the base of the prism.

  • p2 (np.ndarray) – A 3D point on the base of the prism.

  • p3 (np.ndarray) – A 3D point on the base of the prism.

  • height (float) – The height of the prism, which should be positive.

  • ret_unique_vertices_and_faces (bool) – When True return a vertex array containing the unique vertices and an array of faces (i.e. vertex indices). When False, return a flattened array of triangle coordinates.

Returns

  • With ret_unique_vertices_and_faces=True: a tuple containing an 6x3 array of vertices and a 8x3 array of triangle faces.

  • With ret_unique_vertices_and_faces=False: a 8x3x3 matrix of flattened triangle coordinates.

Return type

object

Indices and tables