shape Package

The shape package provides a collection of simple geometric shapes, methods for building arbitrary geometric shapes from functions/points, and methods to load geometry from files. The following sections describe the different shapes currently include in the toolbox. Additional shapes can be created using the shape builders or by sub-classing Shape to define a new shape class.

The basic shapes currently included in the toolbox focus on simple geometries and shapes of particles commonly trapped in optical tweezers. A summary of the different shapes and methods currently included is shown in Fig. 9. The package is split into four main sections: Simple geometric shapes, Shape builders, File loaders and Collections. The shape builders can be used to create arbitrary shapes from sets of vertices or parametric functions. In addition to the simple geometric shapes, some of the shape builder classes define static methods for building other commonly used shapes. More complex shapes can be created in external CAD programs (such as Blender) and loaded using the file loaders. The shape sets can be used to combine shapes or invert geometries.

Graphical (overview) table of contents for shapes package

Fig. 9 Graphical display of the different shape creation methods currently included in the toolbox. Plot titles correspond to shapes classes and static methods of shapes classes. Most methods display the default geometry generated using surf. File loaders show different views of the suzane mesh; Empty is reprsented by a face-less cube; and collections show combinations of cubes.

The design of the shape classes is motivated by the scattering methods. Most scattering methods involve surface integrals, volume integrals or calculation of surface normals. These classes describe geometries with these quantities in mind. The complexity of various scattering simulations can often be reduced when the particle is star shaped, mirror symmetric or rotational symmetric. Consequently, all shapes have methods for querying these properties, although they may not be implemented for certain shapes (check documentation).

Base class

The base class for all OTT shapes is the Shape class. This class defines the interface expected by most of the scattering methods. You will probably only need to use this class directly when implementing complex custom shapes. For simpler custom shapes, see the Shape builders. Additional classes describing additional conditions on shape geometry or helpers for defining commonly used methods can be found in the ott.shapes.mixin package.

Shape

class ott.shape.Shape(varargin)

Shape abstract class for optical tweezers toolbox shapes. Inherits from ott.utils.RotationPositionProp and matlab.mixin.Hetrogeneous.

Properties disregard the rotation/position properties and describe the object in the local coordinates.

Properties
  • position – Location of shape [x, y, z]

  • rotation – Orientation of the particle (3x3 rotation matrix)

Abstract properties
  • maxRadius – Maximum particle radius

  • volume – Particle volume

  • boundingBox – Cartesian coordinate bounding box

  • starShaped – True if the shape is star-shaped

  • xySymmetry – True if shape is xy-mirror symmetric

  • zRotSymmetry – z-axis rotational symmetry of particle

Methods
  • surf – Generate surface visualisation

  • scale – Scale the geometry (uses scaleInternal)

  • voxels – Generate array of voxels or voxel visualisation

  • insideRtp – Determine if Spherical point is inside shape

  • insideXyz – Determine if Cartesian point is inside shape

  • normalsRtp – Calculate normals at surface location

  • normalsXyz – Calculate normals at surface location

  • writeWavefrontObj – write shape to Wavefront OBJ file

  • intersect – Calculate intersection between vectors and surface

  • starRadii – Calculate radii of star shaped particle

  • intersectAll – Calculate intersection between vectors and surface

  • intersectBoundingBox – Calculate intersection with bounding box

  • getBoundingBox – Get the bounding box with transformations applied

  • getBoundingBoxShape – Get a shape representing the bounding box

  • rotate* – Functions for rotating the entity

  • translate* – Functions for translating the entity

  • operator/ – Scale the object (uses scale)

  • operator* – Scale the object (uses scale)

  • operator| – Union operator: creates a new set

  • operator& – Intersection operator: creates a new set

  • operator~ – Inverse operator: creates a new Inverse.

Abstract methods
  • scaleInternal – Scale the geometry (called by times/rdivide)

  • surfInternal – Generate surface visualisation

  • surfPoints – Calculate points for surface integration

  • intersectInternal – Method called by intersect

  • intersectAllInternal – Method called by intersectAll

  • insideRtpInternal – Determine if Spherical point is inside shape

  • insideXyzInternal – Determine if Cartesian point is inside shape

  • normalsRtpInternal – Calculate normals at surface location

  • normalsXyzInternal – Calculate normals at surface location

