geometry — wrapper classes for vectors, segments, polygons etc.

Vector reference

Constructor:

class geometry.Vector(x, y)

Vector instance represents an Euclidean vector. It stores a pair od 2D corrdinates (x, y).

Vectors are immutable.

Vectors are used for the following purposes:

  • storing an actual vector pointing from (0, 0) to (x, y), for example nodes.BodyNode.velocity

  • storing a 2D point, for example nodes.Node.position

  • storing a width/height of a rectangular shape, such as a screen resolution. For example engine.Engine.virtual_resolution

Vector constructor accepts two float numbers: x and y.

Available operators:

  • Adding two vectors: Vector(1,1) + Vector(2,2)

  • Substracting two vectors: Vector(1,1) - Vector(2,2)

  • Multiplying vector by a scalar: Vector(1,1) * 123

  • Dividing vector by a scalar: Vector(1,1) / 123

Class methods:

classmethod Vector.from_angle(angle)

Creates a new unit Vector (i.e. length 1 vector) from angle, in radians.

import math
from kaa.geometry import Vector

v = Vector.from_angle(math.pi / 4)
print(v)  # V[0.7071067811865476, 0.7071067811865475]
print(v.length())  # 1.0
classmethod Vector.from_angle_degrees(degrees)

Creates a new unit Vector (i.e. length 1 vector) from angle, in degrees.

import math
from kaa.geometry import Vector

v = Vector.from_angle_degrees(90) # 90 degrees is pointing up, 180, pointing left, 270 pointing down etc.
print(v)  # V[0.0, 1.0]
print(v.length())  # 1.0

Instance Properties (read only):

Vector.x

Gets the x value of a vector

Vector.y

Gets the y value of a vector

Instance Methods:

Vector.is_zero()

Returns True if vector is a zero vector

from kaa.geometry import Vector

Vector(0, 0).is_zero()  # True
Vector(0.1, 0).is_zero()  # False
Vector.rotate_angle(angle)

Returns a new vector, rotated by given angle, in radians.

from kaa.geometry import Vector
import math

print(Vector(10, 0))  # V[10, 0]
print(Vector(10, 0).rotate_angle(math.pi))  # V[-10, 0]
Vector.rotate_angle_degrees(degrees)

Returns a new vector, rotated by given angle, in degrees.

from kaa.geometry import Vector
import math

print(Vector(10, 0))  # V[10, 0]
print(Vector(10, 0).rotate_angle_degrees(180))  # V[-10, 0]
Vector.to_angle()

Returns vector’s angle, in radians.

Vector.to_angle_degrees()

Returns vector’s angle, in degrees.

Vector.dot(other_vector)

Returns dot product of two vectors. other_vector parameter must be geometry.Vector

Vector.distance(other_vector)

Returns a distance from (x,y) to (other_vector.x, other_vector.y), in other words: distance between two points. other_vector parameter must be geometry.Vector

Vector.angle_between(other_vector)

Returns angle between this vector and other_vector, in radians. The other_vector parameter must be geometry.Vector

Vector.angle_between_degrees(other_vector)

Returns angle between this vector and other_vector, in degrees. The other_vector parameter must be geometry.Vector

Vector.normalize()

Returns a new vector, normalized (i.e. unit vector)

Vector.length()

Returns vector’s length.

Segment reference

Constructor:

class geometry.Segment(vector_a, vector_b)

Segment instance represents a segment between two points, a and b.

Segments are immutable.

vector_a and vector_b params are geometry.Vector instances indicating both ends of a Segment

Instance properties:

Segment.point_a

Read only. Returns point A of the segment

Segment.point_b

Read only. Returns point B of the segment

Segment.bounding_box

Read only. Returns segment’s bounding box as geometry.BoundingBox.

Instance methods:

Segment.transform(transformation)

Applies given transformation to this Segment and returns a new Segment.

The transformation parameter must be a Transformation instance.

Circle reference

Constructor:

class geometry.Circle(radius, center=Vector(0, 0))

Circle instance represents a circualar shape, with a center and a radius. Circles are used e.g. for creating hitboxes.

Circles are immutable.

The center parameter must be geometry.Vector, radius is a number.

Instance properties:

Circle.radius

Read only. Returns circle radius.

Circle.center

Read only. Returns circle center.

Circle.bounding_box

Read only. Returns circle bounding box as geometry.BoundingBox.

Instance methods:

Circle.transform(transformation)

Applies given transformation to this Circle and returns a new Circle.

The transformation parameter must be a Transformation instance.

Polygon reference

Constructor:

class geometry.Polygon(points)

Polygon instance represents a custom shape. Polygons are used e.g. for creating hitboxes.

Polygons are immutable.

