pooltool.ptmath.utils

Overview

Function

solve_transcendental(f, a, b, tol, max_iter)

Solve transcendental equation f(x) = 0 in interval [a, b] using bisection method

convert_2D_to_3D(array)

Convert a 2D vector to a 3D vector, setting z=0

angle_between_vectors(v1, v2)

Returns angles between [-180, 180]

wiggle(x, val)

Vary a float or int x by +- val according to a uniform distribution

are_points_on_same_side(p1, p2, p3, p4)

Are points p3, p4 are on the same side of the line formed by points p1 and p2?

find_intersection_2D(l1x, l1y, l10, l2x, l2y, l20)

Find the intersection point of two lines in 2D space

cross(u, v)

Compute cross product u x v, where u and v are 3-dimensional vectors

unit_vector_slow(vector, handle_zero)

Returns the unit vector of the vector.

unit_vector(vector, handle_zero)

Returns the unit vector of the vector (just-in-time compiled)

angle(v2, v1)

Returns counter-clockwise angle of projections of v1 and v2 onto the x-y plane

coordinate_rotation(v, phi)

Rotate vector/matrix from one frame of reference to another (3D FIXME)

point_on_line_closest_to_point(p1, p2, p0)

Returns point on line defined by points p1 and p2 closest to the point p0

norm3d(vec)

Calculate the norm of a 3D vector

norm2d(vec)

Calculate the norm of a 2D vector

surface_velocity(rvw, d, R)

Compute velocity of a point on ball’s surface (specified by unit direction vector)

tangent_surface_velocity(rvw, d, R)

Compute velocity tangent to surface at a point on ball’s surface (specified by unit direction vector)

rel_velocity(rvw, R)

Compute velocity of ball’s point of contact with the cloth relative to the cloth

get_u_vec(rvw, phi, R, s)

-

get_slide_time(rvw, R, u_s, g)

-

get_roll_time(rvw, u_r, g)

-

get_spin_time(rvw, R, u_sp, g)

-

get_ball_energy(rvw, R, m)

Get the energy of a ball

is_overlapping(rvw1, rvw2, R1, R2)

-

tip_contact_offset(cue_center_offset, tip_radius, ball_radius)

Calculate the ball contact point offset from the cue tip center offset.

tip_center_offset(tip_center_offset, tip_radius, ball_radius)

Calculate the cue tip center offset from a given contact point offset on the ball.

Functions

pooltool.ptmath.utils.solve_transcendental(f: collections.abc.Callable[[float], float], a: float, b: float, tol: float = 1e-05, max_iter: int = 100) float[source]

Solve transcendental equation f(x) = 0 in interval [a, b] using bisection method

Parameters:
  • f (collections.abc.Callable[[float], float]) -- A function representing the transcendental equation.

  • a (float) -- The lower bound of the interval.

  • b (float) -- The upper bound of the interval.

  • tol (float) -- The tolerance level for the solution. The function stops when the absolute difference between the upper and lower bounds is less than tol.

  • max_iter (int) -- The maximum number of iterations to perform.

Returns:

The approximate root of f within the interval [a, b].

Raises:
  • ValueError -- If f(a) and f(b) have the same sign, indicating no root within the interval.

  • RuntimeError -- If the maximum number of iterations is reached without convergence.

Return type:

float

pooltool.ptmath.utils.convert_2D_to_3D(array: numpy.typing.NDArray[numpy.float64]) numpy.typing.NDArray[numpy.float64][source]

Convert a 2D vector to a 3D vector, setting z=0

Return type:

numpy.typing.NDArray[numpy.float64]

pooltool.ptmath.utils.angle_between_vectors(v1: numpy.typing.NDArray[numpy.float64], v2: numpy.typing.NDArray[numpy.float64]) float[source]

Returns angles between [-180, 180]

Return type:

float

pooltool.ptmath.utils.wiggle(x: float, val: float)[source]

Vary a float or int x by +- val according to a uniform distribution

pooltool.ptmath.utils.are_points_on_same_side(p1, p2, p3, p4) bool[source]

Are points p3, p4 are on the same side of the line formed by points p1 and p2?

Accepts indexable objects. This is a 2D function, but if higher dimensions are provided, that’s ok (only the first two dimensions will be used).

Return type:

bool

pooltool.ptmath.utils.find_intersection_2D(l1x: float, l1y: float, l10: float, l2x: float, l2y: float, l20: float) tuple[float, float][source]