Supported casts
  • TriangularMesh – Requires cast for PatchMesh

Shape(varargin)

Construct a new shape instance.

This class cannot be instanced directly, use one of the other shape descriptions to create a new shape.

Usage

shape = shape@ott.shape.Shape(…)

Optional named arguments
  • position (3 numeric) – Position of the shape. Default: [0;0;0].

  • rotation (3x3 numeric) – Orientation of the shape. Default: eye(3).

and(a, b)

Create a intersection of two shapes

Usage

shape = shape1 & shape2

getBoundingBox(shape, varargin)

Get bounding box after applying transformations

Usage

bb = shape.getBoundingBox(…)

Optional named arguments
  • origin (enum) – Coordinate origin. ‘local’ or ‘global’.

insideRtp(shape, varargin)

Determine if point is inside the shape (Spherical coordinates)

Usage

b = shape.insideRtp(rtp, …)

Parameters
  • rtp (3xN numeric) – Spherical coordinates.

Optional arguments
  • origin (enum) – Coordinate system origin. Either ‘global’ or ‘local’ for world coordinates or shape coordinates.

insideXyz(shape, varargin)

Determine if point is inside the shape (Cartesian coordinates)

Usage

b = shape.insideXyz(xyz, …)

Parameters
  • xyz (3xN numeric) – Cartesian coordinates.

Optional arguments
  • origin (enum) – Coordinate system origin. Either ‘global’ or ‘local’ for world coordinates or shape coordinates.

intersect(shape, x0, x1, varargin)

Calculate intersection locations and normals.

Any rays that don’t intersect shape result in nans.

Usage

[locs, norms, dist] = shape.intersectAll(shape, vecs, …)

Parameters
  • vecs (3xM Vector) – vectors to intersect. Must be castable to a ott.utils.Vector object.

Returns
  • locs (3xN numeric) – intersections with N locations

  • norms (3xN numeric) – surface normals at N intersections

Optional named arguments
  • origin (enum) – Coordinate origin. ‘local’ or ‘global’.

Additional arguments passed to intersectInternal.

isosurface(shape, varargin)

Generate an isosurfrace for the shape from the voxel data

This method is more intensive than the surf method and often less accurate but provides a useful alternative when the shape doesn’t directly support a surf method.

Usage

FV = shape.isosurface(…)

Optional named parameters
  • samples (3 numeric) – Number of samples per Cartesian axes.

  • visualise (logical) – Show the visualisation. Default: nargout == 0

  • origin (enum) – Coordinate system origin. Either ‘global’ or ‘local’ for world coordinates or shape coordinates. Default: 'global'.

  • axis (handle) – Axes hanlde to place plot in. Default: [], uses gca() when available.

normalsRtp(shape, varargin)

Calculate normals at the specified surface locations

Usage

nxyz = shape.normalsRtp(rtp, …) Result is in Cartesian coordinates.

Parameters
  • rtp (3xN numeric) – Spherical coordinates.

Optional arguments
  • origin (enum) – Coordinate system origin. Either ‘global’ or ‘local’ for world coordinates or shape coordinates.

normalsXyz(shape, varargin)

Calculate normals at the specified surface locations

Usage

nxyz = shape.normalsXyz(xyz, …) Result is in Cartesian coordinates.

Parameters
  • xyz (3xN numeric) – Cartesian coordinates.

Optional arguments
  • origin (enum) – Coordinate system origin. Either ‘global’ or ‘local’ for world coordinates or shape coordinates.

not(shape)

Take the inverse of the shape

Usage

shape = ~shape

or(a, b)

Create a union of two shapes

Usage

shape = shape1 | shape2

surf(shape, varargin)

Generate a visualisation of the shape

Usage

shape.surf(…) Display visualisation of shape(s).

S = shape.surf(…) Returns a array of handles to the generated patches or when no visualisation is enabled, creates a cell array of structures that can be passed to patch.

Optional named parameters
  • axes (handle) – axis to place surface in (default: gca)

  • surfOptions (cell) – options to be passed to patch (default: {})

  • showNormals (logical) – Show surface normals (default: false)

  • origin (enum) – Coordinate origin for drawing. Can be ‘global’ or ‘local’ Default: ‘global’.

  • visualise (logical) – Show the visualisation. Default: true

  • normalScale (numeric) – Scale for normal vectors. Default: 0.1.