The points parameter must be a list of geometry.Vector instances.

If you don’t close the polygon (the last point in the list is not identical with the first one) kaa will do that for you.

The polygon must be convex. Kaa engine will throw an exception if you try to create a non-convex polygon. You may use classify_polygon() function to check if a list of points will form a convex polygon or not.

from kaa.geometry import Polygon

polygon = Polygon([Vector(-10, -10), Vector(10, 30), Vector(0, 40)])  # a triangular-shaped polygon

Class methods:

classmethod Polygon.from_box(vector)

Creates a rectangular-shaped Polygon whose central point is at (0, 0) and width and height are passed as vector.x and vector.y respectively. A useful shorthand function for creating a rectangular shape for a physics.HitboxNode.

from kaa.geometry import Polygon, Vector

poly = Polygon.from_box(Vector(10, 8)) # creates a rectangular polygon [ V(-5, -4), V(5, -4), V(5, 4), V(-5, 4) ]

Instance properties:

Polygon.points

Read only. Returns a list of points constituting the Polygon.

Polygon.bounding_box

Read only. Returns polygon’s bounding box as geometry.BoundingBox.

Instance methods:

Polygon.transform(transformation)

Applies given transformation to this Polygon and returns a new Polygon.

The transformation parameter must be a Transformation instance.

Transformation reference

class geometry.Transformation

Transformation is a ‘geometrical recipe’, which can be applied to a Segment, Circle or Polygon (using the transform() method) to change their position, rotation and scale.

Transformations cannot be applied to Nodes, although if a Node has a shape, you can apply Transformations to that shape.

Transformation objects are immutable.

Transformation constructor does not accept any parameters and creates a ‘void’ transformation which, when applied, does not have any effect.

To create an actual Transformation use one of the class methods: rotate(), rotate_degrees(), scale() or translate()

from kaa.geometry import Transformation
import math

t1 = Transformation.rotate(math.pi / 2)  # a 90 degrees transformation, clockwise
t2 = Transformation.rotate_degrees(-45)  # a 45 degrees transformation, anti-clockwise
t3 = Transformation.scale(Vector(2,2))  # scale change transformation (enlarge twice)
t4 = Transformation.translate(Vector(10, 0))  # position change transformation (10 units to the right)

You can chain transformations by applying the | operator, which results in a new, combined transformations:

combined_transformation = t1 | t2 | t3 | t4

Rotation and scaling is always relative to the origin of the Euclidean space, or in other words, relative to (0,0) point. Therefore, a sequence of transformations in a chain is important. Consider the following two transformations:

rotate_than_move = t2 | t4
move_than_rotate = t4 | t2

Contrary to intuition they won’t give the same result. When applied to a square with an edge length of 1 and the middle in the (0,0) the first one will rotate the square 45 degrees around (0,0) and then move 10 units to the right, while the second one will move the square 10 units to the right and then rotate, but since the center of the square is now at (10,0) the rotation is going to “wheel” it 45 degrees around the (0,0), making the Polygon end up in a different position. It’s illustrated in the example below:

from kaa.geometry import Vector, Polygon

square = Polygon.from_box(Vector(2,2))
print(square.points)  #[V[-1.0, -1.0], V[1.0, -1.0], V[1.0, 1.0], V[-1.0, 1.0]]

# just move it
square_2 = square.transform(t4)
print(square_2.points)  #[V[9.0, -1.0], V[11.0, -1.0], V[11.0, 1.0], V[9.0, 1.0]]

# rotate then move
square_3 = square.transform(t2 | t4)
print(square_3.points)  # [V[8.585, 0.0], V[10.0, -1.414], V[11.414, 0.0], V[10.0, 1.414]]

# move then rotate
square_4 = square.transform(t4 | t2)
print(square_4.points)  # [V[5.656, -7.071], V[7.071, -8.485], V[8.485, -7.071], V[7.0710, -5.656]]

Using the @ operator you can chain transformation in the matrix-style order:

rotate_then_move = t2 | t4
move_then_rotate = t2 @ t4

Finally, you can use the inverse() method on the Transformation instance to get the inversed transformation:

combined_transformation = t1 | t2 | t3 | t4
inversed_combined_transformation = combined_transformation.inverse()

Class methods:

classmethod Transformation.rotate(rotation)

Creates a new rotation Transformation. The rotation value must be a number (rotation in radians).

classmethod Transformation.rotate_degrees(rotation_degrees)

Creates a new rotation Transformation. The rotation value must be a number (rotation in degrees).

classmethod Transformation.scale(scaling_vector)

Creates a new scaling Transformation. The scaling_vector must be a Vector whose x and y represent scaling in x and y axis respectively.

classmethod Transformation.translate(translation_vector)