Find the intersection point of two lines in 2D space

The lines are defined by their linear equations in the general form: (l1x)x + (l1y)y + l10 = 0 and (l2x)x + (l2y)y + l20 = 0.

Parameters:
  • l1x (float) -- The coefficient of x in the first line equation.

  • l1y (float) -- The coefficient of y in the first line equation.

  • l10 (float) -- The constant term in the first line equation.

  • l2x (float) -- The coefficient of x in the second line equation.

  • l2y (float) -- The coefficient of y in the second line equation.

  • l20 (float) -- The constant term in the second line equation.

Returns:

A tuple (x, y) representing the intersection point if the lines intersect at a single point. Returns None if the lines are parallel or coincident (no unique intersection).

Return type:

tuple[float, float]

pooltool.ptmath.utils.cross(u: numpy.typing.NDArray[numpy.float64], v: numpy.typing.NDArray[numpy.float64]) numpy.typing.NDArray[numpy.float64][source]

Compute cross product u x v, where u and v are 3-dimensional vectors

(just-in-time compiled)

Return type:

numpy.typing.NDArray[numpy.float64]

pooltool.ptmath.utils.unit_vector_slow(vector: numpy.typing.NDArray[numpy.float64], handle_zero: bool = False) numpy.typing.NDArray[numpy.float64][source]

Returns the unit vector of the vector.

“Slow”, but supports more than just 3D.

Parameters:

handle_zero (bool, False) -- If True and vector = <0,0,0>, <0,0,0> is returned.

Return type:

numpy.typing.NDArray[numpy.float64]

pooltool.ptmath.utils.unit_vector(vector: numpy.typing.NDArray[numpy.float64], handle_zero: bool = False) numpy.typing.NDArray[numpy.float64][source]

Returns the unit vector of the vector (just-in-time compiled)

Parameters:

handle_zero (bool, False) -- If True and vector = <0,0,0>, <0,0,0> is returned.

Return type:

numpy.typing.NDArray[numpy.float64]

Notes

  • Only supports 3D (for 2D see unit_vector_slow)

pooltool.ptmath.utils.angle(v2: numpy.typing.NDArray[numpy.float64], v1: numpy.typing.NDArray[numpy.float64] = np.array([1, 0])) float[source]

Returns counter-clockwise angle of projections of v1 and v2 onto the x-y plane

(just-in-time compiled)

Return type:

float

pooltool.ptmath.utils.coordinate_rotation(v: numpy.typing.NDArray[numpy.float64], phi: float) numpy.typing.NDArray[numpy.float64][source]

Rotate vector/matrix from one frame of reference to another (3D FIXME)

(just-in-time compiled)

Return type:

numpy.typing.NDArray[numpy.float64]

pooltool.ptmath.utils.point_on_line_closest_to_point(p1: numpy.typing.NDArray[numpy.float64], p2: numpy.typing.NDArray[numpy.float64], p0: numpy.typing.NDArray[numpy.float64]) numpy.typing.NDArray[numpy.float64][source]

Returns point on line defined by points p1 and p2 closest to the point p0

Equations from https://mathworld.wolfram.com/Point-LineDistance3-Dimensional.html

Return type:

numpy.typing.NDArray[numpy.float64]

pooltool.ptmath.utils.norm3d(vec: numpy.typing.NDArray[numpy.float64]) float[source]

Calculate the norm of a 3D vector

This is ~10x faster than np.linalg.norm

>>> import numpy as np
>>> from pooltool.ptmath import *
>>> vec = np.random.rand(3)
>>> norm3d(vec)
>>> %timeit np.linalg.norm(vec)
>>> %timeit norm3d(vec)
2.65 µs ± 63 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
241 ns ± 2.57 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
Return type:

float

pooltool.ptmath.utils.norm2d(vec: numpy.typing.NDArray[numpy.float64]) float[source]

Calculate the norm of a 2D vector

This is faster than np.linalg.norm

Return type:

float

pooltool.ptmath.utils.surface_velocity(rvw: numpy.typing.NDArray[numpy.float64], d: numpy.typing.NDArray[numpy.float64], R: float) numpy.typing.NDArray[numpy.float64][source]

Compute velocity of a point on ball’s surface (specified by unit direction vector)

Return type:

numpy.typing.NDArray[numpy.float64]

