Geoclide package#

Geoclide#

A python package for geometric calculations in the three-dimensional Euclidean space.

Provides
  1. Basic geometric objects: vectors, points, normals, rays and bounding boxes

  2. Geometric transformations: translations, scales and rotations

  3. Ray intersection tests with shapes: spheres, spheroids, disks, triangles and triangle meshes

How to use the documentation#

Documentation is available in two forms: docstrings provided with the code, and a standalone reference guide, available from the geoclide homepage.

Code snippets are indicated by three greater-than signs:

>>> import geoclide as gc
>>> v = gc.Vector(0., 0., 1.)

Use the built-in help function to view a function’s docstring:

>>> help(gc.calc_intersection)
class geoclide.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.Disk(radius: float, inner_radius: float = 0.0, phi_max: float = 360.0, z_height: float = 0.0, otw: Transform | None = None, wto: Transform | None = None)[source]#

Bases: Shape

Creation of the class Disk

Parameters:
radiusfloat

The disk radius

inner_radiusfloat, optional

The inner radius (case of annulus)

phi_maxfloat, optional

The maximum phi value in degrees of the disk/annulus, where phi is between 0 and 360°

z_heightfloat, optional

the disk height along the z axis

otwTransform, optional

From object to world space or the transformation applied to the spheroid

wtoTransform, optional

From world to object space or the in inverse transformation applied to the spheroid

Methods

area()

compute the disk / annulus area

intersect(r[, ds_output])

Test if a ray/set of rays intersects the disk

is_intersection(r)

Test if a ray/set of rays intersects the disk

is_intersection_t(r)

Test if a ray/set of rays intersects the disk

plot(**kwargs)

Plot the disk

to_trianglemesh([reso])

Convert the disk to a triangle mesh

Notes

Even if z_height is given, the origin for rotation transformation do not change. For exemple: z_height=5 then we apply a rotation of 90 degrees around the y axis, the disk we be rotated from (0.,0.,0.), meaning the disk we be moved from position (0.,0.,5.) to (5.,0.,0.).

area() float[source]#

compute the disk / annulus area

Warning

the scale transformation is not considered for the area calculation!

intersect(r: Ray, ds_output: bool = True) Dataset | tuple[source]#

Test if a ray/set of rays intersects the disk

Parameters:
rRay

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

ds_outputbool, optional

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

Returns:
Dataset or tuple

Xarray dataset containing the intersection information if ds_output is True (see the get_intersect_dataset function of the shapes module for its variables), else a tuple ready to be an input for that same function

Examples

>>> import geoclide as gc
>>> r1 = gc.Ray(gc.Point(1.2,0.,10.), gc.Vector(0.,0.,-1.))
>>> annulus = gc.Disk(radius=1.5, inner_radius=0.8)
>>> # hit point is between the inner radius and radius
>>> ds = annulus.intersect(r1)
>>> ds['thit'].values
array(10.)
>>> ds['phit'].values # the intersection point
array([1.2, 0. , 0. ])
is_intersection(r: Ray) bool | ndarray[source]#

Test if a ray/set of rays intersects the disk

Parameters:
rRay

The ray(s) to use for the intersection test

Returns:
bool or ndarray

If there is an intersection -> True, else False. In case of an ndarray, it is a 1-D ndarray of booleans

Examples

>>> import geoclide as gc
>>> r1 = gc.Ray(gc.Point(1.2,0.,10.), gc.Vector(0.,0.,-1.))
>>> r2 = gc.Ray(gc.Point(0.2,0.,10.), gc.Vector(0.,0.,-1.))
>>> r3 = gc.Ray(gc.Point(1.6,0.,10.), gc.Vector(0.,0.,-1.))
>>> annulus = gc.Disk(radius=1.5, inner_radius=0.8)
>>> # hit point is between the inner radius and radius
>>> annulus.is_intersection(r1)
True
>>> # the ray passes through the annulus hole, no intersection
>>> annulus.is_intersection(r2)
False
>>> # the ray passes outside, no intersection
>>> annulus.is_intersection(r3)
False
is_intersection_t(r: Ray) tuple[float | ndarray | None, bool | ndarray][source]#

Test if a ray/set of rays intersects the disk

Parameters:
rRay

The ray(s) to use for the intersection test

Returns:
thitfloat or ndarray

The t ray variable(s) for its first intersection at the shape surface. In case of an ndarray, it is 1-D

is_intersectionbool or ndarray

If there is an intersection -> True, else False. In case of an ndarray, it is a 1-D ndarray of booleans

Examples

>>> import geoclide as gc
>>> r1 = gc.Ray(gc.Point(1.2,0.,10.), gc.Vector(0.,0.,-1.))
>>> r2 = gc.Ray(gc.Point(0.2,0.,10.), gc.Vector(0.,0.,-1.))
>>> r3 = gc.Ray(gc.Point(1.6,0.,10.), gc.Vector(0.,0.,-1.))
>>> annulus = gc.Disk(radius=1.5, inner_radius=0.8)
>>> # hit point is between the inner radius and radius
>>> annulus.is_intersection_t(r1)
(10.0, True)
>>> # the ray passes through the annulus hole, no intersection
>>> annulus.is_intersection_t(r2)
(None, False)
>>> # the ray passes outside, no intersection
>>> annulus.is_intersection_t(r3)
(None, False)
plot(**kwargs)[source]#

Plot the disk

  • The disk is first converted to a triangle mesh then the TriangleMesh plot method is used

Parameters:
**kwargs

The keyword arguments are passed on to the TriangleMesh plot method

to_trianglemesh(reso: int | None = None) TriangleMesh[source]#

Convert the disk to a triangle mesh

Parameters:
resoint, optional

The number of lines around the polar phi angle, minimum accepted value is 3

Returns:
TriangleMesh

The disk converted to a triangle mesh

class geoclide.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.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.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.Sphere(radius: float, z_min: float | None = None, z_max: float | None = None, phi_max: float = 360.0, otw: Transform | None = None, wto: Transform | None = None)[source]#

Bases: Shape

Creation of the class Sphere

  • without transformation the sphere is centered at the origin

  • z0, z1 and phi_max are needed parameters for the creation of any partial sphere

Parameters:
radiusfloat

The radius of the sphere

z_minfloat, optional

The minimum z value of the sphere where z0 is between [-radius, 0]

z_maxfloat, optional

The maximum z value of the sphere where z1 is between [0, radius]

phi_maxfloat, optional

The maximum phi value in degrees of the sphere, where phi is between 0 and 360°

otwTransform, optional

From object to world space or the transformation applied to the sphere

wtoTransform, optional

From world to object space or the in inverse transformation applied to the sphere

Methods

area()

compute the sphere / partial sphere area

intersect(r[, ds_output])

Test if a ray/set of rays intersects the sphere/partial sphere

