Reshape Geometry

Sometimes we encounter this situation: we want to reset a shape’s size or contour based on geometric parameters, and we might do this:

# First creation
circle = Circle(radius=2)

...

# ? Reset geometric parameters
circle.become(Circle(radius=1.2))

Although this method works, recreating items this way has two major problems:

  • We need to temporarily create a Circle, “borrow” its data, and then discard it immediately, which is cumbersome; and when called frequently, it can slightly hurt performance

  • The temporarily created Circle cannot follow the current item’s position, so extra cumbersome operations are needed for alignment

When you want “the same item, just with a different size or contour”, geometry items provide reshape() to help you do this.

When reshape Fits

reshape() is suitable for “parameterized shape changes”, for example:

  • Change a circle’s radius

  • Change a rectangle’s width and height

  • Change a rounded rectangle’s corner radius

Compared with temporarily constructing a new item, reshape() has these advantages:

  • The code is simpler: just pass new parameters directly, without creating a temporary item

  • Parameters can be omitted, for example changing only a rounded rectangle’s corner radius while keeping width and height unchanged; note that omitted parameters are determined by values originally passed to the constructor, not by the item’s current width and height

  • For some geometry items, the current position can be kept unchanged, with no extra alignment operations

Usage

You can understand it as “re-describing the current item with new shape parameters”; you can reset some parameters originally passed to the constructor:

item.reshape(...)

In animations, a common form is:

self.play(item.anim.reshape(...))

Below we demonstrate reshape() with two common shapes.

circle = Circle(radius=0.8, color=BLUE, fill_alpha=0.4).show()

self.forward()
self.play(circle.anim.reshape(1.8))
self.forward(0.5)
self.play(circle.anim.reshape(0.5))
self.forward()

circle.hide()

rect = RoundedRect(3.6, 1.6, corner_radius=0.25, color=TEAL, fill_alpha=0.35).show()

self.forward()
self.play(rect.anim.reshape(5.2, 2.4, corner_radius=0.5))
self.forward(0.5)
self.play(rect.anim.reshape(corner_radius=0.15))
self.forward(0.5)
self.play(rect.anim.reshape(UL, DR))
self.forward()

A more complex example:

path = VItem(
    [-6.2, 0.76, 0], [-4.45, 0.65, 0], [-3.43, 0.14, 0], [-2.95, -0.11, 0], [-1.89, -0.82, 0],
    [-0.64, -1.66, 0], [0.03, -2, 0], [1.3, -2.64, 0], [4.07, -3.28, 0],
    color=[BLUE, RED]
).show()

star = Star(start_angle=10 * DEGREES, color=YELLOW, fill_alpha=0.5).show()
star.points.to_border(UR, buff=0.75)

dot = Dot()

arrow = Arrow(color=YELLOW, alpha=[0, 1])

self.play(
    MoveAlongPath(dot, path),
    DataUpdater(
        dot,
        lambda data, p:
            data.reshape(radius=DEFAULT_DOT_RADIUS * (1 + 3 * p.alpha))
                .glow.set(alpha=p.alpha)
    ),
    GroupUpdater(
        arrow,
        lambda group, p: group.reshape(dot.current(), star)
    ),
    duration=4
)

See also:

MoveAlongPath

Note

The path is drawn using the GUI Draw feature