10. 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:
10.1. Vector¶

class
picraft.vector.
Vector
(x=0, y=0, z=0)[source]¶ Represents a 3dimensional vector.
This
namedtuple()
derivative represents a 3dimensional vector withx
,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 vectors representing 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, raising to powers, bitshifting, and so on. Such operations are performed elementwise [1]:
>>> v1 = Vector(1, 1, 1) >>> v2 = Vector(2, 2, 2) >>> v1 + v2 Vector(x=3, y=3, z=3) >>> v1 * v2 Vector(x=2, y=2, z=2)
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
namedtuple()
, instances of this class are immutable. That is, you cannot directly manipulate thex
,y
, andz
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 members ofset
or keys in adict
.[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 shorthand 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.123233995736766e17, 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 Xaxis. In the Minecraft world this can be considered to run lefttoright.

y
¶ The position or length of the vector along the Yaxis. In the Minecraft world this can be considered to run vertically up and down.

z
¶ The position or length of the vector along the Zaxis. 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.

10.2. Shorthand 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)
10.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 Xaxis will be incremented first, followed by the Yaxis, and finally the Zaxis. 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 subranges 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 Yaxis is used for height in the Minecraft world).Warning
Bear in mind that the ordering of a vector range may have affect 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 zerobased index of value within the range, or raise
ValueError
if value does not exist in the range.

10.4. line¶

picraft.vector.
line
(start, end)[source]¶ Generates the coordinates of a line joining the start and end
Vector
instances inclusive. This is a generator function; points are yielded from start, proceeding to end. If you don’t require all points you may terminate the generator at any point.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)]
To draw the resulting line you can simply assign a block to the collection of vectors generated (or assign a sequence of blocks of equal length if you want the line to have varying block types):
>>> world.blocks[line(O, V(10, 5, 0))] = Block('stone')
This is a threedimensional implementation of Bresenham’s line algorithm, derived largely from Bob Pendelton’s implementation (public domain).
10.5. lines¶

picraft.vector.
lines
(points, closed=True)[source]¶ Generator function which extends the
line()
function; this yields 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)]
To draw the resulting polygon you can simply assign a block to the collection of vectors generated (or assign a sequence of blocks of equal length if you want the polygon to have varying block types):
>>> world.blocks[lines(points)] = Block('stone')
To generate the coordinates of a filled polygon, see the
filled()
function.
10.6. circle¶

picraft.vector.
circle
(center, radius, plane=Vector(x=0, y=1, z=0))[source]¶ Generator function which yields the coordinates of a threedimensional circle centered at the
Vector
center. The radius parameter is a vector specifying the distance of the circumference from the center. The optional plane parameter (which defaults to the Y unit vector) specifies another vector which, in combination with the radius vector, gives the plane that the circle exists within.For example, to generate the coordinates of a circle centered at (0, 10, 0), with a radius of 5 units, existing in the XY plane:
>>> list(circle(O, 5*X)) [Vector(x=5, y=0, z=0), Vector(x=5, y=1, z=0), Vector(x=4, y=2, z=0), Vector(x=4, y=3, z=0), Vector(x=5, y=1, z=0), Vector(x=4, y=2, z=0), Vector(x=4, y=3, z=0), Vector(x=3, y=4, z=0), Vector(x=3, y=4, z=0), Vector(x=2, y=4, z=0), Vector(x=2, y=4, z=0), Vector(x=1, y=4, z=0), Vector(x=1, y=4, z=0), Vector(x=0, y=5, z=0), Vector(x=0, y=5, z=0), Vector(x=1, y=4, z=0), Vector(x=1, y=4, z=0), Vector(x=2, y=4, z=0), Vector(x=2, y=4, z=0), Vector(x=3, y=4, z=0), Vector(x=3, y=4, z=0), Vector(x=4, y=3, z=0), Vector(x=4, y=3, z=0), Vector(x=4, y=2, z=0), Vector(x=5, y=1, z=0), Vector(x=5, y=0, z=0), Vector(x=4, y=2, z=0), Vector(x=5, y=1, z=0)]
To generate another set of coordinates with the same center and radius, but existing in the XZ (ground) plane:
>>> list(circle(O, 5*X, plane=Z)) [Vector(x=5, y=0, z=0), Vector(x=5, y=0, z=1), Vector(x=4, y=0, z=2), Vector(x=4, y=0, z=3), Vector(x=5, y=0, z=1), Vector(x=4, y=0, z=2), Vector(x=4, y=0, z=3), Vector(x=3, y=0, z=4), Vector(x=3, y=0, z=4), Vector(x=2, y=0, z=4), Vector(x=2, y=0, z=4), Vector(x=1, y=0, z=4), Vector(x=1, y=0, z=4), Vector(x=0, y=0, z=5), Vector(x=0, y=0, z=5), Vector(x=1, y=0, z=4), Vector(x=1, y=0, z=4), Vector(x=2, y=0, z=4), Vector(x=2, y=0, z=4), Vector(x=3, y=0, z=4), Vector(x=3, y=0, z=4), Vector(x=4, y=0, z=3), Vector(x=4, y=0, z=3), Vector(x=4, y=0, z=2), Vector(x=5, y=0, z=1), Vector(x=5, y=0, z=0), Vector(x=4, y=0, z=2), Vector(x=5, y=0, z=1)]
To draw the resulting circle you can simply assign a block to the collection of vectors generated (or assign a sequence of blocks of equal length if you want the circle to have varying block types):
>>> world.blocks[circle(O, 5*X)] = Block('stone')
The algorithm used by this function is based on a straightforward differences of roots method, extended to three dimensions. This produces worse looking circles than the midpoint circle algorithm (also known as a the Bresenham circle algorithm), but isn’t restricted to working in a simple cartesian plane.
Note
If you know of a three dimensional generalization of the midpoint circle algorithm (which handles entirely arbitrary planes, not merely simple XY, XZ, etc. planes), please contact the author!
To create a filled circle, see the
filled()
function.
10.7. sphere¶