Additional parameters passed to surfInternal().

voxels(shape, varargin)

Generate an array of xyz coordinates for voxels inside the shape

Usage

voxels(spacing) shows a visualisation of the shape with circles placed at locations on a Cartesian grid.

xyz = voxels(spacing) returns the voxel locations.

Optional named arguments
  • ‘plotoptions’ Options to pass to the plot3 function

  • ‘visualise’ Show the visualisation (default: nargout == 0)

  • origin (enum) – Coordinate system origin. Either ‘global’ or ‘local’ for world coordinates or shape coordinates. Default: 'global'.

  • axes (handle) – Axes hanlde to place plot in. Default: [], uses gca() when available.

writeWavefrontObj(shape, filename)

Write representation of shape to Wavefront OBJ file

Convert the shape to a TriangularMesh and write it to a file.

Usage

shape.writeWavefrontObj(filename)

Parameters
  • filename (char | string) – Filename for file to write to.

Simple geometric shapes

Cube

class ott.shape.Cube(varargin)

Simple geometric cube. Inherits from Shape.

Properties
  • width – Width of the cube

Additional properties inherited from base.

Cube(varargin)

Construct a cube.

Usage

shape = Cube(width, …) Parameters can be passed as named arguments.

Additional parameters are passed to base.

RectangularPrism

class ott.shape.RectangularPrism(varargin)

Simple geometric rectangular prism. Inherits from Shape.

Properties
  • widths – Widths of each side [x; y; z]

Supported casts
  • ott.shape.Cube – Only if widths all match

Additional properties inherited from base.

RectangularPrism(varargin)

Construct a rectangular base prism

Usage

shape = RectangularPrism(widths, …) Parameters can be passed as named arguments.

Additional parameters are passed to base.

Sphere

class ott.shape.Sphere(varargin)

Spherical particle. Inherits from Shape.

Properties
  • radius – Radius of the sphere

Additional properties inherited from base.

Sphere(varargin)

Construct a new sphere

Usage

shape = Sphere(radius, …)

Additional parameters passed to base class.

Cylinder

class ott.shape.Cylinder(varargin)

A simple cylindrical shape. Inherits from Shape, mixin.AxisymStarShape and mixin.IsosurfSurfPoints.

Properties
  • radius – Radius of the cylinder

  • height – Height of the cylinder

Cylinder(varargin)

Construct a new cylinder

Usage

shape = Cylinder(radius, height, …) Parameters can also be passed as named arguments.

Additional parameters passed to base Shape.

Ellipsoid

class ott.shape.Ellipsoid(varargin)

Ellipsoid shape

Properties:
  • radii – Radii along Cartesian axes [X; Y; Z]

Supported casts
  • ott.shape.Spehre – Only works when ellipsoid is a sphere

  • ott.shape.AxisymInterp – Only works when zRotSymmetry == 0

  • ott.shape.PatchMesh

Ellipsoid(varargin)

Construct a new ellipsoid

Usage

shape = Ellipsoid(radii, …) Parameters can be passed as named arguments.

Additional parameters passed to base.

Superellipsoid

class ott.shape.Superellipsoid(varargin)

Superellipsoid shape

In Cartesian coordinates, a superellipsoid is defined by

\[( |x/a|^{2/e} + |y/b|^{2/e} )^{e/n} + |z/c|^{2/n} = 1\]

where \(a,b,c\) are the radii along Cartesian directions, and \(e,n\) are the east-west and north-south smoothness parameters. For more details see https://en.wikipedia.org/wiki/Superellipsoid

Properties
  • radii – Radii along Cartesian axes [X; Y; Z]

  • ew – East-West smoothness (ew = 1 for ellipsoid)

  • ns – North-South smoothness (sw = 1 for ellipsoid)

Superellipsoid(varargin)

Construct a new superellipsoid

Usage

shape = Superellipsoid(radii, ew, ns, …)

Parameters
  • radii (3 numeric) – Radii along Cartesian axes.

  • ew (numeric) – East-west smoothness (xy-plane) Default: 0.8

  • ns (numeric) – North-south smoothness (z-axis) Default: 1.2

Plane

class ott.shape.Plane(varargin)

Shape describing a plane with infinite extent Inherits from ott.shape.Shape.

