geoclide.basic module#

Basic geometric objects used across geoclide.

This module implements the elementary objects on which the whole package is built: vectors, points, normals, rays and axis-aligned bounding boxes. All of them can represent either a single element or a set of elements (their components being ndarrays), allowing vectorized calculations.

Key Classes#

Vector

A direction in the three-dimensional space, with the common operators (addition, subtraction, scaling, …).

Point

A position in the three-dimensional space. The subtraction of two points gives a vector.

Normal

A vector perpendicular to a surface at a particular position. It is not necessarily normalized and is transformed differently from a vector.

Ray

A semi-infinite line described by an origin (Point), a direction (Vector) and the parametric range [mint, maxt].

BBox

An axis-aligned bounding box described by its pmin and pmax corner points, supporting union operations and ray intersection tests.

class geoclide.basic.BBox(p1: Point | None = None, p2: Point | None = None)[source]#

Bases: object

Bounding Box

Parameters:
p1Point, optional

Frist point(s) to use to create the bounding box(es)

p2Point, optional

Second point(s) to use to create the bounding box(es)

Methods

common_face(b[, fill_value])

Get the face index/indices which is/are common with one of the face(s) of bounding box(es) b2

common_vertices(b)

Get a list of boolean checking which vertices (self) are common to the bounding box(es) b

intersect(...)

Test if a ray/rays intersect(s) the bounding box(es)

is_inside(p)

Test if point(s) p is/are included in the bounding box(es)

is_intersection(r[, diag_calc])

Test if a ray/rays intersect(s) the bounding box(es)

union(b)

Union with a point/set of points or a bounding box/set of bounding boxes

Examples

>>> import geoclide as gc
>>> p1 = gc.Point(0., 0., 0.)
>>> p2 = gc.Point(1., 1., 1.)
>>> b1 = gc.BBox(p1, p2)
>>> b1
pmin=Point(0.0, 0.0, 0.0), pmax=Point(1.0, 1.0, 1.0)
common_face(b: BBox, fill_value: int | float | None = None) int | float | ndarray | None[source]#

Get the face index/indices which is/are common with one of the face(s) of bounding box(es) b2

The convention of index from face 0 to 5, for +X,-X,+Y,-Y,+Z,-Z:

   |F2|                     |+Y|
|F1|F4|F0|F5|  where ->  |-X|+Z|+X|-Z|
   |F3|                     |-Y|

More information

Parameters:
bBBox

The secondary bounding box(es)

fill_valueinteger, optional

In case there is no common face(s) returns fill_value

Returns:
int or ndarray

Returns the index/indices of the common face(s) or fill_value. In case of an ndarray, it is 1-D

Examples

>>> import geoclide as gc
>>> b1 = gc.BBox(gc.Point(0., 0., 0.), gc.Point(1., 1., 1.))
>>> b2 = gc.BBox(gc.Point(1., 0., 0.), gc.Point(2., 1., 1.))
>>> gc.get_common_face(b1, b2)
0
>>> gc.get_common_face(b2, b1)
1
common_vertices(b: BBox) ndarray[source]#

Get a list of boolean checking which vertices (self) are common to the bounding box(es) b

Parameters:
bBBox

The secondary bounding box(es)

Returns:
ndarray

Returns an array of boolean values indicating if the bounding box(es) vertices are common to the secondary b bounding box(es) vertices

Examples

>>> import geoclide as gc
>>> b0 = gc.BBox(gc.Point(0., 0., 0.), gc.Point(1., 1., 1.))
>>> b1 = gc.BBox(gc.Point(1., 0., 0.), gc.Point(2., 1., 1.))
>>> b0.common_vertices(b1)
array([False,  True,  True, False, False,  True,  True, False])
>>> b1.common_vertices(b0)
array([ True, False, False,  True,  True, False, False,  True])
intersect(r: Ray, diag_calc: bool = False, *, ds_output: Literal[True] = True) Dataset[source]#
intersect(r: Ray, diag_calc: bool = False, *, ds_output: Literal[False]) tuple[float | ndarray, float | ndarray, bool | ndarray]
intersect(r: Ray, diag_calc: bool = False, *, ds_output: bool) Dataset | tuple

Test if a ray/rays intersect(s) the bounding box(es)

There are 3 possibilities:

  • no intersection

  • only 1 intersection (case of ray located initially inside the BBox)

  • 2 intersections

Parameters:
rRay

The ray(s) to use for the intersection test(s)

diag_calcbool, optional

Perform diagonal calculations in case of multiple bounding boxes and rays, the output is a 1-D array instead of a 2-D array where out[i] is calculated using r(i) and bbox(i). The same size for the BBox and Ray objects is required.

ds_outputbool, optional