is_intersection(r)

Test if a ray/set of rays intersects the sphere / partial sphere

is_intersection_t(r)

Test if a ray/set of rays intersects the sphere/partial sphere

plot(**kwargs)

Plot the sphere

to_trianglemesh([reso_theta, reso_phi])

Convert the sphere to a triangle mesh

area() float[source]#

compute the sphere / partial sphere area

Warning

the scale transformation is not considered for the area calculation!

intersect(r: Ray, ds_output: bool = True) Dataset | tuple[source]#

Test if a ray/set of rays intersects the sphere/partial sphere

Parameters:
rRay

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

ds_outputbool, optional

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

Returns:
Dataset or tuple

Xarray dataset containing the intersection information if ds_output is True (see the get_intersect_dataset function of the shapes module for its variables), else a tuple ready to be an input for that same function

Examples

>>> import geoclide as gc
>>> sph1 = gc.Sphere(radius=1.) # sphere of radius 1
>>> # partial sphere where portion above z=0.5 is removed
>>> sph2 = gc.Sphere(radius=1., z_max=0.5)
>>> r = gc.Ray(o=gc.Point(-2., 0., 0.8), d=gc.Vector(1.,0.,0.))
>>> ds = sph1.intersect(r)
>>> ds['phit'].values # the intersection point
array([-0.6,  0. ,  0.8])
>>> # The surface normal at the intersection point
>>> ds['nhit'].values
array([-0.6,  0. ,  0.8])
>>> # here no intersection since the sphere part above z=0.5 is
>>> # removed
>>> ds2 = sph2.intersect(r)
>>> ds2['is_intersection'].values
array(False)
is_intersection(r: Ray) bool | ndarray[source]#

Test if a ray/set of rays intersects the sphere / partial sphere

Parameters:
rRay

The ray(s) to use for the intersection test

Returns:
bool or ndarray

If there is an intersection -> True, else False. In case of an ndarray, it is a 1-D ndarray of booleans

Examples

>>> import geoclide as gc
>>> sph1 = gc.Sphere(radius=1.) # sphere of radius 1
>>> # partial sphere where portion above z=0.5 is removed
>>> sph2 = gc.Sphere(radius=1., z_max=0.5)
>>> r = gc.Ray(o=gc.Point(-2., 0., 0.8), d=gc.Vector(1.,0.,0.))
>>> sph1.is_intersection(r)
True
>>> # here no intersection since the sphere part above z=0.5 is
>>> # removed
>>> sph2.is_intersection(r)
False
is_intersection_t(r: Ray) tuple[float | ndarray | None, bool | ndarray][source]#

Test if a ray/set of rays intersects the sphere/partial sphere

Parameters:
rRay

The ray(s) to use for the intersection test

Returns:
thitfloat or ndarray

The t ray variable(s) for its first intersection at the shape surface. In case of an ndarray, it is 1-D

is_intersectionbool or ndarray

If there is an intersection -> True, else False. In case of an ndarray, it is a 1-D ndarray of booleans

Examples

>>> import geoclide as gc
>>> sph1 = gc.Sphere(radius=1.) # sphere of radius 1
>>> # partial sphere where portion above z=0.5 is removed
>>> sph2 = gc.Sphere(radius=1., z_max=0.5)
>>> r = gc.Ray(o=gc.Point(-2., 0., 0.8), d=gc.Vector(1.,0.,0.))
>>> sph1.is_intersection_t(r)
(1.4000000000000004, True)
>>> # here no intersection since the sphere part above z=0.5 is
>>> # removed
>>> sph2.is_intersection(r)
False
plot(**kwargs)[source]#

Plot the sphere

  • The sphere is first converted to a triangle mesh then the TriangleMesh plot method is used

Parameters:
**kwargs

The keyword arguments are passed on to the TriangleMesh plot method

to_trianglemesh(reso_theta: int | None = None, reso_phi: int | None = None) TriangleMesh[source]#

Convert the sphere to a triangle mesh

Parameters:
reso_thetaint, optional

The number of lines around the polar theta angle, minimum accepted value is 3

reso_phiint, optional

The number of lines around the azimuth phi angle, minimum accepted value is 3

Returns:
TriangleMesh

The sphere converted to a triangle mesh

class geoclide.Spheroid(radius_xy: float, radius_z: float, otw: Transform | None = None, wto: Transform | None = None)[source]#

Bases: Shape

Creation of the class Spheroid

  • without transformation the spheroid is centered at the origin

  • spheroid equation: x/(alpha**2) + y/(alpha**2) + z/(gamma**2) = 1, where alpha = radius_xy and gamma = radius_z

  • prolate -> radius_z > radius_xy

  • oblate -> radius_z < radius_xy

Parameters:
radius_xyfloat

The equatorial radius of the spheroid

radius_zfloat

The pole radius of the spheroid (distance from center to pole along z axis)

otwTransform, optional

From object to world space or the transformation applied to the spheroid

wtoTransform, optional

From world to object space or the in inverse transformation applied to the spheroid

Methods

area()

compute the spheroid area

intersect(r[, ds_output])

Test if a ray/set of rays intersects the spheroid

is_intersection(r)

Test if a ray/set of rays intersects the spheroid

is_intersection_t(r)

Test if a ray/set of rays intersects the spheroid

plot(**kwargs)

Plot the spheroid

to_trianglemesh([reso_theta, reso_phi])

Convert the spheroid to a triangle mesh

area() float[source]#

compute the spheroid area

Warning

the scale transformation is not considered for the area calculation!

intersect(r: Ray, ds_output: bool = True) Dataset | tuple[source]#

Test if a ray/set of rays intersects the spheroid

Parameters:
rRay

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

ds_outputbool, optional

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

Returns:
Dataset or tuple

Xarray dataset containing the intersection information if ds_output is True (see the get_intersect_dataset function of the shapes module for its variables), else a tuple ready to be an input for that same function

Examples

>>> import geoclide as gc
>>> oblate = gc.Spheroid(radius_xy=3., radius_z=1.5)
>>> prolate = gc.Spheroid(radius_xy=1.5, radius_z=3.)
>>> r1 = gc.Ray(
...     o=gc.Point(2.5, 0., 10.), d=(gc.Vector(0., 0., -1.))
... )
>>> r2 = gc.Ray(
...     o=gc.Point(10., 0., 2.5), d=(gc.Vector(-1., 0., 0.))
... )
>>> ds1 = oblate.intersect(r1)
>>> ds1['phit'].values # the intersection point
array([2.5      , 0.       , 0.8291562])
>>> # The surface normal at the intersection point
>>> ds1['nhit'].values
array([ 0.60192927, -0.        ,  0.79854941])
>>> ds2 = prolate.intersect(r2)
>>> ds2['phit'].values
array([0.8291562, 0.       , 2.5      ])
>>> ds2['nhit'].values
array([ 0.79854941, -0.        ,  0.60192927])
is_intersection(r: Ray) bool | ndarray[source]#