Dependent properties
  • normal – Vector representing surface normal

  • offset – Offset of surface from coordinate origin

Supported casts
  • TriangularMesh – (Inherited) Uses PatchMesh

  • PatchMesh

  • Strata

  • Slab

Plane(varargin)

Construct a new infinite plane

Usage

shape = Plane(normal, …)

Optional named arguments
  • normal (3xN numeric) – Normals to planes, pointing outside. Default: []. Overwrites any values set with rotation.

  • offset (1xN numeric) – Offset of the plane from the position. Default: []. Overwrites any values set with position.

  • position (3xN numeric) – Position of the plane. Default: [0;0;0].

  • rotation (3x3N numeric) – Plane orientations. Default: eye(3).

Slab

class ott.shape.Slab(varargin)

Shape describing a slab with infinite extent in two directions Inherits from ott.shape.Shape.

Properties
  • normal – Vector representing surface normal

  • depth – Depth of the slab

Supported casts
  • TriangularMesh – (Inherited) Uses PatchMesh

  • PatchMesh – Uses Strata

  • Strata

Slab(varargin)

Construct a new infinite slab

Usage

shape = Slab(depth, normal, …)

Optional named arguments
  • depth (N numeric) – Depth of surface. Default: 0.5.

  • normal (3xN numeric) – Normals to planes. Default: []. Overwrites any values set with rotation.

  • position (3xN numeric) – Position of the plane. Default: [0;0;0].

  • rotation (3x3N numeric) – Plane orientations. Default: eye(3).

Strata

class ott.shape.Strata(varargin)

Shape describing a series of stratified interfaces. Inherits from Plane.

This shape describes a series of layered planes. When the number of layers is equal to 2, this object can be converted to a Slab. All points above the first layer are considered to be inside the shape.

Properties
  • normal – Vector representing surface normal

  • offset – Offset of surface from coordinate origin

  • depths – Depth of each layer (must be positive)

Strata(varargin)

Construct a new infinite slab

Usage

shape = Slab(depths, normal, …)

Optional named parameters
  • depths (numeric) – Depth of each layer. Default: [0.2, 0.5].

  • normal (3x1 numeric) – Surface normal. Default: []. Overwrite any value set with rotation.

  • offset (numeric) – Offset of the plane from the position. Default: []. Overwrites any values set with position.

  • position (3x1 numeric) – Position of the plane. Default: [0;0;0].

  • rotation (3x3 numeric) – Plane orientation. Default: eye(3).

Empty

class ott.shape.Empty(varargin)

An empty shape (with no geometry)

The element still inherits from Shape and has position and rotation properties. The object can be visualised with surf, which draws a cube with transparent faces.

This is the default element in an empty Shape array.

Properties
  • volume – Shape has no volume

  • maxRadius – Shape max radius is zero

Methods
  • surf – Draws a hollow cube

  • surfPoints – Returns an empty array

Empty(varargin)

Construct a new empty shape

Usage

shape = Empty(…)

Optional parameters
  • position (3xN numeric) – Position of the shape. Default: [0;0;0].

  • rotation (3x3N numeric) – Orientation of the shape. Default: eye(3).

Shape builders

TriangularMesh

class ott.shape.TriangularMesh(verts, faces, varargin)

Describes a mesh formed by triangular patches.

This class is similar to PatchMesh except the patches must be triangles (described by three vertices).

Properties
  • verts – 3xN matrix of vertex locations

  • faces – 3xN matrix of vertex indices describing faces

  • norms – 3xN matrix of face normal vectors

  • zRotSymmetry – (Can be set) rotational symmetry of shape

  • xySymmetry – (Can be set) mirror symmetry of shape

  • starShaped – (Can be set) if the particle is star shaped

Methods
  • subdivide – Add an extra vertex to the centre of each face

Faces vertices should be ordered so normals face outwards for volume and inside functions to work correctly.

TriangularMesh(verts, faces, varargin)

Construct a new triangular mesh representation

Usage

shape = TriangularMesh(verts, faces)

Parameters
  • verts (3xN numeric) – Vertex locations

  • faces (3xN numeric) – Indices describing faces

Faces vertices should be ordered so normals face outwards for volume and inside functions to work correctly.

Any faces formed by duplicate vertices are removed.

PatchMesh

class ott.shape.PatchMesh(verts, faces, varargin)

