A point is a zero-dimensional location in 2D or 3D space. The
Point2 and Point3 classes in pbrt represent points in the obvious way: using , , (in 3D)
coordinates with respect to a coordinate system. Although the same
representation is used for vectors, the fact that a point represents a
position whereas a vector represents a direction leads to a number of
important differences in how they are treated. Points are denoted in text
by .
In this section, we will continue the approach of only including
implementations of the 3D point methods for the Point3 class here.
using Tuple3<Point3, T>::x;
using Tuple3<Point3, T>::y;
using Tuple3<Point3, T>::z;
using Tuple3<Point3, T>::HasNaN;
using Tuple3<Point3, T>::operator+;
using Tuple3<Point3, T>::operator+=;
using Tuple3<Point3, T>::operator*;
using Tuple3<Point3, T>::operator*=;
Point3() = default;
PBRT_CPU_GPU
Point3(T x, T y, T z) : Tuple3<pbrt::Point3, T>(x, y, z) {}
// We can't do using operator- above, since we don't want to pull in
// the Point-Point -> Point one so that we can return a vector
// instead...
PBRT_CPU_GPU
Point3<T> operator-() const { return {-x, -y, -z}; }
template <typename U>
explicit Point3(Point3<U> p)
: Tuple3<pbrt::Point3, T>(T(p.x), T(p.y), T(p.z)) {}
template <typename U>
explicit Point3(Vector3<U> v)
: Tuple3<pbrt::Point3, T>(T(v.x), T(v.y), T(v.z)) {}
template <typename U>
auto operator+(Vector3<U> v) const -> Point3<decltype(T{} + U{})> {
return {x + v.x, y + v.y, z + v.z};
}
template <typename U>
Point3<T> &operator+=(Vector3<U> v) {
x += v.x; y += v.y; z += v.z;
return *this;
}
template <typename U>
auto operator-(Vector3<U> v) const -> Point3<decltype(T{} - U{})> {
return {x - v.x, y - v.y, z - v.z};
}
template <typename U>
Point3<T> &operator-=(Vector3<U> v) {
x -= v.x; y -= v.y; z -= v.z;
return *this;
}
template <typename U>
auto operator-(Point3<U> p) const -> Vector3<decltype(T{} - U{})> {
return {x - p.x, y - p.y, z - p.z};
}
};
As with vectors, it is helpful to have shorter type names for commonly used
point types.
<<Point3* Definitions>>=
using Point3f = Point3<Float>;
using Point3i = Point3<int>;
It is also useful to be able to convert a point with one element type
(e.g., a Point3f) to a point of another one (e.g., Point3i) as
well as to be able to convert a point to a vector with a different
underlying element type. The following constructor and conversion operator
provide these conversions. Both also require an explicit cast, to make it
clear in source code when they are being used.
There are certain Point3 methods that either return or take a
Vector3. For instance, one can add a vector to a point, offsetting
it in the given direction to obtain a new point. Analogous methods, not
included in the text, also allow subtracting a vector from a point.
<<Point3 Public Methods>>+=
template <typename U>
auto operator+(Vector3<U> v) const -> Point3<decltype(T{} + U{})> {
return {x + v.x, y + v.y, z + v.z};
}
template <typename U>
Point3<T> &operator+=(Vector3<U> v) {
x += v.x; y += v.y; z += v.z;
return *this;
}
Alternately, one can subtract one point from another, obtaining
the vector between them, as shown in Figure 3.7.
Figure 3.7: Obtaining the Vector between Two Points. The vector
is given by the component-wise subtraction of the points
and .
<<Point3 Public Methods>>+=
template <typename U>
auto operator-(Point3<U> p) const -> Vector3<decltype(T{} - U{})> {
return {x - p.x, y - p.y, z - p.z};
}
The distance between two points can be computed by subtracting them to
compute the vector between them and then finding the length of that
vector. Note that we can just use auto for the return type and let
it be set according to the return type of Length(); there is no need
to use the TupleLength type trait to find that type.