Test if a ray/set of rays intersects the spheroid

Parameters:
rRay

The ray(s) to use for the intersection test

Returns:
bool or ndarray

If there is an intersection -> True, else False. In case of an ndarray, it is a 1-D ndarray of booleans

Examples

>>> import geoclide as gc
>>> oblate = gc.Spheroid(radius_xy=3., radius_z=1.5)
>>> prolate = gc.Spheroid(radius_xy=1.5, radius_z=3.)
>>> r1 = gc.Ray(
...     o=gc.Point(2.5, 0., 10.), d=(gc.Vector(0., 0., -1.))
... )
>>> r2 = gc.Ray(
...     o=gc.Point(10., 0., 2.5), d=(gc.Vector(-1., 0., 0.))
... )
>>> oblate.is_intersection(r1)
True
>>> oblate.is_intersection(r2)
False
>>> prolate.is_intersection(r1)
False
>>> prolate.is_intersection(r2)
True
is_intersection_t(r: Ray) tuple[float | ndarray | None, bool | ndarray][source]#

Test if a ray/set of rays intersects the spheroid

Parameters:
rRay

The ray(s) to use for the intersection test

Returns:
thitfloat or ndarray

The t ray variable(s) for its first intersection at the shape surface. In case of an ndarray, it is 1-D

is_intersectionbool or ndarray

If there is an intersection -> True, else False. In case of an ndarray, it is a 1-D ndarray of booleans

Examples

>>> import geoclide as gc
>>> oblate = gc.Spheroid(radius_xy=3., radius_z=1.5)
>>> prolate = gc.Spheroid(radius_xy=1.5, radius_z=3.)
>>> r1 = gc.Ray(
...     o=gc.Point(2.5, 0., 10.), d=(gc.Vector(0., 0., -1.))
... )
>>> r2 = gc.Ray(
...     o=gc.Point(10., 0., 2.5), d=(gc.Vector(-1., 0., 0.))
... )
>>> oblate.is_intersection_t(r1)
(9.170843802411135, True)
>>> oblate.is_intersection_t(r2)
(None, False)
>>> prolate.is_intersection_t(r1)
(None, False)
>>> prolate.is_intersection_t(r2)
(9.170843802411135, True)
plot(**kwargs)[source]#

Plot the spheroid

  • The spheroid is first converted to a triangle mesh then the TriangleMesh plot method is used

Parameters:
**kwargs

The keyword arguments are passed on to the TriangleMesh plot method

to_trianglemesh(reso_theta: int | None = None, reso_phi: int | None = None) TriangleMesh[source]#

Convert the spheroid to a triangle mesh

Parameters:
reso_thetaint, optional

The number of lines around the polar theta angle, minimum accepted value is 3

reso_phiint, optional

The number of lines around the azimuth phi angle, minimum accepted value is 3

Returns:
TriangleMesh

The spheroid converted to a triangle mesh

class geoclide.Transform(m: Transform | ndarray | None = None, m_inv: ndarray | None = None)[source]#

Bases: object

Represents 3D geometric transformation(s) using a 4x4 matrix or ntx4x4 matrix, where nt is the number of transformations

It allows translation, rotation and scalling. It can be applied to vectors, points, normals and rays

Parameters:
mTransform or ndarray, optional

The matrix of the transformation(s), of shape (4, 4) or (nt, 4, 4) for a set of nt transformations

m_invTransform or ndarray, optional

The inverse matrix of the transformation(s), of shape (4, 4) or (nt, 4, 4) for a set of nt transformations

Methods

__call__(...)

Apply the transformations

inverse()

Inverse the transformation(s) matrix

rotate(angle, axis[, diag_calc])

Update the self transformation(s) by adding a rotate transformation(s)

rotate_x(angle)

Update the self transformation(s) by adding a rotate_x transformation(s)

rotate_y(angle)

Update the self transformation(s) by adding a rotate_y transformation(s)

rotate_z(angle)

Update the self transformation(s) by adding a rotate_z transformation(s)

scale(v)

Update the self transformation(s) by adding a scale transformation(s)

translate(v)

Update the self transformation(s) by adding a translate transformation(s)

is_identity

Examples

>>> import geoclide as gc
>>> t1 = gc.Transform()
>>> t1
m=
array(
 [[1. 0. 0. 0.]
 [0. 1. 0. 0.]
 [0. 0. 1. 0.]
 [0. 0. 0. 1.]] )
m_inv=
array(
 [[1. 0. 0. 0.]
 [0. 1. 0. 0.]
 [0. 0. 1. 0.]
 [0. 0. 0. 1.]] )
__call__(c: Vector, diag_calc: bool = False, flatten: bool = False) Vector[source]#
__call__(c: Point, diag_calc: bool = False, flatten: bool = False) Point
__call__(c: Normal, diag_calc: bool = False, flatten: bool = False) Normal
__call__(c: Ray, diag_calc: bool = False, flatten: bool = False) Ray
__call__(c: BBox, diag_calc: bool = False, flatten: bool = False) BBox

Apply the transformations

Parameters:
cVector or Point or Normal or Ray or BBox

The vector(s)/point(s)/normal(s)/ray(s)/bounding box(es) to which the transformation is applied

diag_calcbool, optional

Perform diagonal calculations between c(i) and tranformation(i). The number of transformations must be equal to the number of vectors/points/ …

Returns:
Vector or Point or Normal or Ray or BBox or ndarray

The vector(s)/point(s)/normal(s)/ray(s)/bounding box(es) after the application of the transformation(s). In case of several transformations, it returns a 1-D ndarray of dtype equals to the c parameter type, but if flatten is True returns directly an object of same type as the c parameter.

Examples

>>> import geoclide as gc
>>> t = gc.get_translate_tf(gc.Vector(5., 5., 5.))
>>> p = gc.Point(0., 0., 0.)
>>> t[p]
Point(5.0, 5.0, 5.0)
inverse() Transform[source]#

Inverse the transformation(s) matrix

Parameters:
tTransform

The transformation(s) to be inversed

Returns:
Transform

The inversed transformation(s)

is_identity() bool[source]#
rotate(angle: float | ndarray, axis: Vector | Normal, diag_calc: bool = False) Transform[source]#

Update the self transformation(s) by adding a rotate transformation(s)

Warning

The angle parameter can be a 1-D array only if axis parameter is a Vector/Normal with scalar x, y, z components, or if the parameter diag_calc=True

Parameters:
anglefloat or ndarray

The angle(s) in degrees for the rotation(s). In case of an ndarray, it must be 1-D

axisVector or Normal

The rotation(s) is/are performed around the vector(s)/normal(s) axis/axes

