geoclide.quadrics module#

Quadric shapes.

This module implements the quadric shapes of geoclide. Each shape is placed with an object-to-world transformation and provides ray intersection tests, accepting a single ray or a set of rays, as well as the calculation of its total area.

Key Classes#

Sphere

A full or partial sphere, optionally cut below zmin and above zmax and limited to a maximum azimuthal angle phi_max.

Spheroid

An oblate or prolate spheroid, described by its equatorial (xy) and polar (z) radii.

Disk

A full or partial disk, described by its radius, an optional inner radius (annulus), a z height and a maximum azimuthal angle phi_max.

class geoclide.quadrics.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.quadrics.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.quadrics.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