struct Chem::Spatial::Parallelepiped

Overview

A parallelepiped is a three-dimensional figure formed by six parallelograms. It is defined by three vectors (basis) and it's useful for representing spatial bounds and unit cells.

It is internally represented by a 3x3 matrix, where each column correspond to a basis vector (see Mat3.basis), where coordinates are expressed in Cartesian space (angstroms). In this way, the basis matrix can be used to transform from Cartesian to fractional coordinates, and viceversa, by matrix multiplication (see #cart and #fract).

Defined in:

chem/spatial/parallelepiped.cr

Constructors

Instance Method Summary

Constructor Detail

def self.[](a : Number, b : Number, c : Number) : self #

Creates a Parallelepiped with the given lengths placed at the origin.


[View source]
def self.cubic(a : Number) : self #

Creates a cubic parallelepiped (a = b = c and α = β = γ = 90°).


[View source]
def self.from_io(io : IO, format : IO::ByteFormat) : self #

Reads a parallelepiped from io in the given format. See also: IO#read_bytes.


[View source]
def self.hexagonal(a : Number, c : Number) : self #

Creates a hexagonal parallelepiped (a = b, α = β = 90°, and γ = 120°).


[View source]
def self.monoclinic(a : Number, c : Number, beta : Number) : self #

Creates a monoclinic parallelepiped (ac, α = γ = 90°, and β ≠ 90°).


[View source]
def self.new(i : Vec3, j : Vec3, k : Vec3, origin : Vec3 = Vec3.zero) : self #

Creates a Parallelepiped with the given basis vectors located at origin.


[View source]
def self.new(basis : Mat3, origin : Vec3 = Vec3.zero) #

Creates a Parallelepiped with basis located at origin.


[View source]
def self.new(vmin : Vec3, vmax : Vec3) : self #

Creates a Parallelepiped spanning from vmin to vmax.


[View source]
def self.new(size : NumberTriple | Size3, angles : NumberTriple = {90, 90, 90}, origin : Vec3 = Vec3.zero) : self #

Creates a Parallelepiped with the given lengths (in angstroms) and angles (in degrees) located at origin. Raises ArgumentError if any of the lengths or angles is negative.

NOTE The first basis vector will be aligned to the X axis and the second basis vector will lie in the XY plane.


[View source]
def self.orthorhombic(a : Number, b : Number, c : Number) : self #

Creates an orthorhombic parallelepiped (abc and α = β = γ = 90°).


[View source]
def self.rhombohedral(a : Number, alpha : Number) : self #

Creates an rhombohedral parallelepiped (a = b = c and α = β = γ ≠ 90°).


[View source]
def self.tetragonal(a : Number, c : Number) : self #

Creates an tetragonal parallelepiped (a = bc and α = β = γ = 90°).


[View source]

Instance Method Detail

def *(value : Number) : self #

Returns a parallelepiped with the basis vectors multiplied by value.


[View source]
def ==(rhs : self) : Bool #

[View source]
def angles : NumberTriple #

Returns the parallelepiped angles (alpha, beta, gamma) in degrees.


[View source]
def basis : Mat3 #

Matrix containing the basis vectors.


[View source]
def basisvec : Tuple(Vec3, Vec3, Vec3) #

Returns the basis vectors.


[View source]
def cart(vec : Vec3) : Vec3 #

Returns the vector in Cartesian coordinates equivalent to the given fractional coordinates.


[View source]
def center : Vec3 #

Returns the center of the parallelepiped.


[View source]
def center_at(vec : Vec3) : self #

Centers the parallelepiped at vec.


[View source]
def center_at_origin : self #

Centers the parallelepiped at the origin.


[View source]
def close_to?(rhs : self, delta : Number = Float64::EPSILON) : Bool #

Returns true if the values of the parallelepipeds are within delta from each other, else false.


[View source]
def cubic? : Bool #

Returns true if the parallelepiped is cubic (a = b = c and α = β = γ = 90°), else false.


[View source]
def each_edge(& : Vec3, Vec3 -> ) : Nil #

Yields each parallelepiped' edge as a pair of vertices.


[View source]
def each_vertex(& : Vec3 -> ) : Nil #

Yields parallelepiped' vertices.

Parallelepiped[5, 10, 20].each_vertex { |vec| puts vec }

Prints:

Vec3[0.0, 0.0, 0.0]
Vec3[0.0, 0.0, 20.0]
Vec3[0.0, 10.0, 0.0]
Vec3[0.0, 10.0, 20.0]
Vec3[5.0, 0.0, 0.0]
Vec3[5.0, 0.0, 20.0]
Vec3[5.0, 10.0, 0.0]
Vec3[5.0, 10.0, 20.0]

[View source]
def edges : Array(Tuple(Vec3, Vec3)) #

Returns the parallelepiped' edges as pairs of vertices.


[View source]
def fract(vec : Vec3) : Vec3 #

Returns the vector in fractional coordinates equivalent to the given Cartesian coordinates.


[View source]
def hexagonal? : Bool #

Returns true if the parallelepiped is hexagonal (a = b, α = β = 90°, and γ = 120°), else false.


[View source]
def image(vec : Vec3, ix : Tuple(Int, Int, Int)) : Vec3 #

Returns the vector's image with respect to the parallelepiped.

pld = new Parallelepiped.new({2, 2, 3}, {90, 90, 120})
pld.i # => Vec3[2.0, 0.0, 0.0]
pld.j # => Vec3[-1, 1.732, 0.0]
pld.k # => Vec3[0.0, 0.0, 3.0]

vec = Vec3[1, 1, 1.5]
pld.image(vec, {1, 0, 0}) # => Vec3[3.0, 1.0, 1.5]
pld.image(vec, {0, 1, 0}) # => Vec3[0.0, 2.732, 1.5]
pld.image(vec, {0, 0, 1}) # => Vec3[1.0, 1.0, 4.5]
pld.image(vec, {1, 0, 1}) # => Vec3[3.0, 1.0, 4.5]
pld.image(vec, {1, 1, 1}) # => Vec3[2.0, 2.732, 4.5]

[View source]
def includes?(other : self) : Bool #

Returns true if the parallelepiped encloses other, false otherwise.

It effectively checks if every vertex of other is contained by the parallelepiped.

pld = Parallelepiped.new({10, 10, 10}, {90, 90, 120})
pld.includes? Parallelepiped[5, 4, 6]                        # => true
pld.includes? Parallelepiped.new(Vec3[-1, 2, -4], {5, 4, 6}) # => false

[View source]
def includes?(vec : Vec3) : Bool #

Returns true if the parallelepiped encloses vec, false otherwise.

pld = Parallelepiped.new({23.803, 23.828, 5.387}, {90, 90, 120})
pld.includes? Vec3[10, 20, 2]  # => true
pld.includes? Vec3[0, 0, 0]    # => true
pld.includes? Vec3[30, 30, 10] # => false
pld.includes? Vec3[-3, 10, 2]  # => true
pld.includes? Vec3[-3, 2, 2]   # => false

[View source]
def inspect(io : IO) : Nil #
Description copied from struct Struct

Appends this struct's name and instance variables names and values to the given IO.

struct Point
  def initialize(@x : Int32, @y : Int32)
  end
end

p1 = Point.new 1, 2
p1.to_s    # "Point(@x=1, @y=2)"
p1.inspect # "Point(@x=1, @y=2)"

[View source]
def monoclinic? : Bool #

Returns true if the parallelepiped is monoclinic (ac, α = γ = 90°, and β ≠ 90°), else false.


[View source]
def origin : Vec3 #

Origin of the parallelepiped.


[View source]
def orthogonal? : Bool #

Returns true if the parallelepiped is orthogonal (α = β = γ = 90°), else false.


[View source]
def orthorhombic? : Bool #

Returns true if the parallelepiped is orthorhombic (abc and α = β = γ = 90°), else false.


[View source]
def pad(px : Number, py : Number, pz : Number, centered : Bool = true) : self #

Returns a new parallelepiped by expanding the extents by padding in each direction. padding can be either a single value, three values, or a Size3 instance.

If centered is true, the origin will be changed such that the center does not change, else it will be kept intact.

pld = Parallelepiped.new(Vec3[1, 5, 3], {10, 5, 12})

other = pld.pad(2.5)
other.size                 # => Size3[15, 10, 17]
other.origin == pld.origin # => false
other.center == pld.center # => true

other = pld.pad(2.5, centered: false)
other.size                 # => Size3[15, 10, 17]
other.origin == pld.origin # => true
other.center == pld.center # => false

NOTE Note that its size is actually increased by padding * 2.


[View source]
def pad(padding : Number, centered : Bool = true) : self #

Returns a new parallelepiped by expanding the extents by padding in each direction. padding can be either a single value, three values, or a Size3 instance.

If centered is true, the origin will be changed such that the center does not change, else it will be kept intact.

pld = Parallelepiped.new(Vec3[1, 5, 3], {10, 5, 12})

other = pld.pad(2.5)
other.size                 # => Size3[15, 10, 17]
other.origin == pld.origin # => false
other.center == pld.center # => true

other = pld.pad(2.5, centered: false)
other.size                 # => Size3[15, 10, 17]
other.origin == pld.origin # => true
other.center == pld.center # => false

NOTE Note that its size is actually increased by padding * 2.


[View source]
def pad(padding : Size3, centered : Bool = true) : self #

Returns a new parallelepiped by expanding the extents by padding in each direction. padding can be either a single value, three values, or a Size3 instance.

If centered is true, the origin will be changed such that the center does not change, else it will be kept intact.

pld = Parallelepiped.new(Vec3[1, 5, 3], {10, 5, 12})

other = pld.pad(2.5)
other.size                 # => Size3[15, 10, 17]
other.origin == pld.origin # => false
other.center == pld.center # => true

other = pld.pad(2.5, centered: false)
other.size                 # => Size3[15, 10, 17]
other.origin == pld.origin # => true
other.center == pld.center # => false

NOTE Note that its size is actually increased by padding * 2.


[View source]
def resize(si : Number | Nil, sj : Number | Nil, sk : Number | Nil) : self #

Returns a parallelepiped by resizing the basis vectors to the given values.

pld = Parallelepiped.hexagonal(1, 2)
pld.angles # => {90, 90, 120}
pld.size   # => Size3[1, 1, 2]

other = pld.resize(5, 5, 12)
other.angles # => {90, 90, 120}
other.size   # => Size3[5, 5, 12]

Use nil to keep the current size:

other = pld.resize(nil, 5, nil)
other.angles # => {90, 90, 120}
other.size   # => Size3[1, 5, 12]

[View source]
def resize(size : Chem::Spatial::Size3) : self #

Returns a parallelepiped by resizing the basis vectors to the given size.

pld = Parallelepiped.hexagonal(1, 2)
pld.angles # => {90, 90, 120}
pld.size   # => Size3[1, 1, 2]

other = pld.resize(Size3[5, 5, 12])
other.angles # => {90, 90, 120}
other.size   # => Size3[5, 5, 12]

[View source]
def resize(& : Float64, Float64, Float64 -> Tuple(Float64, Float64, Float64)) : self #

Yields the basis vectors' sizes to the given block, and returns a parallelepiped by resizing them to the returned values.

pld = Parallelepiped.hexagonal(1, 2)
pld.angles # => {90, 90, 120}
pld.size   # => Size3[1, 1, 2]

other = pld.resize { |a, b, c| {a * 2, b / 10, c} }
other.angles # => {90, 90, 120}
other.size   # => Size3[2, 0.1, 2]

[View source]
def resize_by(a : Number, b : Number, c : Number) : self #

Returns a parallelepiped by padding the basis vectors by the given values.

pld = Parallelepiped.hexagonal(1, 2)
pld.angles # => {90, 90, 120}
pld.size   # => Size3[1, 1, 2]

other = pld.resize_by(2, 3, -0.5)
other.angles # => {90, 90, 120}
other.size   # => Size3[3, 4, 1.5]

[View source]
def rhombohedral? : Bool #

Returns true if the parallelepiped is rhombohedral (a = b = c and α = β = γ ≠ 90°), else false.


[View source]
def rotate(x : Number, y : Number, z : Number) : self #

Returns the parallelepiped rotated by the given Euler angles in degrees. Delegates to Quat.rotation for computing the rotation.


[View source]
def rotate(about rotaxis : Vec3, by angle : Number) : self #

Returns the parallelepiped rotated about the axis vector rotaxis by angle degrees. Delegates to Quat.rotation for computing the rotation.


[View source]
def rotate(quat : Quat) : self #

Returns the parallelepiped rotated by the given quaternion.


[View source]
def size : Size3 #

Returns the lengths of the basis vectors.


[View source]
def tetragonal? : Bool #

Returns true if the parallelepiped is tetragonal (a = bc and α = β = γ = 90°), else false.


[View source]
def to_io(io : IO, format : IO::ByteFormat = :system_endian) : Nil #

Writes the binary representation of the parallelepiped to io in the given format. See also IO#write_bytes.


[View source]
def transform(transformation : Transform) : self #

Returns the parallelepiped resulting of applying the given transformation.

NOTE the rotation will be applied about the center of the parallelepiped. Translation will be applied afterwards.


[View source]
def transform(& : Vec3, Vec3, Vec3 -> Tuple(Vec3, Vec3, Vec3)) : self #

Returns a new parallelepiped with the return value of the given block, which is invoked with the basis vectors.

pld = Parallelepiped.cubic(10).transform do |bi, bj, bk|
  bi *= 2
  bk /= 0.4
  {bi, bj, bk}
end
pld.basisvec[0] # => Vec3[20, 0, 0]
pld.basisvec[1] # => Vec3[0, 10, 0]
pld.basisvec[2] # => Vec3[0, 0, 25]

[View source]
def translate(offset : Vec3) : self #

Returns a new parallelepiped translated by offset.

pld = Parallelepiped.new(Vec3[-5, 1, 20], {10, 10, 10}, {90, 90, 120})
pld.translate Vec3[1, 2, 10]
pld.origin # => Vec3[-4.0, 3.0, 30.0]

[View source]
def triclinic? : Bool #

Returns true if the parallelepiped is triclinic (not orthogonal, hexagonal, monoclinic, nor rhombohedral), else false.


[View source]
def vertices : Array(Vec3) #

Returns parallelepiped' vertices.

pld = Parallelepiped[5, 10, 20]
pld.vertices # => [Vec3[0.0, 0.0, 0.0], Vec3[0.0, 0.0, 20.0], ...]

[View source]
def vmax : Vec3 #

Returns the maximum vertex.

pld = Parallelepiped.new(Vec3[1.5, 3, -0.4], {10, 10, 12}, {90, 90, 120})
pld.vmax # => Vec3[6.5, 11.66, 11.6]

[View source]
def vmin : Vec3 #

Returns the minimum vertex. This is equivalent to the parallelepiped's origin.

pld = Parallelepiped.new(Vec3[1.5, 3, -0.4], {10, 10, 12}, {90, 90, 120})
pld.vmin # => Vec3[1.5, 3, -0.4]

[View source]
def volume : Float64 #

Returns the volume of the parallelepiped.


[View source]
def wrap(vec : Vec3, around center : Vec3) : Vec3 #

Returns the vector by wrapping it into the parallelepiped centered at center. The vector is assumed to be expressed in Cartesian coordinates.


[View source]
def wrap(vec : Vec3) : Vec3 #

Returns the vector by wrapping it into the parallelepiped. The vector is assumed to be expressed in Cartesian coordinates.


[View source]
def xyz? : Bool #

Whether the parallelepiped is aligned to the X, Y, and Z axes.


[View source]