diag_calcbool, optional

Perform diagonal calculations in case angle is a 1-D ndarray and axis is a Vector/Normal with 1-D ndarray x, y, z components. Use angle(i) with axis(i) to calculate transformation(i)

Returns:
Transform

The product of the self transformation(s) and the rotate transformation(s) matrices

rotate_x(angle: float | ndarray) Transform[source]#

Update the self transformation(s) by adding a rotate_x transformation(s)

Parameters:
anglefloat or ndarray

The angle(s) in degrees for the rotation(s) around the x axis. In case of an ndarray, it must be 1-D

Returns:
Transform

The product of the self transformation(s) and the rotate_x transformation(s) matrices

rotate_y(angle: float | ndarray) Transform[source]#

Update the self transformation(s) by adding a rotate_y transformation(s)

Parameters:
anglefloat or ndarray

The angle(s) in degrees for the rotation(s) around the y axis. In case of an ndarray, it must be 1-D

Returns:
Transform

The product of the self transformation(s) and the rotate_y transformation(s) matrices

rotate_z(angle: float | ndarray) Transform[source]#

Update the self transformation(s) by adding a rotate_z transformation(s)

Parameters:
anglefloat or ndarray

The angle(s) in degrees for the rotation(s) around the Z axis. In case of an ndarray, it must be 1-D

Returns:
Transform

The product of the initial transformation(s) and the rotate_z transformation(s) matrices

scale(v: Vector) Transform[source]#

Update the self transformation(s) by adding a scale transformation(s)

Parameters:
vVector

The vector(s) used for scale transformation(s)

Returns:
Transform

The product of the self transformation(s) and the scale transformation(s) matrices

translate(v: Vector) Transform[source]#

Update the self transformation(s) by adding a translate transformation(s)

Parameters:
vVector

The vector(s) used for the transformation(s)

Returns:
Transform

The product of the self transformation(s) and the translate transformation(s)

Examples

>>> import geoclide as gc
>>> t = Transform()
>>> t = t.translate(gc.Vector(5.,0.,0.))
>>> t
m=
array(
 [[1. 0. 0. 5.]
 [0. 1. 0. 0.]
 [0. 0. 1. 0.]
 [0. 0. 0. 1.]] )
m_inv=
array(
 [[ 1.  0.  0. -5.]
 [ 0.  1.  0.  0.]
 [ 0.  0.  1.  0.]
 [ 0.  0.  0.  1.]] )
class geoclide.Triangle(p0: Point | None = None, p1: Point | None = None, p2: Point | None = None, otw: Transform | None = None, wto: Transform | None = None, p0t: Point | None = None, p1t: Point | None = None, p2t: Point | None = None)[source]#

Bases: Shape

Creation of the class Triangle

Parameters:
p0Point

The first point(s) of the triangle(s)

p1Point

The second point(s) of the triangle(s)

p2Point

The the third point(s) of the triangle(s)

otwTransform, optional

From object to world space or the transformation applied to the triangle

wtoTransform, optional

From world to object space or the in inverse transformation applied to the triangle

p0tPoint, optional

If given circumvent the automatically computed p0t (p0 after applying transformation)

p1tPoint, optional

If given circumvent the automatically computed p1t (p1 after applying transformation)

p2tPoint, optional

If given circumvent the automatically computed p2t (p2 after applying transformation)

Methods

area()

compute the area of the triangle

intersect(...)

Test if a ray/set of rays intersect with the triangle(s) and return intersection information

intersect_v2(...)

intersect_v3(...)

is_intersection(r[, method, diag_calc])

Test if a ray/set of rays intersect with the triangle(s)

is_intersection_t(r[, method, diag_calc])

Test if a ray/set of rays intersect with the triangle(s)

is_intersection_v2(r[, diag_calc])

is_intersection_v2_t(r[, diag_calc])

is_intersection_v3(r, diag_calc)

is_intersection_v3_t(r[, diag_calc])

area() float | ndarray[source]#

compute the area of the triangle

Warning

the scale transformation is not considered for the area calculation!

intersect(r: Ray, method: str = 'v3', diag_calc: bool = False, *, ds_output: Literal[True] = True) Dataset[source]#
intersect(r: Ray, method: str = 'v3', diag_calc: bool = False, *, ds_output: Literal[False]) tuple
intersect(r: Ray, method: str = 'v3', diag_calc: bool = False, *, ds_output: bool) Dataset | tuple

Test if a ray/set of rays intersect with the triangle(s) and return intersection information

Parameters:
rRay

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

methodstr, optional

Two choices -> ‘v2’ (use mainly pbrt v2 intersection test method) or ‘v3’ (pbrt v3)

diag_calcbool, optional

Perform diagonal calculations in case Triangle and Ray have ndarray point components, meaning the output is a 1-D array instead of a 2-D array where out[i] is calculated using r(i) and triangle(i). The same size for the Triangle and the Ray is required.

ds_outputbool, optional

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

Returns:
Dataset or tuple

Xarray dataset containing the intersection information if ds_output is True (see the get_intersect_dataset function of the shapes module for its variables), else a tuple ready to be an input for that same function. Form of the tuple:

  • shape_namestr

    -> The shape class name

  • rRay

    -> The ray(s) used for the intersection test

  • tNone or float or ndarray

    -> The t ray variable(s) for its first intersection at the shape surface. An ndarray is 1-D, or 2-D for a set of rays and a set of triangles.

  • is_intersectionbool or ndarray

    -> If there is an 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 triangles.

  • uNone or float or ndarray

    -> The u coordinate(s) of the parametric representation. An ndarray is 1-D, or 2-D for a set of rays and a set of triangles.

  • vNone or float or ndarray

    -> The v coordinate(s) of the parametric representation. An ndarray is 1-D, or 2-D for a set of rays and a set of triangles.

  • dpduNone or ndarray

    -> The surface partial derivative(s) of phit with respect to u. An ndarray is 1-D, or 2-D in case of a set of rays and/or a set of triangles.

  • dpdvNone or ndarray

    -> The surface partial derivative(s) of phit with respect to v. An ndarray is 1-D, or 2-D in case of a set of rays and/or a set of triangles.

  • diag_calcbool

    -> This indicates whether a diagonal calculation has been performed

Notes

By default the ‘v3’ method is used since there are more robustness tests. But the ‘v2’ method is at least twice faster than ‘v3’.

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

Test if a ray/set of rays intersect with the triangle(s)

Parameters:
rRay

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

methodstr, optional

Two choices -> ‘v2’ (use mainly pbrt v2 intersection test method) or ‘v3’ (pbrt v3)

diag_calcbool, optional

Perform diagonal calculations in case Triangle and Ray have ndarray point components, meaning the output is a 1-D array instead of a 2-D array where out[i] is calculated using r(i) and triangle(i). The same size for the Triangle and the Ray is required.