pooltool.ptmath.utils.tangent_surface_velocity(rvw: numpy.typing.NDArray[numpy.float64], d: numpy.typing.NDArray[numpy.float64], R: float) numpy.typing.NDArray[numpy.float64][source]

Compute velocity tangent to surface at a point on ball’s surface (specified by unit direction vector)

Return type:

numpy.typing.NDArray[numpy.float64]

pooltool.ptmath.utils.rel_velocity(rvw: numpy.typing.NDArray[numpy.float64], R: float) numpy.typing.NDArray[numpy.float64][source]

Compute velocity of ball’s point of contact with the cloth relative to the cloth

This vector is non-zero whenever the ball is sliding

Return type:

numpy.typing.NDArray[numpy.float64]

pooltool.ptmath.utils.get_u_vec(rvw: numpy.typing.NDArray[numpy.float64], phi: float, R: float, s: int) numpy.typing.NDArray[numpy.float64][source]
Return type:

numpy.typing.NDArray[numpy.float64]

pooltool.ptmath.utils.get_slide_time(rvw: numpy.typing.NDArray[numpy.float64], R: float, u_s: float, g: float) float[source]
Return type:

float

pooltool.ptmath.utils.get_roll_time(rvw: numpy.typing.NDArray[numpy.float64], u_r: float, g: float) float[source]
Return type:

float

pooltool.ptmath.utils.get_spin_time(rvw: numpy.typing.NDArray[numpy.float64], R: float, u_sp: float, g: float) float[source]
Return type:

float

pooltool.ptmath.utils.get_ball_energy(rvw: numpy.typing.NDArray[numpy.float64], R: float, m: float) float[source]

Get the energy of a ball

Currently calculating linear and rotational kinetic energy. Need to add potential energy if z-axis is freed

Return type:

float

pooltool.ptmath.utils.is_overlapping(rvw1: numpy.typing.NDArray[numpy.float64], rvw2: numpy.typing.NDArray[numpy.float64], R1: float, R2: float) bool[source]
Return type:

bool

pooltool.ptmath.utils.tip_contact_offset(cue_center_offset: numpy.typing.NDArray[numpy.float64], tip_radius: float, ball_radius: float) numpy.typing.NDArray[numpy.float64][source]

Calculate the ball contact point offset from the cue tip center offset.

This function converts the offset of the cue tip’s center (relative to the ball’s center, and normalized by the ball’s radius) into the offset of the contact point on the ball’s surface.

The conversion is based on the geometry of two circles in contact. Since the distance from the ball’s center to the cue tip’s center is (ball_radius + tip_radius) while the ball’s surface is at a distance ball_radius, the contact point lies along the same line scaled by the factor

1 / (1 + tip_radius/ball_radius).

In other words, if (a, b) represent the cue tip center offset, then the ball is struck at

(a, b) / (1 + tip_radius/ball_radius).

Parameters:
  • cue_center_offset (numpy.typing.NDArray[numpy.float64]) -- A 2D vector (e.g., [a, b]) representing the offset of the cue tip center relative to the ball center (normalized by the ball’s radius).

  • tip_radius (float) -- The radius of the cue tip.

  • ball_radius (float) -- The radius of the ball.

Returns:

A 2D vector representing the offset of the contact point on the ball’s surface, normalized by the ball’s radius.

Return type:

NDArray[np.float64]

pooltool.ptmath.utils.tip_center_offset(tip_center_offset: numpy.typing.NDArray[numpy.float64], tip_radius: float, ball_radius: float) numpy.typing.NDArray[numpy.float64][source]

Calculate the cue tip center offset from a given contact point offset on the ball.

This function performs the inverse transformation of tip_contact_offset. Given a 2D contact point offset on the ball’s surface (normalized by the ball’s radius), it computes the corresponding cue tip center offset. Since the cue tip’s center is located an extra tip_radius beyond the ball’s surface, the transformation scales the contact offset by

1 + tip_radius/ball_radius.

Parameters:
  • cue_center_offset -- A 2D vector (e.g., [a, b]) representing the offset of the cue tip center relative to the ball center (normalized by the ball’s radius).

  • tip_radius (float) -- The radius of the cue tip.

  • ball_radius (float) -- The radius of the ball.

Returns:

A 2D vector representing the offset of the cue tip’s center relative to the

ball’s center (normalized by the ball’s radius).

Return type:

NDArray[np.float64]