picraft.vector.
sphere
(center, radius)[source]¶ Generator function which yields the coordinates of a hollow sphere. The center
Vector
specifies the center of the sphere, and radius is a scalar number of blocks giving the distance from the center to the edge of the sphere.For example to create the coordinates of a sphere centered at the origin with a radius of 5 units:
>>> list(sphere(O, 5))
To draw the resulting sphere you can simply assign a block to the collection of vectors generated (or assign a sequence of blocks of equal length if you want the sphere to have varying block types):
>>> world.blocks[sphere(O, 5)] = Block('stone')
The algorithm generates concentric circles covering the sphere’s surface, advancing along the X, Y, and Z axes with duplicate elimination to prevent repeated coordinates being yielded. Three axes are required to eliminate gaps in the surface.
10.8. filled¶

picraft.vector.
filled
(points)[source]¶ Generator function which yields the coordinates necessary to fill the space enclosed by the specified points.
This function can be applied to anything that returns a sequence of points. For example, to create a filled triangle:
>>> triangle = [O, 4*X, 4*Z] >>> list(filled(lines(triangle))) [Vector(x=0, y=0, z=0), Vector(x=0, y=0, z=1), Vector(x=0, y=0, z=2), Vector(x=0, y=0, z=3), Vector(x=0, y=0, z=4), Vector(x=1, y=0, z=2), Vector(x=1, y=0, z=1), Vector(x=1, y=0, z=0), Vector(x=1, y=0, z=3), Vector(x=2, y=0, z=1), Vector(x=2, y=0, z=0), Vector(x=2, y=0, z=2), Vector(x=3, y=0, z=1), Vector(x=3, y=0, z=0), Vector(x=4, y=0, z=0)]
Or to create a filled circle:
>>> list(filled(circle(O, 4*X))) [Vector(x=4, y=0, z=0), Vector(x=3, y=1, z=0), Vector(x=3, y=2, z=0), Vector(x=3, y=0, z=0), Vector(x=3, y=1, z=0), Vector(x=3, y=2, z=0), Vector(x=2, y=1, z=0), Vector(x=2, y=2, z=0), Vector(x=2, y=3, z=0), Vector(x=2, y=0, z=0), Vector(x=2, y=1, z=0), Vector(x=2, y=2, z=0), Vector(x=2, y=3, z=0), Vector(x=1, y=0, z=0), Vector(x=1, y=1, z=0), Vector(x=1, y=2, z=0), Vector(x=1, y=3, z=0), Vector(x=1, y=1, z=0), Vector(x=1, y=2, z=0), Vector(x=1, y=3, z=0), Vector(x=0, y=1, z=0), Vector(x=0, y=2, z=0), Vector(x=0, y=3, z=0), Vector(x=0, y=4, z=0), Vector(x=0, y=0, z=0), Vector(x=0, y=1, z=0), Vector(x=0, y=2, z=0), Vector(x=0, y=3, z=0), Vector(x=0, y=4, z=0), Vector(x=1, y=0, z=0), Vector(x=1, y=1, z=0), Vector(x=1, y=2, z=0), Vector(x=1, y=3, z=0), Vector(x=1, y=1, z=0), Vector(x=1, y=2, z=0), Vector(x=1, y=3, z=0), Vector(x=2, y=0, z=0), Vector(x=2, y=1, z=0), Vector(x=2, y=2, z=0), Vector(x=2, y=3, z=0), Vector(x=2, y=1, z=0), Vector(x=2, y=2, z=0), Vector(x=2, y=3, z=0), Vector(x=3, y=0, z=0), Vector(x=3, y=1, z=0), Vector(x=3, y=2, z=0), Vector(x=3, y=1, z=0), Vector(x=3, y=2, z=0), Vector(x=4, y=0, z=0), Vector(x=4, y=1, z=0), Vector(x=4, y=1, z=0)]
To draw the resulting filled object you can simply assign a block to the collection of vectors generated (or assign a sequence of blocks of equal length if you want the object to have varying block types):
>>> world.blocks[filled(lines(triangle))] = Block('stone')
A simple bruteforce algorithm is used that simply generates all the lines connecting all specified points. However, duplicate elimination is used to ensure that no point within the filled space is yielded twice.
Note that if you pass the coordinates of a polyhedron which contains holes or gaps compared to its convex hull, this function may fill those holes or gaps (but it will depend on the orientation of the object).