Regina 7.4 Calculation Engine
|
Represents a specific embedding of a directed knot or link in real 3-dimensional space. More...
#include <link/spatiallink.h>
Public Types | |
using | Node = Vector3D<double> |
Represents a single point on the path that a link component takes through three-dimensional space. | |
using | Component = std::vector<Node> |
Represents a single link component. | |
using | PacketChangeGroup |
A type alias for PacketChangeSpan, used when a span is being used purely for optimisation purposes. | |
Public Member Functions | |
std::shared_ptr< PacketOf< SpatialLink > > | packet () |
Returns the packet that holds this data, if there is one. | |
std::shared_ptr< const PacketOf< SpatialLink > > | packet () const |
Returns the packet that holds this data, if there is one. | |
std::string | anonID () const |
A unique string ID that can be used in place of a packet ID. | |
std::string | str () const |
Returns a short text representation of this object. | |
std::string | utf8 () const |
Returns a short text representation of this object using unicode characters. | |
std::string | detail () const |
Returns a detailed text representation of this object. | |
Constructors and Destructors | |
SpatialLink ()=default | |
Constructs an empty link. | |
SpatialLink (const SpatialLink &)=default | |
Constructs a new copy of the given link. | |
SpatialLink (SpatialLink &&) noexcept=default | |
Moves the given link into this new link. | |
template<typename iterator > | |
SpatialLink (iterator begin, iterator end) | |
Creates a new link whose components are supplied by the given sequences of points in 3-space. | |
SpatialLink (std::initializer_list< std::initializer_list< Node > > components) | |
Creates a new link whose components are given by hard-coded sequences of points in 3-space. | |
Nodes and Components | |
size_t | size () const |
Returns the total number of nodes in this spatial link. | |
bool | isEmpty () const |
Determines whether this link is empty. | |
size_t | countComponents () const |
Returns the number of components in this link. | |
const Component & | component (size_t index) const |
Returns a reference to the component at the given index within this link. | |
auto | components () const |
Returns an object that allows iteration through and random access to all components of this link. | |
size_t | componentSize (size_t componentIndex) const |
Returns the number of nodes that are stored for the given component of this link. | |
const Node & | node (size_t componentIndex, size_t nodeIndex) const |
Returns a particular node belong to a particular component of this link. | |
double | radius () const |
Returns the radius that should be used when rendering this link. | |
void | setRadius (double useRadius) |
Indicates that the given radius should be used when rendering this link. | |
void | clearRadius () |
Removes any user-specified radius to use when rendering this link. | |
bool | hasRadius () const |
Indicates whether the user has set their own custom radius to use when rendering this link. | |
double | defaultRadius () const |
Returns a sensible default radius to use when rendering the link. | |
bool | operator== (const SpatialLink &other) const |
Determines if this link is identical to the given link. | |
std::pair< Node, Node > | range () const |
Returns the range of coordinates that this link occupies. | |
Editing | |
SpatialLink & | operator= (const SpatialLink &src) |
Sets this to be a (deep) copy of the given link. | |
SpatialLink & | operator= (SpatialLink &&src) |
Moves the contents of the given link into this link. | |
void | swap (SpatialLink &other) |
Swaps the contents of this and the given link. | |
void | scale (double factor) |
Scales the entire link by the given factor. | |
void | translate (const Vector3D< double > &vector) |
Translates the entire link by the given vector. | |
void | reflect (int axis=2) |
Reflects the link in plane perpendicular to the given axis. | |
void | refine () |
Adds additional nodes to make the embedding appear smoother. | |
void | refine (int sub) |
Adds a configurable number of additional nodes to make the embedding appear smoother. | |
Exporting Links | |
void | writeTextShort (std::ostream &out) const |
Writes a short text representation of this link to the given output stream. | |
void | writeTextLong (std::ostream &out) const |
Writes a detailed text representation of this link to the given output stream. | |
Protected Attributes | |
PacketHeldBy | heldBy_ |
Indicates whether this Held object is in fact the inherited data for a PacketOf<Held>. | |
Building Links | |
class | XMLSpatialLinkReader |
class | XMLWriter< SpatialLink > |
static SpatialLink | fromKnotPlot (const char *filename) |
Creates a new link from a KnotPlot data file. | |
Represents a specific embedding of a directed knot or link in real 3-dimensional space.
This class SpatialLink is a "purely geometric" representation of a link, as opposed to the Link class which is a "purely combinatorial" representation (holding the combintorics of a 2-dimensional link diagram, with no geometric information at all about the specific placements of strands or crossings).
One caveat with using the SpatialLink class is that it uses floating point arithmetic. This makes it good for visualisation purposes, but makes it susceptible to floating point errors. If you need to perform exact computations (for example, of link invariants), use the Link class instead.
This class supports links with any number of components (including zero). Each component is made up of a non-empty sequence of nodes, which are points in 3-dimensional space represented by objects of type Vector3D. The nodes in each component are connected by straight line segments to form a closed loop.
It is assumed that this indeed forms an embedding (i.e., no two nodes are equal, no node meets any other line segment beyond the two that it sits between on its link component, and no two line segments meet beyond the expect cases of two adjacent segments touching at their common endpoint). This is not checked, and indeed the use of floating point arithmetic makes it difficult to check this precisely. Note that, as a consequence of forming an embedding, each link component must contain at least three nodes.
It is assumed that the underlying coordinate system is right-handed.
Like the regular Link and Triangulation classes, SpatialLink is not a packet type that can be inserted directly into the packet tree. Instead it is a standalone mathematatical object, which makes it slimmer and faster for ad-hoc use. Therefore:
If you are adding new functions to this class that edit the internal data structures of the link, you must remember to surround these changes with a ChangeAndClearSpan. This manages bookkeeping such as clearing computed properties, and (if this link does belong to a packet) firing packet change events.
This class implements C++ move semantics and adheres to the C++ Swappable requirement. It is designed to avoid deep copies wherever possible, even when passing or returning objects by value.
using regina::SpatialLink::Component = std::vector<Node> |
Represents a single link component.
This is stored as a sequence of nodes:
Link components must not be empty. As a consequence, since they describe embeddings, each component must have at least three nodes.
using regina::SpatialLink::Node = Vector3D<double> |
Represents a single point on the path that a link component takes through three-dimensional space.
|
inherited |
A type alias for PacketChangeSpan, used when a span is being used purely for optimisation purposes.
This type alias is used in the same way as Packet::PacketChangeGroup: it is purely for the benefit of the human reader, used to indicate that an event span is present purely for optimisation (and in particular, that the code would still be correct without it).
See Packet::PacketChangeGroup for further details.
|
default |
Constructs an empty link.
This will have zero components.
|
default |
Constructs a new copy of the given link.
|
defaultnoexcept |
Moves the given link into this new link.
This is a fast (constant time) operation.
The link that is passed will no longer be usable.
noexcept
, and in particular does not fire any change events. This is because this link is freshly constructed (and therefore has no listeners yet), and because we assume that the source link is about to be destroyed (an action that will fire a packet destruction event). regina::SpatialLink::SpatialLink | ( | iterator | begin, |
iterator | end ) |
Creates a new link whose components are supplied by the given sequences of points in 3-space.
Each element of the given sequence should represent a separate link component. Each component should be given as a sequence of at least three points in 3-space (any reasonable container type will do; see the requirements for the iterator type below). These are the points that will be stored directly in the Component structure, which means that to form the actual geometry of the link component:
This constructor induces a deep copy of the given data.
iterator | the iterator type used to access the full sequence of nodes in each link component. This must satisfy the following requirements: (i) when dereferenced, the resulting object (which represents a single link component) has appropriate begin() and end() functions; and (ii) when those iterators are dereferenced, the resulting object (which represents an individual point along some link component) is convertible to a Vector3D<double>. |
begin | the beginning of the sequence of link components. |
end | a past-the-end iterator indicating the end of the sequence of components. |
regina::SpatialLink::SpatialLink | ( | std::initializer_list< std::initializer_list< Node > > | components | ) |
Creates a new link whose components are given by hard-coded sequences of points in 3-space.
Each element of the given list should represent a separate link component. Each component should be given as a sequence of at least three points in 3-space. These are the points that will be stored directly in the Component structure, which means that to form the actual geometry of the link component:
components | the full sequences of nodes in each link component. |
|
inherited |
A unique string ID that can be used in place of a packet ID.
This is an alternative to Packet::internalID(), and is designed for use when Held is not actually wrapped by a PacketOf<Held>. (An example of such a scenario is when a normal surface list needs to write its triangulation to file, but the triangulation is a standalone object that is not stored in a packet.)
The ID that is returned will:
These IDs are not preserved when copying or moving one object to another, and are not preserved when writing to a Regina data file and then reloading the file contents.
See Packet::internalID() for further details.
|
inline |
Removes any user-specified radius to use when rendering this link.
Any subsequent calls to radius() will return a sensible default, as computed by defaultRadius().
|
inline |
Returns a reference to the component at the given index within this link.
index | the index of the requested component. This must be between 0 and countComponents()-1 inclusive. |
|
inline |
Returns an object that allows iteration through and random access to all components of this link.
The object that is returned is lightweight, and can be happily copied by value. The C++ type of the object is subject to change, so C++ users should use auto
(just like this declaration does).
The returned object is guaranteed to be an instance of ListView, which means it offers basic container-like functions and supports range-based for
loops. Each element of the list will be a constant reference to some component; more precisely, iterating through this list is equivalent to calling component(0)
, component(1)
, ..., component(countComponents()-1)
in turn. As an example, your code might look like:
The object that is returned will remain up-to-date and valid for as long as the link exists: even as components are added and/or removed, it will always reflect the components that are currently in the link. Nevertheless, it is recommended to treat this object as temporary only, and to call components() again each time you need it.
|
inline |
Returns the number of nodes that are stored for the given component of this link.
This is equivalent to calling component[componentIndex].size()
.
componentIndex | indicates the link component to query; this must be between 0 and countComponents() - 1 inclusive. |
|
inline |
Returns the number of components in this link.
|
inline |
Returns a sensible default radius to use when rendering the link.
Specifically, this is the radius to use for the balls and cylinders used in the 3-D model.
Currently this routine makes a "barely educated" decision: it looks only at the scale of the embedding, without studying the complexity of the knot or the closeness of the strands. Specifically, it chooses some fixed fraction of the minimum range amongst the x, y and z dimensions.
Eventually this will be replaced with something intelligent that factors in how far apart the strands are, and will (as a result) guarantee that the renderings of no-adjacent strands will not collide.
This function is expensive to call the first time, but it caches its value and so subsesquent calls are essentially instantaneous (until the embedding of the link changes, at which point the cached value will be cleared).
|
inherited |
Returns a detailed text representation of this object.
This text may span many lines, and should provide the user with all the information they could want. It should be human-readable, should not contain extremely long lines (which cause problems for users reading the output in a terminal), and should end with a final newline. There are no restrictions on the underlying character set.
|
static |
Creates a new link from a KnotPlot data file.
Since KnotPlot files are in a binary format, this routine takes a filename (not the file contents).
For further information on the KnotPlot file format, see https://knotplot.com/manual/FileFormats.html .
float
and double
are 32-bit and 64-bit respectively. This is at least true on x86_64
chips (64-bit intel) and arm64
chips (e.g., Apple Silicon). The sizes of the floating point types will be checked, and if this requirement fails to hold then this routine will throw a NotImplemented exception.FileError | The given file could not be read. |
InvalidInput | The contents of the given file could not be interpreted as a KnotPlot data file, or the data file contains a structure that cannot be represented by a SpatialLink (e.g., a link that is cut open leaving free ends). |
NotImplemented | The chipset on this machine uses floating-point types that are incompatible with KnotPlot's binary file format. |
filename | the name of a KnotPlot data file. |
|
inline |
Indicates whether the user has set their own custom radius to use when rendering this link.
true
if a custom radius has been set (e.g., via setRadius()), or false
if the default radius should be used (as computed by defaultRadius()).
|
inline |
Determines whether this link is empty.
An empty link is one with no components at all.
true
if and only if this link is empty.
|
inline |
Returns a particular node belong to a particular component of this link.
This is equivalent to calling component[componentIndex][nodeIndex]
.
componentIndex | indicates the component of the link to which the requested node belongs; this must be between 0 and countComponents() - 1 inclusive. |
nodeIndex | indicates which node to return from the given component; this must be between 0 and componentSize(componentIndex) - 1 inclusive. |
SpatialLink & regina::SpatialLink::operator= | ( | const SpatialLink & | src | ) |
Sets this to be a (deep) copy of the given link.
src | the link to copy. |
SpatialLink & regina::SpatialLink::operator= | ( | SpatialLink && | src | ) |
Moves the contents of the given link into this link.
This is a fast (constant time) operation.
The link that is passed (src) will no longer be usable.
noexcept
, since it fires change events on this link which may in turn call arbitrary code via any registered packet listeners. It deliberately does not fire change events on src, since it assumes that src is about to be destroyed (which will fire a destruction event instead).src | the link to move. |
|
inline |
Determines if this link is identical to the given link.
Here "identical" means that both links follow exactly the same paths through 3-dimensional space, with their components and nodes stored in exactly the same order.
If any rendering radii have been fixed (e.g., via setRadius()), these will be ignored for the purpose of this comparison.
other | the link to compare with this. |
true
if and only if the two links are identical.
|
inlineinherited |
Returns the packet that holds this data, if there is one.
If this object is being held by a packet p of type PacketOf<Held>, then that packet p will be returned. Otherwise, if this is a "standalone" object of type Held, then this routine will return null
.
There is a special case when dealing with a packet q that holds a SnapPea triangulation. Here q is of type PacketOf<SnapPeaTriangulation>, and it holds a Triangulation<3> "indirectly" in the sense that Packetof<SnapPeaTriangulation> derives from SnapPeaTriangulation, which in turn derives from Triangulation<3>. In this scenario:
null
, since there is no "direct" PacketOf<Triangulation<3>>;The function inAnyPacket() is specific to Triangulation<3>, and is not offered for other Held types.
null
if this data is not (directly) held by a packet.
|
inlineinherited |
Returns the packet that holds this data, if there is one.
See the non-const version of this function for further details, and in particular for how this functions operations in the special case of a packet that holds a SnapPea triangulation.
null
if this data is not (directly) held by a packet.
|
inline |
Returns the radius that should be used when rendering this link.
Specifically, this is the radius to use for the balls and cylinders used in the 3-D model.
If the user has fixed their own radius (e.g., via setRadius()), then that radius will be returned. Otherwise a sensible default (as computed by defaultRadius()) will be returned.
Returns the range of coordinates that this link occupies.
Specifically, this routine returns a pair (min, max)
, where min contains the minimum x, y and z coordinates over all nodes, and max contains the maximum x, y and z coordinates over all nodes.
((0,0,0), (0,0,0))
. void regina::SpatialLink::refine | ( | ) |
Adds additional nodes to make the embedding appear smoother.
Specifically, each adjacent pair of nodes will have one new node inserted between them (thereby doubling the number of nodes and arcs overall). This new node is not added at the midpoint of line segment between the two original nodes (which would not help with smoothing); instead it is calculated to lie on a Catmull-Rom spline defined by the original nodes. This spline is configured to have tension τ=0.5.
See also refine(int), which allows for many new nodes to be inserted between each adjacent pair of original nodes. Calling refine()
is equivalent to calling refine(2)
(but uses a more streamlined implementation).
void regina::SpatialLink::refine | ( | int | sub | ) |
Adds a configurable number of additional nodes to make the embedding appear smoother.
Specifically, each adjacent pair of nodes will have sub - 1
new nodes inserted between them (thereby multiplying the number of nodes and arcs by sub overall). The new nodes are not added along the line segments joining the original nodes (since this would not help with smoothing); instead they are calculated to lie on Catmull-Rom splines defined by the original nodes. These splines are configured to have tension τ=0.5.
See also refine(), which allows for many new nodes to be inserted between each adjacent pair of original nodes. Calling refine()
is equivalent to calling refine(2)
(but uses a more streamlined implementation).
sub | the number of pieces that each original arc (i.e., line segment) should be subdivided into. This must be at least 2. |
void regina::SpatialLink::reflect | ( | int | axis = 2 | ) |
Reflects the link in plane perpendicular to the given axis.
Specifically:
InvalidInput | The argument axis was not 0, 1 or 2. |
axis | indicates the axis of reflection, as described above. |
void regina::SpatialLink::scale | ( | double | factor | ) |
Scales the entire link by the given factor.
Specifically, all coordinates of all nodes will be multiplied by factor.
The rendering radius, if this has been fixed, will be scaled also.
factor | the scaling factor; this must not be zero. |
|
inline |
Indicates that the given radius should be used when rendering this link.
The given value will be returned by any subsequent calls to radius().
useRadius | the radius to use when rendering this link; this must be strictly positive. |
|
inline |
Returns the total number of nodes in this spatial link.
|
inherited |
Returns a short text representation of this object.
This text should be human-readable, should use plain ASCII characters where possible, and should not contain any newlines.
Within these limits, this short text ouptut should be as information-rich as possible, since in most cases this forms the basis for the Python __str__()
and __repr__()
functions.
__str__()
will use precisely this function, and for most classes the Python __repr__()
function will incorporate this into its output.void regina::SpatialLink::swap | ( | SpatialLink & | other | ) |
Swaps the contents of this and the given link.
All crossings that belong to this link will be moved to other, and all crossings that belong to other will be moved to this link.
This routine will behave correctly if other is in fact this link.
noexcept
, since it fires change events on both links which may in turn call arbitrary code via any registered packet listeners.other | the link whose contents should be swapped with this. |
void regina::SpatialLink::translate | ( | const Vector3D< double > & | vector | ) |
Translates the entire link by the given vector.
Specifically, the x, y and z coordinates of all nodes will be incremented by vector.x
, vector.y
and vector.z
respectively.
vector | holds the three constants that should be added to the x, y and z coordinates of every node. |
|
inherited |
Returns a short text representation of this object using unicode characters.
Like str(), this text should be human-readable, should not contain any newlines, and (within these constraints) should be as information-rich as is reasonable.
Unlike str(), this function may use unicode characters to make the output more pleasant to read. The string that is returned will be encoded in UTF-8.
void regina::SpatialLink::writeTextLong | ( | std::ostream & | out | ) | const |
Writes a detailed text representation of this link to the given output stream.
out | the output stream to which to write. |
void regina::SpatialLink::writeTextShort | ( | std::ostream & | out | ) | const |
Writes a short text representation of this link to the given output stream.
out | the output stream to which to write. |
|
protectedinherited |
Indicates whether this Held object is in fact the inherited data for a PacketOf<Held>.
As a special case, this field is also used to indicate when a Triangulation<3> is in fact the inherited data for a SnapPeaTriangulation. See the PacketHeldBy enumeration for more details on the different values that this data member can take.