If True the output is a dataset, else returns a tuple with intersection information variables

Returns:
Dataset or tuple

Xarray dataset containing the intersection information if ds_output is True (see the get_bbox_intersect_dataset function for its variables), else a tuple. Form of the tuple:

  • t0None or float or ndarray

    -> The t ray variable of the first intersection. In case of only 1 intersection it represents nothing. An ndarray is 1-D, or 2-D for a set of rays and a set of bounding boxes.

  • t1None or float or ndarray

    -> The t ray variable of the second intersection. In case of only 1 intersection, t1 becomes the t ray variable of the first intersection. An ndarray is 1-D, or 2-D for a set of rays and a set of bounding boxes.

  • is_intersectionbool or ndarray

    -> If there is at least 1 intersection return True, else False. An ndarray is an ndarray of booleans, 1-D, or 2-D for a set of rays and a set of bounding boxes.

Examples

>>> import geoclide as gc
>>> p1 = gc.Point(0., 0., 0.)
>>> p2 = gc.Point(1., 1., 1.)
>>> b1 = gc.BBox(p1, p2)
>>> b1
pmin=Point(0.0, 0.0, 0.0), pmax=Point(1.0, 1.0, 1.0)
>>> p3 = gc.Point(0.5, 0.5, 0.1)
>>> v1 = gc.Vector(0., 0., 1.)
>>> r1 = gc.Ray(p3, v1)
>>> r1
r(t) = (0.5, 0.5, 0.1) + t*(0.0, 0.0, 1.0) with t ∈ [0,inf[
>>> t0, t1, is_intersection = b1.intersect(r1, ds_output=False)
>>> t0, t1, is_intersection
(0.0, np.float64(0.9000000000000006), True)
>>> r1(t1)
Point(0.5, 0.5, 1.0000000000000007)
is_inside(p: Point) bool | ndarray[source]#

Test if point(s) p is/are included in the bounding box(es)

is_intersection(r: Ray, diag_calc: bool = False) bool | ndarray[source]#

Test if a ray/rays intersect(s) the bounding box(es)

Parameters:
rRay

The ray(s) to use for the intersection test(s)

diag_calcbool

Perform diagonal calculations in case of multiple bounding boxes and rays, the output is a 1-D array instead of a 2-D array where out[i] is calculated using r(i) and bbox(i). The same size for the BBox and Ray objects is required.

Returns:
bool or ndarray

If there is at least 1 intersection returns True, else False. In case of an ndarray, it is an ndarray of booleans, 1-D, or 2-D for a set of rays and a set of bounding boxes.

Examples

>>> import geoclide as gc
>>> p1 = gc.Point(0., 0., 0.)
>>> p2 = gc.Point(1., 1., 1.)
>>> b1 = gc.BBox(p1, p2)
>>> b1
pmin=Point(0.0, 0.0, 0.0), pmax=Point(1.0, 1.0, 1.0)
>>> p3 = gc.Point(0.5, 0.5, 0.1)
>>> v1 = gc.Vector(0., 0., 1.)
>>> r1 = gc.Ray(p3, v1)
>>> r1
r(t) = (0.5, 0.5, 0.1) + t*(0.0, 0.0, 1.0) with t ∈ [0,inf[
>>> b1.is_intersection(r1)
True
union(b: Point | BBox) BBox[source]#

Union with a point/set of points or a bounding box/set of bounding boxes

Parameters:
bPoint or BBox

The point(s) or bounding box(es) to use for the union

Returns:
BBox

The new bounding box(es) after the union

Examples

>>> import geoclide as gc
>>> p1 = gc.Point(0., 0., 0.)
>>> p2 = gc.Point(1., 1., 1.)
>>> p3 = gc.Point(1., 1., 3.)
>>> b1 = gc.BBox(p1, p2)
>>> b1
pmin=Point(0.0, 0.0, 0.0), pmax=Point(1.0, 1.0, 1.0)
>>> b2 = b1.union(p3)
>>> b2
pmin=Point(0.0, 0.0, 0.0), pmax=Point(1.0, 1.0, 3.0)
class geoclide.basic.Normal(x: float | ndarray | Vector | Point | Normal | None = None, y: float | ndarray | None = None, z: float | ndarray | None = None, copy: bool = False)[source]#

Bases: object

Parameters:
xfloat or ndarray or Point or Vector or Normal, optional

The x component(s) of the normal (see notes)

yfloat or ndarray, optional

The y component(s) of the normal. In case of an ndarray, it must be 1-D

zfloat or ndarray, optional

The z component(s) of the normal. In case of an ndarray, it must be 1-D

copybool, optional

If True the given ndarrays are copied, else they are used as they are (see notes)

Methods

length

length_squared

to_numpy

Notes

  • if the parameter x is a 1-D ndarray of size 3 and y and z are None, the values of x, y and z will be equal to respectively x[0], x[1], and x[2]

  • if the parameter x is a 2-D ndarray of shape (n,3) and y and z are None, the values of x, y and z will be equal to respectively x[:,0], x[:,1], and x[:,2]

  • if the parameter x is a Point, Vector or Normal, it will circumvent the y and z parameters and take the components of the Point/Vector/Normal for x, y and z values

  • the x, y and z ndarrays given as parameters are not copied, modifying them afterwards modifies the normal. Use copy=True to get a normal with its own components

Examples

>>> import geoclide as gc
>>> n1 = gc.Normal(0.,0.,1.)
>>> n1
Normal(0.0, 0.0, 1.0)
fmt = '.8f'#
length() float | ndarray[source]#
length_squared() float | ndarray[source]#
to_numpy() ndarray[source]#
class geoclide.basic.Point(x: float | ndarray | Vector | Point | Normal | None = None, y: float | ndarray | None = None, z: float | ndarray | None = None, copy: bool = False)[source]#

Bases: object

Parameters:
xfloat or ndarray or Point or Vector or Normal, optional

The x component(s) of the point (see notes)

yfloat or ndarray, optional

The y component(s) of the point. In case of an ndarray, it must be 1-D

zfloat or ndarray, optional

The z component(s) of the point. In case of an ndarray, it must be 1-D

copybool, optional

If True the given ndarrays are copied, else they are used as they are (see notes)

Methods

to_numpy

Notes

  • if the parameter x is a 1-D ndarray of size 3 and y and z are None, the values of x, y and z will be equal to respectively x[0], x[1], and x[2]

  • if the parameter x is a 2-D ndarray of shape (n,3) and y and z are None, the values of x, y and z will be equal to respectively x[:,0], x[:,1], and x[:,2]

  • if the parameter x is a Point, Vector or Normal, it will circumvent the y and z parameters and take the components of the Point/Vector/Normal for x, y and z values

  • the x, y and z ndarrays given as parameters are not copied, modifying them afterwards modifies the point. Use copy=True to get a point with its own components

Examples

>>> import geoclide as gc
>>> p1 = gc.Point(0.,0.,1.)
>>> p1
Point(0.0, 0.0, 1.0)
fmt = '.8f'#
to_numpy() ndarray[source]#
class geoclide.basic.Ray(o: Point | Ray, d: Vector | None = None, mint: float = 0, maxt: float = inf)[source]#

Bases: object

Definition of ray:

r(t) = o + t*d, where:

  • o is/are the origin point(s) of the ray(s)

  • d is/are the direction(s) of the ray(s)

  • t belongs to stricly positive real numbers

Parameters:
oPoint or Ray

Origin point(s) of the ray(s). If the o parameter is a Ray -> circumvent all the parameters by the ray attributs

dVector

Direction(s) of the ray(s)

mintfloat, optional

The minimum t value

maxtfloat, optional

The maximum t value

Methods

__call__(t)

Solve ray(s) equation(s)

Examples

>>> import geoclide as gc
>>> o = gc.Point(0., 50., 2.)
>>> d = gc.Vector(0.,0.,1.)
>>> r = gc.Ray(o, d, mint=20, maxt=100)
>>> r
r(t) = (0.0, 50.0, 2.0) + t*(0.0, 0.0, 1.0) with t ∈ [20,100[
__call__(t: float | ndarray) Point[source]#

Solve ray(s) equation(s)

Parameters:
tfloat or ndarray

The t rays(s) values(s). The value(s) must lie between mint and maxt. In case of an ndarray, it must be 1-D

Returns:
Point

The result(s) of the equation r(t) = o + t*d

Examples

>>> import geoclide as gc
>>> o = gc.Point(0., 0., 0.)
>>> d = gc.Vector(1., 0., 0.)
>>> r = gc.Ray(o, d)
>>> t = 10.
>>> r(t)
Point(10.0, 0.0, 0.0)
class geoclide.basic.Vector(x: float | ndarray | Vector | Point | Normal | None = None, y: float | ndarray | None = None, z: float | ndarray | None = None, copy: bool = False)[source]#

Bases: object

Parameters:
xfloat or ndarray or Point or Vector or Normal, optional

The x component(s) of the vector (see notes)

yfloat or ndarray, optional

The y component(s) of the vector. In case of an ndarray, it must be 1-D

zfloat or ndarray, optional

The z component(s) of the vector. In case of an ndarray, it must be 1-D

copybool, optional

If True the given ndarrays are copied, else they are used as they are (see notes)

Methods

length

length_squared

to_numpy

Notes

  • if the parameter x is a 1-D ndarray of size 3 and y and z are None, the values of x, y and z will be equal to respectively x[0], x[1], and x[2]

  • if the parameter x is a 2-D ndarray of shape (n,3) and y and z are None, the values of x, y and z will be equal to respectively x[:,0], x[:,1], and x[:,2]

  • if the parameter x is a Point, Vector or Normal, it will circumvent the y and z parameters and take the components of the Point/Vector/Normal for x, y and z values

  • the x, y and z ndarrays given as parameters are not copied, modifying them afterwards modifies the vector. Use copy=True to get a vector with its own components

Examples

>>> import geoclide as gc
>>> v1 = gc.Vector(0.,0.,1.)
>>> v1
Vector(0.0, 0.0, 1.0)
fmt = '.8f'#
length() float | ndarray[source]#
length_squared() float | ndarray[source]#
to_numpy() ndarray[source]#
geoclide.basic.get_bbox_intersect_dataset(bbox: BBox, r: Ray, t0: float | ndarray | None = None, t1: float | ndarray | None = None, is_intersection: bool | ndarray = False, diag_calc: bool = False) Dataset[source]#

Create dataset containing the intersection test information

  • The intersect method return of BBox class gives the t0, t1 and is_intersection inputs of this function

Parameters:
bboxBBox

The bounding box(es) used for the intersection test

rRay

The ray(s) used for the intersection test

t0float or ndarray

The t ray variable of the first intersection. In case of an ndarray, it is 1-D, or 2-D for a set of rays and a set of bounding boxes

t1float or ndarray

The t ray variable of the second intersection. In case of an ndarray, it is 1-D, or 2-D for a set of rays and a set of bounding boxes

is_intersectionbool or ndarray, optional

If there is an intersection returns True, else False. In case of an ndarray, it is an ndarray of booleans, 1-D, or 2-D for a set of rays and a set of bounding boxes

diag_calcbool, optional

This indicates whether diagonal calculations have been performed

Returns:
Dataset

Xarray dataset containing the intersection information.

Key variables included:

  • o: The origin(s) of the ray(s) [xyz]

  • d: The direction(s) of the ray(s) [xyz]

  • mint: The mint attribute of the ray(s)

  • maxt: The maxt attribute of the ray(s)

  • is_intersection: If there is an intersection -> True, else False

  • thit: The t ray variable(s) of the intersection point(s)

  • phit: The intersection point(s) [xyz]

In case of a set of rays and/or a set of bounding boxes, the variables get an extra nrays/nobj dimension.

geoclide.basic.get_common_face(b1: BBox, b2: BBox, fill_value: int | float | None = None) int | float | ndarray | None[source]#

Get the face index/indices of the bounding box(es) b1 which is/are common to the bounding box(es) b2

The convention of index from face 0 to 5, for +X,-X,+Y,-Y,+Z,-Z:

   |F2|                     |+Y|
|F1|F4|F0|F5|  where ->  |-X|+Z|+X|-Z|
   |F3|                     |-Y|

More information

Parameters:
b1BBox

The principal bounding box(es)

b2BBox

The secondary bounding box(es)

fill_valueinteger, optional

In case there is no common face(s) returns fill_value

Returns:
int or ndarray

Returns the index/indices of the common face(s) or fill_value. In case of an ndarray, it is 1-D

Examples

>>> import geoclide as gc
>>> b1 = gc.BBox(gc.Point(0., 0., 0.), gc.Point(1., 1., 1.))
>>> b2 = gc.BBox(gc.Point(1., 0., 0.), gc.Point(2., 1., 1.))
>>> gc.get_common_face(b1, b2)
0
>>> gc.get_common_face(b2, b1)
1
geoclide.basic.get_common_vertices(b1: BBox, b2: BBox) ndarray[source]#

Check which vertices of bounding box(es) b1 are common to the vectices of bounding box(es) b2

Parameters:
b1BBox

The principal bounding box(es)

b2BBox

The secondary bounding box(es)

Returns:
ndarray

Returns an array of boolean values indicating whether the principal bounding box(es) b1 vertices are common to the secondary bounding box(es) b2 vertices. It is 1-D, or 2-D in case of a set of bounding boxes.

Examples

>>> import geoclide as gc
>>> b1 = gc.BBox(gc.Point(0., 0., 0.), gc.Point(1., 1., 1.))
>>> b2 = gc.BBox(gc.Point(1., 0., 0.), gc.Point(2., 1., 1.))
>>> gc.get_common_vertices(b1, b2)
array([False,  True,  True, False, False,  True,  True, False])
>>> gc.get_common_vertices(b2, b1)
array([ True, False, False,  True,  True, False, False,  True])