Returns:
bool or ndarray

If there is an intersection -> 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 triangles

is_intersection_t(r: Ray, method: str = 'v3', diag_calc: bool = False) tuple[float | ndarray | None, bool | ndarray][source]#

Test if a ray/set of rays intersect with the triangle(s)

Parameters:
rRay

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

methodstr, optional

Two choices -> ‘v2’ (use mainly pbrt v2 intersection test method) or ‘v3’ (pbrt v3)

diag_calcbool, optional

Perform diagonal calculations in case Triangle and Ray have ndarray point components, meaning the output is a 1-D array instead of a 2-D array where out[i] is calculated using r(i) and triangle(i). The same size for the Triangle and the Ray is required.

Returns:
thitNone or float or ndarray

The t ray variable(s) for its first intersection at the shape surface. In case of an ndarray, it is 1-D, or 2-D for a set of rays and a set of triangles

is_intersectionbool or ndarray

If there is an intersection -> 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 triangles

class geoclide.TriangleMesh(vertices: ndarray, faces: ndarray, otw: Transform | None = None, wto: Transform | None = None)[source]#

Bases: Shape

Creation of the class TriangleMesh

Parameters:
verticesndarray

The vertices xyz coordinates. It is a 2d ndarray of size (nvertices, 3) where the first element is the coordinate of first vertex and so on

facesndarray

The vertices indices of triangles, a 2d ndarray of shape (ntriangles, 3). The 3 first indices are the vertices (p0, p1 and p3) indices of the first triangle and so on

otwTransform, optional

From object to world space or the transformation applied to the triangle mesh

wtoTransform, optional

From world to object space or the in inverse transformation applied to the triangle mesh

Methods

apply_tf(t)

Apply transformation to the triangle mesh

area()

compute the area of the triangle mesh

intersect(...)

Test if a ray/set of rays intersect with the triangle mesh and return intersection information

is_intersection(r[, method, diag_calc, use_loop])

Test if a ray/set of rays intersect with the triangle mesh

is_intersection_t(r[, method, diag_calc, ...])

Test if a ray/set of rays intersect with the triangle mesh

plot([source, savefig_name])

Plot the triangle mesh

to_dataset([name])

Create an xarray dataset where the triangle mesh information are stored

write(path, **kwargs)

Save the mesh

apply_tf(t: Transform) None[source]#

Apply transformation to the triangle mesh

Parameters:
tTranform

The transfomation matrix to apply

area() float[source]#

compute the area of the triangle mesh

Warning

the scale transformation is not considered for the area calculation!

intersect(r: Ray, method: str = 'v3', diag_calc: bool = False, *, ds_output: Literal[True] = True, use_loop: bool = False) Dataset[source]#
intersect(r: Ray, method: str = 'v3', diag_calc: bool = False, *, ds_output: Literal[False], use_loop: bool = False) tuple
intersect(r: Ray, method: str = 'v3', diag_calc: bool = False, *, ds_output: bool, use_loop: bool = False) Dataset | tuple

Test if a ray/set of rays intersect with the triangle mesh and return intersection information

Parameters:
rRay

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

methodstr, optional

Two choices -> ‘v2’ (use mainly pbrt v2 triangle intersection test method) or ‘v3’ (pbrt v3)

diag_calcbool, optional

Perform diagonal calculations between r(i) and triangle(i). The number of triangles must be equal to the number of rays

use_loopbool, optional

If True -> scalar calculations over a loop (instead of using numpy). It can be useful for debugging

ds_outputbool, optional

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

Returns:
Dataset or tuple

Xarray dataset containing the intersection information if ds_output is True (see the get_intersect_dataset function of the shapes module for its variables), else a tuple ready to be an input for that same function. Form of the tuple:

  • shape_namestr

    -> The shape class name

  • rRay

    -> The ray(s) used for the intersection test

  • tNone or float or ndarray

    -> The t ray variable(s) for its first intersection at the shape surface. An ndarray is 1-D.

  • is_intersectionbool or ndarray

    -> If there is an intersection return True, else False. An ndarray is a 1-D ndarray of booleans.

  • uNone or float or ndarray

    -> The u coordinate(s) of the parametric representation. An ndarray is 1-D.

  • vNone or float or ndarray

    -> The v coordinate(s) of the parametric representation. An ndarray is 1-D.

  • dpduNone or ndarray

    -> The surface partial derivative(s) of phit with respect to u. An ndarray is 1-D, or 2-D for a set of rays.

  • dpdvNone or ndarray

    -> The surface partial derivative(s) of phit with respect to v. An ndarray is 1-D, or 2-D for a set of rays.

  • diag_calcbool

    -> This indicates whether a diagonal calculation has been performed

is_intersection(r: Ray, method: str = 'v3', diag_calc: bool = False, use_loop: bool = False) bool | ndarray[source]#

Test if a ray/set of rays intersect with the triangle mesh

Parameters:
rRay

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

methodstr, optional

Two choices -> ‘v2’ (use mainly pbrt v2 triangle intersection test method) or ‘v3’ (pbrt v3)

diag_calcbool, optional

Perform diagonal calculations between r(i) and triangle(i). The number of triangles must be equal to the number of rays

use_loopbool, optional

If True -> scalar calculations over a loop (instead of using numpy). It can be useful for debugging

Returns:
bool or ndarray

If there is an intersection -> True, else False. In case of an ndarray, it is a 1-D ndarray of booleans

is_intersection_t(r: Ray, method: str = 'v3', diag_calc: bool = False, use_loop: bool = False) tuple[float | ndarray | None, bool | ndarray][source]#

Test if a ray/set of rays intersect with the triangle mesh

Parameters:
rRay

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

methodstr, optional

Two choices -> ‘v2’ (use mainly pbrt v2 triangle intersection test method) or ‘v3’ (pbrt v3)

diag_calcbool, optional

Perform diagonal calculations between r(i) and triangle(i). The number of triangles must be equal to the number of rays

use_loopbool, optional

If True -> scalar calculations over a loop (instead of using numpy). It can be useful for debugging

Returns:
thitNone or float or ndarray

The t ray variable(s) for its first intersection at the shape surface. In case of an ndarray, it is 1-D

is_intersectionbool or ndarray

If there is an intersection -> True, else False. In case of an ndarray, it is a 1-D ndarray of booleans

Notes

If use_loop = True, is_intersection_t can be significantly more consuming than is_intersection. Because it does not stop at the first intersection, but it finalize the complete loop to return the thit corresponding to the nearest triangle.

plot(source: str | None = None, savefig_name: str | None = None, **kwargs)[source]#

Plot the triangle mesh

Parameters:
sourcestr