Creates a new translation (position change) Transformation. The translation_vector must be a Vector.

Instance methods:

Transformation.inverse()

Returns a new Transformation, being an inversed version of this Transformation.

Transformation.decompose()

Returns a DecomposedTransformation object which allows reading transformation’s translation, rotation and scale.

combined_transformation = t1 | t2 | t3 | t4
result = combined_transformation.decompose()
print(result.translation, result.rotation, result.rotation_degrees, result.scale)

DecomposedTransformation reference

class geometry.DecomposedTransformation

Object returned by Transformation.decompose(). It surfaces transformation properties.

Instance properties:

DecomposedTransformation.translation

Returns translation as geometry.Vector

DecomposedTransformation.rotation

Returns rotation as float, in radians

DecomposedTransformation.rotation_degrees

Returns rotation as float, in degrees

DecomposedTransformation.scale

Returns scale, as geometry.Vector

BoundingBox reference

class geometry.BoundingBox(min_x, min_y, max_x, max_y)

Represents a rectangular bounding box. Bounding box is always aligned with x and y axis. Bounding boxes are being used when querying for nodes on scene. Constructor accepts four parameters, which determine the bounding box x and y limits. You can also construct the BoundingBox using helper methods BoundingBox.single_point() and BoundingBox.from_points()

Class methods:

classmethod BoundingBox.single_point(point)

Creates a BoundingBox from a single point. The point parameter must be a geometry.Vector representing point coordinates. A single point BoundingBox has no width/height.

classmethod BoundingBox.from_points(points)

Creates a BoundingBox from points. The points must be a list of geometry.Vector instances, representing point coordinates.

If points list is empty, it will return bounding box with NaN values.

If points list has 1 point, it behaves exactly like BoundingBox.single_point()

If points list has 2 or more points, it will return smallest box which contains all provided points.

Instance properties:

BoundingBox.min_x

Gets min_x of the bounding box.

BoundingBox.min_y

Gets min_y of the bounding box.

BoundingBox.max_x

Gets max_x of the bounding box.

BoundingBox.max_y

Gets max_y of the bounding box.

BoundingBox.is_nan

Gets “not a number” status of the bounding box, as bool

BoundingBox.center

Gets the central point of the bounding box, as geometry.Vector.

BoundingBox.dimensions

Gets dimensions of the bounding box, as geometry.Vector, x being width and y being height.

Instance methods:

BoundingBox.merge(other_bounding_box)

Merges the bounding box with other and returns a new bounding box.

BoundingBox.contains(other)

Other can be BoundingBox or geometry.Vector. Returns True if bounding box contains other bounding box or point.

BoundingBox.intersects(other_bounding_box)

Returns True if bounding box intersects with other geometry.BoundingBox, otherwise returns False

BoundingBox.intersection(other_bounding_box)

If bounding box intersects with other geometry.BoundingBox a BoundingBox is returned which spans the intersection. If there’s no intersection, an ‘empty’ geometry.BoundingBox is returned (all properties set to NaN)

BoundingBox.grow(vector)

Grows the bounding box by given vector (adds the vector’s x and y value to the corresponding sides of the bounding box). The vector param must be geometry.Vector

Alignment reference

class geometry.Alignment

Enum type used to set Node’s origin alignment to one of the 9 positions. See nodes.Node.origin_alignment

Available values are:

  • Alignment.none

  • Alignment.top

  • Alignment.bottom

  • Alignment.left

  • Alignment.right

  • Alignment.top_left

  • Alignment.bottom_left

  • Alignment.top_right

  • Alignment.bottom_right

  • Alignment.center

PolygonType reference

class geometry.PolygonType

Enum type returned by the classify_polygon() function. Available values:

  • PolygonType.convex_cw - the list of points forms a convex polygon, the points are ordered clockwise

  • PolygonType.convex_ccw - the list of points forms a convex polygon, the points are ordered counter clockwise

  • PolygonType.not_convex - the list of points forms a non-convex polygon

classify_polygon() reference

geometry.classify_polygon(polygon)

Accepts a list of points (list of geometry.Vector) and returns if polygon formed by those points is convex or not. The function returns a PolygonType enum value.

from kaa.geometry import Vector, classify_polygon

print(classify_polygon([Vector(0, 0), Vector(10, 0), Vector(10, 10), Vector(0, 10)]))  # PolygonType.conwex_ccw
print(classify_polygon([Vector(0, 0), Vector(0, 10), Vector(10, 10), Vector(10, 0)]))  # PolygonType.conwex_cw
print(classify_polygon([Vector(0, 0), Vector(10, 0), Vector(2, 2), Vector(0, 10)]))  # PolygonType.not_convex