3D

Surfaces, solids and 3D axes, seen through a camera you can tilt, turn and move.

A first 3D scene

from pymations import *


class FirstLook(ThreeDScene):
    def construct(self):
        axes = ThreeDAxes()
        sphere = Sphere(radius=1.5)
        self.set_camera_orientation(phi=70 * DEGREES, theta=-45 * DEGREES)
        self.play(Create(axes))
        self.play(FadeIn(sphere))
        self.begin_ambient_camera_rotation(rate=0.3)
        self.wait(4)
  • A 3D scene is a ThreeDScene instead of a Scene. Everything in it is seen through the 3D camera: shapes are drawn in perspective, nearer faces cover farther ones, and faces are shaded by the light.
  • Points have a third number, z, which points up out of the flat picture. OUT and IN are one unit up and down in z.
  • Everything else works as in 2D: Create, Transform, the timeline, scrubbing and Record Video.

The camera

CallWhat it does
self.set_camera_orientation(phi=70 * DEGREES, theta=-45 * DEGREES)Points the camera at once. phi tilts it down from straight above (0 looks straight down); theta turns it round the z axis
self.move_camera(phi=..., theta=..., zoom=...)Moves the camera smoothly, as an animation. run_time= and added_anims=[...] work as in play
self.begin_ambient_camera_rotation(rate=0.2)Keeps the camera turning slowly round the scene (rate in radians a second)
self.stop_ambient_camera_rotation()Stops it
self.begin_3dillusion_camera_rotation()Sways the camera gently, which makes the scene look solid
self.set_to_default_angled_camera_orientation()The usual 3D view

zoom=2 makes everything twice as big; frame_center=[x, y, z] moves the point the camera looks at. The camera's angles are on the timeline like everything else, so scrubbing shows the right view at every moment.

Words that stay flat

Titles and captions usually should not turn with the camera:

from pymations import *


class Labelled(ThreeDScene):
    def construct(self):
        self.set_camera_orientation(phi=65 * DEGREES, theta=30 * DEGREES)
        title = Text("A torus", font_size=40).to_corner(UL)
        self.add_fixed_in_frame_mobjects(title)
        self.play(Write(title))
        self.play(Create(Torus(major_radius=2, minor_radius=0.6)), run_time=2)
        self.move_camera(phi=30 * DEGREES, theta=120 * DEGREES, run_time=3)
  • self.add_fixed_in_frame_mobjects(m) keeps m flat on the screen, where you put it, whatever the camera does.
  • self.add_fixed_orientation_mobjects(m) keeps m at its place in 3D but always facing the camera, for labels on points.

Shapes in 3D

ShapeMade with
SphereSphere(radius=1)
Dot in 3DDot3D([1, 2, 1])
CubeCube(side_length=2)
BoxPrism(dimensions=[3, 2, 1])
ConeCone(base_radius=1, height=2)
CylinderCylinder(radius=1, height=2)
RingTorus(major_radius=3, minor_radius=1)
Line and arrowLine3D(ORIGIN, [1, 1, 2]), Arrow3D(ORIGIN, [1, 1, 2])
Regular solidsTetrahedron(), Octahedron(), Icosahedron(), Dodecahedron()
Any solidPolyhedron(vertex_coords, faces_list)

They take fill_color, fill_opacity and the other style options. m.rotate(angle, axis=UP) turns a shape about any axis; shift and move_to take points with z.

from pymations import *


class Solids(ThreeDScene):
    def construct(self):
        self.set_camera_orientation(phi=70 * DEGREES, theta=-60 * DEGREES)
        solids = VGroup(
            Cube(side_length=1.5),
            Icosahedron().set_fill(TEAL, opacity=0.8),
            Cone(base_radius=0.8, height=1.6, fill_color=ORANGE),
        ).arrange(RIGHT, buff=1)
        self.play(LaggedStart(*[FadeIn(s) for s in solids], lag_ratio=0.3))
        self.play(Rotate(solids[1], angle=PI, axis=UP), run_time=2)

Surfaces and 3D graphs

A Surface is made from a function of two numbers, u and v, that returns a point:

from pymations import *


class Ripple(ThreeDScene):
    def construct(self):
        axes = ThreeDAxes(x_range=[-3, 3, 1], y_range=[-3, 3, 1], z_range=[-1, 1, 1],
                         x_length=6, y_length=6, z_length=2)
        surface = Surface(
            lambda u, v: axes.c2p(u, v, 0.6 * np.sin(u) * np.cos(v)),
            u_range=[-3, 3], v_range=[-3, 3], resolution=24,
        )
        self.set_camera_orientation(phi=65 * DEGREES, theta=-45 * DEGREES)
        self.play(Create(axes))
        self.play(Create(surface), run_time=2)
        self.begin_ambient_camera_rotation(rate=0.2)
        self.wait(3)
  • resolution=24 is how many squares make it up in each direction: more is smoother and slower.
  • It is checkerboard-coloured unless you say. surface.set_fill_by_value(axes=axes, colorscale=[BLUE, GREEN, YELLOW]) colours it by height instead.
  • axes.plot_surface(lambda x, y: ..., colorscale=[...]) plots z = f(x, y) over the axes in one step.
  • ThreeDAxes has x, y and z axes, sized with x_length, y_length and z_length; axes.c2p(x, y, z) turns graph numbers into a point, and axes.get_axis_labels() labels all three.
  • A ParametricFunction that returns points with z draws a curve in 3D, such as a helix.