11. API - Vector, vector_range, etc.¶
The vector module defines the Vector
class, which is the usual method
of represent coordinates or vectors when dealing with the Minecraft world. It
also provides functions like vector_range()
for generating sequences of
vectors.
Note
All items in this module are available from the picraft
namespace
without having to import picraft.vector
directly.
The following items are defined in the module:
11.1. Vector¶
-
class
picraft.vector.
Vector
(x=0, y=0, z=0)[source]¶ Represents a 3-dimensional vector.
This tuple derivative represents a 3-dimensional vector with x, y, z components. Instances can be constructed in a number of ways. By explicitly specifying the x, y, and z components (optionally with keyword identifiers), or leaving the empty to default to 0:
>>> Vector(1, 1, 1) Vector(x=1, y=1, z=1) >>> Vector(x=2, y=0, z=0) Vector(x=2, y=0, z=0) >>> Vector() Vector(x=0, y=0, z=0) >>> Vector(y=10) Vector(x=0, y=10, z=0)
Shortcuts are available for the X, Y, and Z axes:
>>> X Vector(x=1, y=0, z=0) >>> Y Vector(x=0, y=1, z=0)
Note that vectors don’t much care whether their components are integers, floating point values, or
None
:>>> Vector(1.0, 1, 1) Vector(x=1.0, y=1, z=1) >>> Vector(2, None, None) Vector(x=2, y=None, z=None)
The class supports simple arithmetic operations with other vectors such as addition and subtraction, along with multiplication and division with scalars, raising to powers, bit-shifting, and so on. Such operations are performed element-wise [1]:
>>> v1 = Vector(1, 1, 1) >>> v2 = Vector(2, 2, 2) >>> v1 + v2 Vector(x=3, y=3, z=3) >>> 2 * v2 Vector(x=4, y=4, z=4)
Simple arithmetic operations with scalars return a new vector with that operation performed on all elements of the original. For example:
>>> v = Vector() >>> v Vector(x=0, y=0, z=0) >>> v + 1 Vector(x=1, y=1, z=1) >>> 2 * (v + 2) Vector(x=4, y=4, z=4) >>> Vector(y=2) ** 2 Vector(x=0, y=4, z=0)
Within the Minecraft world, the X,Z plane represents the ground, while the Y vector represents height.
Note
Note that, as a derivative of tuple, instances of this class are immutable. That is, you cannot directly manipulate the x, y, and z attributes; instead you must create a new vector (for example, by adding two vectors together). The advantage of this is that vector instances can be used in sets or as dictionary keys.
[1] I realize math purists will hate this (and demand that abs() should be magnitude and * should invoke matrix multiplication), but the element wise operations are sufficiently useful to warrant the short-hand syntax. -
replace
(x=None, y=None, z=None)[source]¶ Return the vector with the x, y, or z axes replaced with the specified values. For example:
>>> Vector(1, 2, 3).replace(z=4) Vector(x=1, y=2, z=4)
-
ceil
()[source]¶ Return the vector with the ceiling of each component. This is only useful for vectors containing floating point components:
>>> Vector(0.5, -0.5, 1.2) Vector(1.0, 0.0, 2.0)
-
floor
()[source]¶ Return the vector with the floor of each component. This is only useful for vectors containing floating point components:
>>> Vector(0.5, -0.5, 1.9) Vector(0.0, -1.0, 1.0)
-
dot
(other)[source]¶ Return the dot product of the vector with the other vector. The result is a scalar value. For example:
>>> Vector(1, 2, 3).dot(Vector(2, 2, 2)) 12 >>> Vector(1, 2, 3).dot(X) 1
-
cross
(other)[source]¶ Return the cross product of the vector with the other vector. The result is another vector. For example:
>>> Vector(1, 2, 3).cross(Vector(2, 2, 2)) Vector(x=-2, y=4, z=-2) >>> Vector(1, 2, 3).cross(X) Vector(x=0, y=3, z=-2)
-
distance_to
(other)[source]¶ Return the Euclidian distance between two three dimensional points (represented as vectors), calculated according to Pythagoras’ theorem. For example:
>>> Vector(1, 2, 3).distance_to(Vector(2, 2, 2)) 1.4142135623730951 >>> Vector().distance_to(X) 1.0
-
angle_between
(other)[source]¶ Returns the angle between this vector and the other vector on a plane that contains both vectors. The result is measured in degrees. For example:
>>> X.angle_between(Y) 90.0 >>> (X + Y).angle_between(X) 45.00000000000001
-
project
(other)[source]¶ Return the scalar projection of this vector onto the other vector. This is a scalar indicating the length of this vector in the direction of the other vector. For example:
>>> Vector(1, 2, 3).project(2 * Y) 2.0 >>> Vector(3, 4, 5).project(Vector(3, 4, 0)) 5.0
-
rotate
(angle, about, origin=None)[source]¶ Return this vector after rotation of angle degrees about the line passing through origin in the direction about. Origin defaults to the vector 0, 0, 0. Hence, if this parameter is omitted this method calculates rotation about the axis (through the origin) defined by about. For example:
>>> Y.rotate(90, about=X) Vector(x=0, y=6.123233995736766e-17, z=1.0) >>> Vector(3, 4, 5).rotate(30, about=X, origin=10 * Y) Vector(x=3.0, y=2.3038475772933684, z=1.330127018922194)
Information about rotation around arbitrary lines was obtained from Glenn Murray’s informative site.
-
x
¶ The position or length of the vector along the X-axis. In the Minecraft world this can be considered to run left-to-right.
-
y
¶ The position or length of the vector along the Y-axis. In the Minecraft world this can be considered to run vertically up and down.
-
z
¶ The position or length of the vector along the Z-axis. In the Minecraft world this can be considered as depth (in or out of the screen).
-
magnitude
¶ Returns the magnitude of the vector. This could also be considered the distance of the vector from the origin, i.e.
v.magnitude
is equivalent toVector().distance_to(v)
. For example:>>> Vector(2, 4, 4).magnitude 6.0 >>> Vector().distance_to(Vector(2, 4, 4)) 6.0
-
unit
¶ Return a unit vector (a vector with a magnitude of one) with the same direction as this vector:
>>> X.unit Vector(x=1.0, y=0.0, z=0.0) >>> (2 * Y).unit Vector(x=0.0, y=1.0, z=0.0)
Note
If the vector’s magnitude is zero, this property returns the original vector.
-
11.2. Short-hand variants¶
The Vector
class is used sufficiently often to justify the inclusion
of some shortcuts. The class itself is also available as V
, and vectors
representing the three axes are each available as X
, Y
, and Z
.
Finally, a vector representing the origin is available as O
:
>>> from picraft import V, O, X, Y, Z
>>> O
Vector(x=0, y=0, z=0)
>>> 2 * X
Vector(x=2, y=0, z=0)
>>> X + Y
Vector(x=1, y=1, z=0)
>>> (X + Y).angle_between(X)
45.00000000000001
>>> V(3, 4, 5).projection(X)
3.0
>>> X.rotate(90, about=Y)
Vector(x=0.0, y=0.0, z=1.0)
11.3. vector_range¶
-
class
picraft.vector.
vector_range
(start, stop=None, step=None, order=u'zxy')[source]¶ Like
range()
,vector_range
is actually a type which efficiently represents a range of vectors. The arguments to the constructor must beVector
instances (or objects which have integerx
,y
, andz
attributes).If step is omitted, it defaults to
Vector(1, 1, 1)
. If the start argument is omitted, it defaults toVector(0, 0, 0)
. If any element of the step vector is zero,ValueError
is raised.The contents of the range are largely determined by the step and order which specifies the order in which the axes of the range will be incremented. For example, with the order
'xyz'
, the X-axis will be incremented first, followed by the Y-axis, and finally the Z-axis. So, for a range with the default start, step, and stop set toVector(3, 3, 3)
, the contents of the range will be:>>> list(vector_range(Vector(3, 3, 3), order='xyz')) [Vector(0, 0, 0), Vector(1, 0, 0), Vector(2, 0, 0), Vector(0, 1, 0), Vector(1, 1, 0), Vector(2, 1, 0), Vector(0, 2, 0), Vector(1, 2, 0), Vector(2, 2, 0), Vector(0, 0, 1), Vector(1, 0, 1), Vector(2, 0, 1), Vector(0, 1, 1), Vector(1, 1, 1), Vector(2, 1, 1), Vector(0, 2, 1), Vector(1, 2, 1), Vector(2, 2, 1), Vector(0, 0, 2), Vector(1, 0, 2), Vector(2, 0, 2), Vector(0, 1, 2), Vector(1, 1, 2), Vector(2, 1, 2), Vector(0, 2, 2), Vector(1, 2, 2), Vector(2, 2, 2)]
Vector ranges implement all common sequence operations except concatenation and repetition (due to the fact that range objects can only represent sequences that follow a strict pattern and repetition and concatenation usually cause the resulting sequence to violate that pattern).
Vector ranges are extremely efficient compared to an equivalent
list()
ortuple()
as they take a small (fixed) amount of memory, storing only the arguments passed in its construction and calculating individual items and sub-ranges as requested.Vector range objects implement the
collections.Sequence
ABC, and provide features such as containment tests, element index lookup, slicing and support for negative indices.The default order (
'zxy'
) may seem an odd choice. This is primarily used as it’s the order used by the Raspberry Juice server when returning results from the world.getBlocks call. In turn, Raspberry Juice probably uses this order as it results in returning a horizontal layer of vectors at a time (given the Y-axis is used for height in the Minecraft world).Warning
Bear in mind that the ordering of a vector range may have a bearing on tests for its ordering and equality. Two ranges with different orders are unlikely to test equal even though they may have the same start, stop, and step attributes (and thus contain the same vectors, but in a different order).
Vector ranges can be accessed by integer index, by
Vector
index, or by a slice of vectors. For example:>>> v = vector_range(Vector() + 1, Vector() + 3) >>> list(v) [Vector(x=1, y=1, z=1), Vector(x=1, y=1, z=2), Vector(x=2, y=1, z=1), Vector(x=2, y=1, z=2), Vector(x=1, y=2, z=1), Vector(x=1, y=2, z=2), Vector(x=2, y=2, z=1), Vector(x=2, y=2, z=2)] >>> v[0] Vector(x=1, y=1, z=1) >>> v[Vector(0, 0, 0)] Vector(x=1, y=1, z=1) >>> v[Vector(1, 0, 0)] Vector(x=2, y=1, z=1) >>> v[-1] Vector(x=2, y=2, z=2) >>> v[Vector() - 1] Vector(x=2, y=2, z=2) >>> v[Vector(x=1):] vector_range(Vector(x=2, y=1, z=1), Vector(x=3, y=3, z=3), Vector(x=1, y=1, z=1), order='zxy') >>> list(v[Vector(x=1):]) [Vector(x=2, y=1, z=1), Vector(x=2, y=1, z=2), Vector(x=2, y=2, z=1), Vector(x=2, y=2, z=2)]
However, integer slices are not currently permitted.
-
count
(value)[source]¶ Return the count of instances of value within the range (note this can only be 0 or 1 in the case of a range, and thus is equivalent to testing membership with
in
).
-
index
(value)[source]¶ Return the zero-based index of value within the range, or raise
ValueError
if value does not exist in the range.
-
11.4. line¶
-
picraft.vector.
line
(start, end)[source]¶ A three-dimensional implementation of Bresenham’s line algorithm, derived largely from Bob Pendelton’s implementation (public domain). Given the end points of the line as the start and end vectors, this generator function yields the coordinate of each block (inclusive of the start and end vectors) that should be filled in to render the line.
For example:
>>> list(line(O, V(10, 5, 0))) [Vector(x=0, y=0, z=0), Vector(x=1, y=1, z=0), Vector(x=2, y=1, z=0), Vector(x=3, y=2, z=0), Vector(x=4, y=2, z=0), Vector(x=5, y=3, z=0), Vector(x=6, y=3, z=0), Vector(x=7, y=4, z=0), Vector(x=8, y=4, z=0), Vector(x=9, y=5, z=0), Vector(x=10, y=5, z=0)]
11.5. lines¶
-
picraft.vector.
lines
(points, closed=True)[source]¶ Extension of the
line()
function which returns all vectors necessary to render the lines connecting the specified points (which is an iterable ofVector
instances).If the optional closed parameter is
True
(the default) the last point in the points sequence will be connected to the first point. Otherwise, the lines will be left disconnected (assuming the last point is not coincident with the first). For example:>>> points = [O, 4*X, 4*Z] >>> list(lines(points)) [Vector(x=0, y=0, z=0), Vector(x=1, y=0, z=0), Vector(x=2, y=0, z=0), Vector(x=3, y=0, z=0), Vector(x=4, y=0, z=0), Vector(x=3, y=0, z=1), Vector(x=2, y=0, z=2), Vector(x=1, y=0, z=3), Vector(x=0, y=0, z=4), Vector(x=0, y=0, z=3), Vector(x=0, y=0, z=2), Vector(x=0, y=0, z=1), Vector(x=0, y=0, z=0)]