MCPcopy Create free account
hub / github.com/Notgnoshi/generative / Turtle

Class Turtle

generative/lsystem/turtle.py:9–67  ·  view source on GitHub ↗

A turtle object that keeps track of its position and rotation in 3D space. All angles given in degrees.

Source from the content-addressed store, hash-verified

7
8
9class Turtle:
10 """A turtle object that keeps track of its position and rotation in 3D space.
11
12 All angles given in degrees.
13 """
14
15 def __init__(self, position: np.ndarray = None, rotation: Rotation = None):
16 """Initialize the turtle with the given position and rotation.
17
18 Use a traditional RHS coordinate system with Z pointing up.
19 Transformations are performed using intrinsic Euler angles, so rotations about an axis are
20 relative to the turtle's local reference frame.
21
22 :param position: The starting position of the turtle. Defaults to (0, 0, 0).
23 :param rotation: The starting rotation of the turtle, applied to the vector (0, 0, 1).
24 """
25 if rotation is not None and not isinstance(rotation, Rotation):
26 raise TypeError("Rotation must be a scipy.spatial.transform.Rotation")
27
28 self._position = np.array(position) if position is not None else np.array([[0, 0, 0]])
29 self.rotation = rotation if rotation is not None else Rotation.from_matrix(np.eye(3))
30
31 @property
32 def position(self):
33 """Ensure the position is externally always treated as (3,), not (1, 3)."""
34 return self._position.reshape((3,))
35
36 @position.setter
37 def position(self, value):
38 self._position = np.array(value)
39
40 def forward(self, stepsize=1):
41 """Move the turtle forward by the given stepsize."""
42 orientation = (0, 0, 1)
43 orientation = self.rotation.apply(orientation)
44 self.position = self.position + stepsize * orientation
45 logger.debug(f"stepping forward to {self.position}")
46
47 def yaw(self, angle):
48 """Yaw the turtle around its local Z axis."""
49 # NOTE: Capital axes indicate intrinsic Euler angles.
50 # Apparently, it's normal to indicate the normal and longitudinal axes with X and Z respectively
51 # I still want to keep the mental model of "Z is up, duh."
52 self.rotation = self.rotation * Rotation.from_euler("X", [angle], degrees=True)
53 logger.debug(f"yaw {angle}deg")
54
55 def pitch(self, angle):
56 """Pitch the turtle around its local Y axis."""
57 self.rotation = self.rotation * Rotation.from_euler("Y", [angle], degrees=True)
58 logger.debug(f"pitch {angle}deg")
59
60 def roll(self, angle):
61 """Roll the turtle around its local X axis.
62
63 Just a roll is enough to affect direction, since it's a rotation around the longitudinal
64 axis. That is, a rotation around the axis you're facing.
65 """
66 self.rotation = self.rotation * Rotation.from_euler("Z", [angle], degrees=True)

Callers 9

test_initial_positionMethod · 0.90
test_initial_rotationMethod · 0.90
test_forwardMethod · 0.90
test_rotated_forwardMethod · 0.90
test_rollMethod · 0.90
test_pitchMethod · 0.90
test_yawMethod · 0.90
__init__Method · 0.85

Calls

no outgoing calls

Tested by 8

test_initial_positionMethod · 0.72
test_initial_rotationMethod · 0.72
test_forwardMethod · 0.72
test_rotated_forwardMethod · 0.72
test_rollMethod · 0.72
test_pitchMethod · 0.72
test_yawMethod · 0.72