The package used for the plot, only 2 option -> ‘matplotlib’ or ‘trimesh’. If source = None, use matplolib for mesh with ntriangle < 5000, else use trimesh

savefig_namestr, optional

If savefig_name is given, the figure is saved with the given name (only if source=’matplotlib)

**kwargs

All other keyword arguments are passed on to matplotlib plot_trisurf function. For example: alpha, color, shade, … If source = ‘trimesh’ then the keyword arguments passed on to show Trimesh method

Examples

>>> import geoclide as gc
>>> prolate = gc.Spheroid(radius_xy=1.5, radius_z=3.)
>>> msh = prolate.to_trianglemesh()
>>> msh.plot(color='green', edgecolor='k')
to_dataset(name: str = 'none') Dataset[source]#

Create an xarray dataset where the triangle mesh information are stored

Parameters:
namestr, optional

The name of the triangle mesh to be stored

Returns:
Dataset

Xarray dataset containing the triangle mesh information.

Key variables included:

  • obj_names: The name(s) of the mesh(es)

  • vertices: The vertices xyz coordinates [nobj, nvertices, xyz]

  • faces: For each triangle, the indices of its vertices p0, p1 and p2 [nobj, ntriangles, p0p1p2]

write(path: str, **kwargs) None[source]#

Save the mesh

  • if gcnc format use xarray, else use trimesh

Parameters:
pathstr

The xarray to_netcdf path paramter (or trimesh)

**kwargs

The keyword arguments are passed on to xarray to_netcdf or trimesh export method

class geoclide.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.ang2vec(theta: float | ndarray, phi: float | ndarray, vec_view: str = 'zenith', diag_calc: bool = False) Vector[source]#

Convert a direction/directions described by 2 angles/set of 2 angles into a direction/directions described by a vector/vectors

  • direct orthogonal coordinate system where z is pointing upwards

Parameters:
thetafloat or ndarray

The polar angle(s) in degrees, starting at z+ in the zx plane and going in the trigonometric direction around the y axis. In case of an ndarray, it must be 1-D

phifloat or ndarray

The azimuthal angle(s) in degrees, starting at x+ in the xy plane and going in the trigonometric direction around the z axis. In case of an ndarray, it must be 1-D

vec_viewstr, optional

Two choices (concerning initial direction at theta=phi=0): ‘zenith’ (i.e. pointing above) or ‘nadir’ (i.e. pointing below)

diag_calcbool, optional

Perform diagonal calculations, v(i) is calculated using theta(i) and phi(i)

Returns:
Vector

The direction(s) described by a vector

Notes

In case both theta and phi are 1-D ndarrays, the vectors are ordered in phi-major order

Examples

>>> import geoclide as gc
>>> th = 30.
>>> ph = 0.
>>> v1 = gc.ang2vec(theta=th, phi=ph, vec_view='zenith')
>>> v1
Vector(0.49999999999999994, 0.0, 0.8660254037844387)
>>> v2 = gc.ang2vec(theta=th, phi=ph, vec_view='nadir')
>>> v2
Vector(-0.49999999999999994, 0.0, -0.8660254037844387)
geoclide.calc_intersection(shape: BBox | Sphere | Spheroid | Disk | Triangle | TriangleMesh, r: Ray, **kwargs) Dataset[source]#

Performs intersection test between a shape and a ray and returns dataset

Parameters:
shapeBBox, Sphere, Spheroid, Disk, Triangle or TriangleMesh

The shape used for the intersection(s)

rRay

The ray(s) used for the intersection(s)

**kwargs

The keyword arguments are passed on to intersect method. The ds_output parameter is forced here to always be True.

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]

  • nhit: The surface normal(s) at the intersection point(s) [xyz] (not for a BBox)

  • u, v, dpdu, dpdv: The parametric coordinates and surface partial derivatives (not for a BBox)

  • the shape attributes (e.g. radius, z_min, z_max and phi_max for a sphere, or pmin and pmax for a bounding box)

  • wto_m, wto_m_inv, otw_m, otw_m_inv: The transformation matrices of the shape (not for a BBox)

Examples

>>> import geoclide as gc
>>> sphere = gc.Sphere(radius=1.) # sphere of radius 1
>>> bbox = gc.BBox(p1=gc.Point(0., 0., 0.), p2=gc.Point(1.,1.,1.))
>>> ray = gc.Ray(o=gc.Point(-2., 0., 0.8), d=gc.Vector(1.,0.,0.))
>>> ds_sphere = gc.calc_intersection(sphere, ray)
>>> ds_sphere['thit'].values
array(1.4)
>>> ds_sphere['phit'].values
array([-0.6,  0. ,  0.8])
>>> ds_bbox = gc.calc_intersection(bbox, ray)
>>> ds_bbox['thit'].values
array(2.)
>>> ds_bbox['phit'].values
array([0. , 0. , 0.8])
geoclide.clamp(val: float, val_min: float, val_max: float) float[source]#

Clamps val into the range [val_min, val_max]

Parameters:
valfloat

The scalar to be clamped

val_minfloat

The minimum value

val_maxfloat

The maximum value

Returns:
float

The result of the clamp

Examples

>>> import geoclide as gc
>>> gc.clamp(4, val_min=5, val_max=11)
5
geoclide.coordinate_system(v1: Vector, method: str = 'm2') tuple[Vector, Vector][source]#

Create orthogonal coordinate system(s) from a vector/set of vectors

Parameters:
v1Vector

The base vector(s) used to create the orthogonal coordinate system(s)

methodstr, optional

Default is ‘m2’ (method from pbrt v4), other choice is ‘m1’ (pbrt v2 and v3)

Returns:
v2Vector

The second vector(s) of the orthogonal coordinate system(s)

v3Vector

The third vector(s) of the orthogonal coordinate system(s)

Examples

>>> import geoclide as gc
>>> v1 = gc.Vector(0., 0., 1.)
>>> gc.coordinate_system(v1)
(Vector(1.0, -0.0, -0.0), Vector(-0.0, 1.0, -0.0))
geoclide.cross(a: Vector | Normal, b: Vector | Normal) Vector[source]#

The cross product

  • Warning

    The cross product of 2 normals is not allowed

Definition of the cross product:

  • \((a × b) = ((a.y*b.z)-(a.z*b.y))x̂ + ((a.z*b.x)-(a.x*b.z))ŷ + ((a.x*b.y)-(a.y*b.x))\)

  • \((a × b) = ||a||*||b||*sin(θ)\)

    where x̂, ŷ and ẑ are the unitary vectors respectively in axes x, y and z

Parameters:
aVector or Normal

The first vector(s) or normal(s) used for the cross product

bVector or Normal

The second vector(s) or normal(s) used for the cross product

Returns:
Vector

The result(s) of the cross product

Examples