A surface resembling Matlab polygon patches.

This surface casts to TriangularMesh for most operations.

Properties
  • verts – (3xN numeric) Array of vertices for forming faces

  • faces – (mxN numeric) Vertex indices for polygons

PatchMesh(verts, faces, varargin)

Construct a new Patch mesh representation

Usage

shape = PatchMesh(verts, faces, …)

Parameters
  • verts (3xN numeric) – Array of vertices for forming faces

  • faces (mxN numeric) – Vertex indices for polygons

AxisymInterp

class ott.shape.AxisymInterp(varargin)

Rotationally symmetric shape described by discrete set of points.

Shape produced when converting this object to a patch uses linear interpolation between points and discrete rotational segments.

Properties
  • points – Discrete points describing surface [rho; z]

Methods
  • boundaryPoints – Calculate points for line integration

  • surfPoints – Calculate points for surface integration

Supported casts
  • PatchMesh

Static methods
  • Bicone – Create a bicone

  • ConeTippedCylinder – Create a cone-tipped cylinder

Volume is computed numerically, may change in future.

Additional properties/methods inherited from base.

AxisymInterp(varargin)

Construct a new rotationally symmetry shape from discrete points

Usage

shape = AxisymInterp(points, …)

Parameters
  • points (2xN numeric) – Array of points in cylindrical coordinates describing shape geometry. [rho; z]

Additional parameters passed to base.

static Bicone(varargin)

Construct a bicone

A Bicone is xy-mirror symmetric and has three vertices, two on the +(ve)/-(ve) axes and one in the mirror symmetric plane.

Usage

shape = AxisymInterp.Bicone(height, radius, …)

Parameters
  • height (numeric) – Total height of the shape (default: 2.0)

  • radius (numeric) – Radius of the cone (default: 1.0)

Additional parameters passed to class constructor.

static ConeTippedCylinder(varargin)

Construct a cone-tipped cylinder

Usage

shape = ConeTippedCylinder(height, radius, coneHeight, …)

Parameters
  • height (numeric) – total height of the shape (default: 2.0)

  • radius (numeric) – radius of cylinder (default: 1.0)

  • coneHeight (numeric) – Height of the cone segment (default: 0.5)

Additional parameters passed to class constructor.

AxisymFunc

class ott.shape.AxisymFunc(varargin)

Rotationally symmetry shape described by a function

Properties
  • func – Function describing surface

  • type – Type of function (radial | angular | axial | axialSym)

  • range – Range of function parameter values (default: [-Inf, Inf])

Methods
  • surf – Visualise the shape (via PatchMesh)

Static methods
  • BiconcaveDisc – Create a biconcave disk shape

  • Pill – Create a pill tipped shaped cylinder

Supported casts
  • AxisymInterp

  • PatchMesh – Via AxisymInterp

AxisymFunc(varargin)

Construct a new rotationally symmetric shape from a function

Usage

shape = AxisymFunc(func, type, …)

Parameters
  • func (function_handle) – A function handle describing the shape surface. The function should take a single vectorised argument. The argument will depend on the type: radial: z, angular: theta, axial: r

  • type (enum) – Type of function. Can either be ‘angular’, ‘radial’, ‘axial’ or ‘axialSym’.

Optional named arguments
  • range (2 numeric) – Range of function parameter values. Default: [-Inf, Inf] (radial), [-pi, pi] (angular), and [0, Inf] (axial/axialSym).

Additional parameters passed to base.

static BiconcaveDisc(varargin)

Construct a biconcave disc shape

This shape can be used to model cells such as unstressed Red Blood Cells. It implements the function:

z(r) = D \sqrt{1 - \frac{4r^2}{D^2}} \left(a_0 +
    \frac{a_1 r^2}{D^2} + \frac{a_2 r^4}{D^4} \right)

where \(D\) is the particle diameter and \(a\) are shape coefficients.

Usage

shape = AxisymFunc.BiconcaveDisc(radius, coefficients, …)

Parameters
  • radius (numeric) – Radius of disc. Default: 7.82.

  • coefficients (3 numeric) – Coefficients describing shape. Default: [0.0518, 2.0026, -4.491].

Additional parameters are passed to shape constructor.

static Pill(varargin)

Construct a pill-shaped particle

Constructs a cylindrical shaped rod with spherical end caps.