>>> import geoclide as gc
>>> a = gc.Vector(0.,0.,1.)
>>> b = gc.Vector(1.,0.,0.)
>>> gc.cross(a,b)
Vector(0.0, 1.0, 0.0)
geoclide.distance(p1: Point, p2: Point) float | ndarray[source]#

Compute the distance(s) between 2 points/set of points

Parameters:
p1Point

The first point(s)

p2Point

The second point(s)

Returns:
float or ndarray

The distance(s) between the 2 points/set of points. In case of an ndarray, it is 1-D

Examples

>>> import geoclide as gc
>>> p1 = gc.Point(0., 0., 0.)
>>> p2 = gc.Point(0., 0., 10.)
>>> gc.distance(p1,p2)
10.0
>>> p1 = gc.Point(1., 2., 1.9)
>>> p2 = gc.Point(5., 15., 3.)
>>> gc.distance(p1,p2)
13.64587849865299
geoclide.dot(a: Vector | Normal, b: Vector | Normal) float | ndarray[source]#

The dot/scalar product

Definition of the dot product:

  • \((a . b) = a.x*b.x + a.y*b.y + a.z*b.z\)

  • \((a . b) = ||a|| * ||b|| * cos(θ)\)

Parameters:
aVector or Normal

The first vector(s) or normal(s) used for the dot product

bVector or Normal

The second vector(s) or normal(s) used for the dot product

Returns:
float or ndarray

The result(s) of the dot product i.e. sum of products. In case of an ndarray, it is 1-D

Examples

>>> import geoclide as gc
>>> a = gc.Vector(0., 0., 1.)
>>> b = gc.Vector(math.sqrt(2.)/2., 0., math.sqrt(2.)/2.)
>>> gc.dot(a,b)
0.7071067811865476
geoclide.face_forward(a: Vector | Normal, b: Vector | Normal) Vector | Normal[source]#

Flip the vector(s)/normal(s) a if the vector(s)/normal(s) b is/are in the opposite direction(s)

It can be useful to flip a surface normal so that it lies in the same hemisphere as a given vector.

Parameters:
aVector or Normal

The vector(s) or normal(s) to potentially flip

bVector or Normal

The base vector(s) or normal(s) used for the flip

Returns:
Vector or Normal

The potentially flipped vector(s) or normal(s)

Examples

>>> import geoclide as gc
>>> n1 = gc.Normal(1., 0., 0.)
>>> v1 = gc.Vector(-1., 0., 0.)
>>> gc.face_forward(v1, n1)
Vector(1.0, -0.0, -0.0)
geoclide.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.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])
geoclide.get_inverse_tf(t: Transform) Transform[source]#

Get the inverse transformation(s)

Parameters:
tTransform

The transformation(s) to be inversed

Returns:
Transform

The inversed transformation(s)

geoclide.get_rotate_tf(angle: float | ndarray, axis: Vector | Normal, diag_calc: bool = False) Transform[source]#

Get the rotate transformation(s) around a given axis/axes

Warning

The angle parameter can be a 1-D array only if axis parameter is a Vector/Normal with scalar x, y, z components, or if the parameter diag_calc=True

Parameters:
anglefloat or ndarray

The angle(s) in degrees for the rotation(s). In case of an ndarray, it must be 1-D

axisVector or Normal

The rotation(s) is/are performed around the vector(s)/normal(s) axis/axes

diag_calcbool, optional

Perform diagonal calculations in case angle is a 1-D ndarray and axis is a Vector/Normal with 1-D ndarray x, y, z components. Use angle(i) with axis(i) to calculate transformation(i)

Returns:
Transform

The rotate transformation(s)

geoclide.get_rotate_x_tf(angle: float | ndarray) Transform[source]#

Get the rotate_x transformation(s)

Parameters:
anglefloat or ndarray

The angle(s) in degrees for the rotation(s) around the x axis. In case of an ndarray, it must be 1-D

Returns:
Transform

The rotate_x transformation(s)

geoclide.get_rotate_y_tf(angle: float | ndarray) Transform[source]#

Get the rotate_y transformation(s)

Parameters:
anglefloat or ndarray

The angle(s) in degrees for the rotation(s) around the y axis. In case of an ndarray, it must be 1-D

Returns:
Transform

The rotate_y transformation(s)

geoclide.get_rotate_z_tf(angle: float | ndarray) Transform[source]#

Get the rotate_z transformation(s)

Parameters:
anglefloat or ndarray

The angle(s) in degrees for the rotation(s) around the Z axis. In case of an ndarray, it must be 1-D

Returns:
Transform

The rotate_z transformation(s)

geoclide.get_scale_tf(v: Vector) Transform[source]#

Get the scale transformation(s)

Parameters:
vVector

The vector(s) used for scale transformation(s)

Returns:
Transform

The scale transformation(s)

geoclide.get_translate_tf(v: Vector) Transform[source]#

Get the translate transformation(s)

Parameters:
vVector

The vector(s) used for the translate transformation(s)

Returns:
Transform

The translate transformation(s)

Examples

>>> import geoclide as gc
>>> t = gc.get_translate_tf(gc.Vector(5.,0.,0.))
>>> t
m=
array(
 [[1. 0. 0. 5.]
 [0. 1. 0. 0.]
 [0. 0. 1. 0.]
 [0. 0. 0. 1.]] )
m_inv=
array(
 [[ 1.  0.  0. -5.]
 [ 0.  1.  0. -0.]
 [ 0.  0.  1. -0.]
 [ 0.  0.  0.  1.]] )
geoclide.normalize(v: Vector) Vector[source]#
geoclide.normalize(v: Normal) Normal

Normalize a vector/normal or a set of vectors/normals

Parameters:
vVector or Normal

The vector(s) or normal(s) to be normalized

Returns:
Vector or Normal

The normalized vector(s)/normal(s)

Examples

>>> import geoclide as gc
>>> v = gc.Vector(1.,0.,1.)
>>> gc.normalize(v)
Vector(0.7071067811865475, 0.0, 0.7071067811865475)
geoclide.permute(a: Vector | Point | Normal, ix: int | ndarray | list | None = None, iy: int | ndarray | list | None = None, iz: int | ndarray | list | None = None) Vector | Point | Normal[source]#

Permutes the vector(s)/point(s)/normal(s) x, y, z values according to the given indices

Parameters:
aVector or Point or Normal

The vector(s), point(s) or normal(s) used for permutation

ixint or np.ndarray or list, optional

The index/indices of the value(s) we want to keep as a remplacement for the x component(s)

iyint or np.ndarray or list, optional

The index/indices of the value(s) we want to keep as a remplacement for the y component(s)

izint or np.ndarray or list, optional

The index/indices of the value(s) we want to keep as a remplacement for the z component(s)

Returns:
Vector or Point or Normal

The vector(s)/point(s)/normal(s) after the permute operation

Examples

>>> import geoclide as gc
>>> v1 = gc.Vector(2., 3., 1.)
>>> gc.permute(v1, 1, 0, 2)
Vector(3.0, 2.0, 1.0)
>>> import numpy as np
>>> p_set = np.array(
...     [[0.,0.,3.], [1.,0.,0.], [-0.5,0.,0.], [0.,-3.,0.]]
... )
>>> p_set = gc.Point(p_set)
>>> gc.permute(p_set, [2,0,0,1], 1, 2).to_numpy()
array([[ 3. ,  0. ,  3. ],
       [ 1. ,  0. ,  0. ],
       [-0.5,  0. ,  0. ],
       [-3. , -3. ,  0. ]])
geoclide.quadratic(a: ndarray, b: ndarray, c: ndarray) tuple[ndarray, ndarray, ndarray][source]#
geoclide.quadratic(a: float, b: float, c: float) tuple[bool, float | None, float | None]

Resolve the quadratic polynomial: ax**2 + bx + c

  • where x is the quadratic polynomial variable and a, b and c the coefficients

Parameters:
afloat or ndarray

The first coefficient(s) of the quadratic polynomial. In case of an ndarray, it must be 1-D

bfloat or ndarray

The second coefficient(s) of the quadratic polynomial. In case of an ndarray, it must be 1-D

cfloat or ndarray

The third coefficient(s) of the quadratic polynomial. In case of an ndarray, it must be 1-D

Returns:
bbool or ndarray

If the quadratic can be solved return True, else False. In case of an ndarray, it is a 1-D ndarray of booleans

x0float or None or ndarray

The first solution(s). In case of an ndarray, it is 1-D

x1float or None or ndarray

The second solution(s). In case of an ndarray, it is 1-D

Notes

If There are 2 solutions x0 < x1. And if there is only one solution x0 = x1.

Examples

>>> import geoclide as gc
>>> a = 2
>>> b = -5
>>> c = 0
>>> gc.quadratic(a, b, c)
(True, 0.0, 2.5)
geoclide.read_trianglemesh(path: str, **kwargs) TriangleMesh[source]#

Open mesh file

  • if gcnc format use xarray, else use trimesh

Parameters:
pathstr

The xarray filename_or_obj or trimesh file_obj parameter

**kwargs

The keyword arguments are passed on to xarray open_dataset or trimesh load_mesh method

Returns:
TriangleMesh

The triangle mesh

Notes

A file describing several objects, as a glb file or an obj file with several groups, is read as a single triangle mesh gathering all of them.

geoclide.vabs(a: Vector | Point | Normal) Vector | Point | Normal[source]#

Calculate the absolute value(s) of each component of the vector(s)/point(s)/normal(s)

Parameters:
aVector or Point or Normal

The vector(s)/point(s)/normal(s) used

Returns:
Vector or Point or Normal or ndarray

The vector(s)/point(s)/normal(s) with absolute values. In case of an ndarray, it is 1-D

geoclide.vargmax(a: Vector | Point | Normal) int | ndarray[source]#

Get the index/indices of the vector(s)/point(s)/normal(s) components with the largest value(s)

Parameters:
aVector or Point or Normal

The vector(s)/point(s)/normal(s) used

Returns:
int or ndarray

The index/indices of the largest vector(s)/point(s)/normal(s) value(s). In case of an ndarray, it is 1-D

Examples

>>> import geoclide as gc
>>> v1 = gc.Vector(2.,3.,1.)
>>> gc.vargmax(v1)
1
geoclide.vargmin(a: Vector | Point | Normal) int | ndarray[source]#

Get the index/indices of the vector(s)/point(s)/normal(s) components with the smallest value(s)

Parameters:
aVector or Point or Normal

The vector(s)/point(s)/normal(s) used

Returns:
int or ndarray

The index/indices of the smallest vector(s)/point(s)/normal(s) value(s). In case of an ndarray, it is 1-D

Examples

>>> import geoclide as gc
>>> v1 = gc.Vector(2.,3.,1.)
>>> gc.vargmin(v1)
2
geoclide.vec2ang(v: Vector, vec_view: str = 'zenith', acc: float = 1e-06) tuple[float | ndarray, float | ndarray][source]#

Convert a direction/directions described by a vector/vectors into a direction/directions described by 2 angles/set of 2 angles

  • direct orthogonal coordinate system where z is pointing upwards

Parameters:
vVector

The direction(s) described by a vector

vec_viewstr, optional

Two choices (concerning initial direction at theta=phi=0): ‘zenith’ (i.e. pointing above) or ‘nadir’ (i.e. pointing below)

accfloat, optional

The tolerance for numerical errors. Default is 1e-6.

Returns:
thetafloat or ndarray

The polar angle(s) in degrees, starting at z+ in the zx plane and going in the trigonometric direction around the y axis. In case of an ndarray, it is 1-D

phifloat or ndarray

The azimuthal angle(s) in degrees, starting at x+ in the xy plane and going in the trigonometric direction around the z axis. In case of an ndarray, it is 1-D

Examples

>>> import geoclide as gc
>>> th = 30.
>>> ph = 0.
>>> v1 = gc.ang2vec(theta=th, phi=ph, vec_view='zenith')
>>> v1
Vector(0.49999999999999994, 0.0, 0.8660254037844387)
>>> theta, phi = gc.vec2ang(v1, vec_view='zenith')
>>> theta, phi
(29.999999999999993, 0.0)
>>> v2 = gc.ang2vec(theta=th, phi=ph, vec_view='nadir')
>>> v2
Vector(-0.49999999999999994, 0.0, -0.8660254037844387)
>>> theta, phi = gc.vec2ang(v2, vec_view='nadir')
>>> theta, phi
(29.999999999999993, 0.0)
geoclide.vmax(a: Vector | Point | Normal) float | ndarray[source]#

Get the largest components value(s) of the vector(s)/point(s)/normal(s)

Parameters:
aVector or Point or Normal

The vector(s)/point(s)/normal(s) used

Returns:
float or ndarray

The largest vector(s)/point(s)/normal(s) value(s). In case of an ndarray, it is 1-D

Examples

>>> import geoclide as gc
>>> v1 = gc.Vector(2.,3.,1.)
>>> gc.vmax(v1)
3.0
geoclide.vmin(a: Vector | Point | Normal) float | ndarray[source]#

Get the smallest components value(s) of the vector(s)/point(s)/normal(s)

Parameters:
aVector or Point or Normal

The vector(s)/point(s)/normal(s) used

Returns:
float or ndarray

The smallest vector(s)/point(s)/normal(s) value(s). In case of an ndarray, it is 1-D

Examples

>>> import geoclide as gc
>>> v1 = gc.Vector(2.,3.,1.)
>>> gc.vmin(v1)
1.0

Modules#