Usage

shape = AxisymFunc.Pill(height, radius, capRadius, …)

Parameters
  • height (numeric) – Total height of pill including end caps. Default: 2.0.

  • radius (numeric) – Radius of rod segment. Default: 0.5.

  • capRadius (numeric) – Radius of end cap. Default: radius. If capRadius is greater than radius, adds a sharp edge at the intersection of the cap and the rod. If radius is less, adds a flat end-cap.

Additional parameters are passed to class constructor.

File loaders

For more complex shapes, it is often more convinent to work with a dedicated computer aided design (CAD) program such as Blender. The toolbox currently includes two types of commonly used CAD formats: STL and Wavefront OBJ.

StlLoader

class ott.shape.StlLoader(filename, varargin)

Load a shape from a STL file. Inherits from TriangularMesh.

Properties
  • filename – Name of the file this object loaded

Additional methods and properties inherited from base class.

This class uses a 3rd party STL file reader: https://au.mathworks.com/matlabcentral/fileexchange/22409-stl-file-reader See tplicenses/stl_EricJohnson.txt for information about licensing.

See also StlLoader, ott.shape.TriangularMesh, ott.shape.WavefrontObj.

StlLoader(filename, varargin)

Construct a new shape from a STL file

Usage

shape = StlLoader(filename, …) Loads the face and vertex information contained in the file.

Only supports binary STL files.

This function uses 3rd party code, see tplicenses/stl_EricJohnson.txt for licensing information.

ObjLoader

class ott.shape.ObjLoader(filename)

Load a shape from a Wavefront OBJ file. Inherits from TriangularMesh.

Properties
  • filename – Filename for loaded OBJ file

The file format is described at https://en.wikipedia.org/wiki/Wavefront_.obj_file

ObjLoader(filename)

Construct a new shape from a Wavefront OBJ file

Loads the face and vertex information contained in the file. Faces are converted to triangles.

Usage

shape = ObjLoader(filename)

Collections

These classes can be used for combining or modifying existing geometry. For example, to combine two shapes so that all points inside either shape are considered inside the combined shape, use a union:

shape = shape1 | shape2;

Or, to create a shape from the intersection of two shapes, use:

shape = shape1 & shape2;

To invert the inside/outside of a shape, use the inverse:

shape = ~shape;

Set

class ott.shape.Set(shapes, varargin)

Collection of shapes Inherits from Shape.

This is the base class for collections of shapes including Union and Intersection.

Properties
  • shapes – Shapes forming the set

  • volume – Calculated numerically

  • starShaped – Variable, default false

  • xySymmetry – Variable, default false

  • zRotSymmetry – Variable, default 1

Set(shapes, varargin)

Construct a new shape set

This is the abstract constructor for shape sets. Use Union or Intersection for instances.

Usage

shape = shape@ott.shape.Set(shapes, …)

Stores shapes and passes optional arguments to base.

Union

class ott.shape.Union(varargin)

Represents union between two shapes (| operator). Inherits from Set.

Properties
  • shapes – Shapes forming the set

  • maxRadius – Estimated from bounding box

  • volume – Calculated numerically

  • boundingBox – Surrounding all shapes

Methods
  • operator| – (Overloaded) Allow daisy chains

Union(varargin)

Construct a union shape set.

Usage

shape = Union(shapes, …)

All parameters are passed to base class.

Intersection

class ott.shape.Intersection(varargin)

Represents intersection between two shapes (& operator). Inherits from Set.

Properties
  • shapes – Shapes forming the set

  • maxRadius – Estimated from bounding box

  • volume – Calculated numerically

  • boundingBox – Surrounding intersection

Methods
  • operator& – (Overloaded) Allow daisy chains

Intersection(varargin)

Construct a intersection shape set.

Usage

shape = Intersection(shapes, …)

All parameters are passed to base class.

Inverse

class ott.shape.Inverse(internal, varargin)

Inverts the geometry of a shape. Inherits from Shape.

Properties
  • internal – Internal shape that is inversed

  • volume – If volume was finite, makes it infinite

  • maxRadius – If maxRadius was finite, makes it infinite

Methods
  • operator~ – Smart inverse

Inverse(internal, varargin)

Construct a new inverse shape

Usage

shape = Inverse(internal, …)

Additional parameters passed to base.