Adding wires

Wires

There are two types of wire in manim-eng:

  • Wire, which routes itself automatically; and

  • ManualWire, which requires manual specification of routing points.

We’ll use Wire, as it’s much simpler and easier (and is almost always all you need). First, we’ll connect just two terminals, to get a feel for things. The wire interface is very simple — simply pass the two terminals you wish to connect, and it does the rest.

class CurrentShunt(Scene):
    def construct(self):
        r1 = Resistor().rotate(90 * DEGREES)
        r2 = Resistor().rotate(90 * DEGREES).shift(2 * RIGHT)
        isource = CurrentSource().rotate(90 * DEGREES).shift(2 * LEFT)

        r1.set_label("R_1")
        r2.set_label("R_2")
        isource.set_current("I_0")

        self.add(r1, r2, isource)
        self.add(Wire(isource.right, r1.right))
../_images/CurrentShunt-5.png

Note

The terminal naming is based on when a component is horizontal, and does not change as the terminal is rotated. Since we rotated the components by 90 degrees anticlockwise, the terminal on the top is the one that used to be on the right.

We can then connect the rest together in the same way.

class CurrentShunt(Scene):
    def construct(self):
        r1 = Resistor().rotate(90 * DEGREES)
        r2 = Resistor().rotate(90 * DEGREES).shift(2 * RIGHT)
        isource = CurrentSource().rotate(90 * DEGREES).shift(2 * LEFT)

        r1.set_label("R_1")
        r2.set_label("R_2")
        isource.set_current("I_0")

        self.add(r1, r2, isource)
        self.add(
            Wire(isource.right, r1.right),
            Wire(isource.left, r1.left),
            Wire(r1.right, r2.right),
            Wire(r1.left, r2.left),
        )
../_images/CurrentShunt-6.png

This is starting to look pretty good! But there’s something not quite right about those intersections — they need a solder blob. For this, we have to turn to the other key player in manim-eng wiring and wire routing — the Node.

Nodes

Nodes have two main purposes in manim-eng:

  1. Managing multi-wire intersections, including automatic solder blobbing; and

  2. Representing external connections to a circuit.

We’re going to be using them for their first purpose here. Before we begin, a few further remarks about nodes are in order:

  1. Unlike normal components, nodes add terminals as they need them, and terminals can be added in any direction; and

  2. When acting as an intersection, they automatically add and remove solder blobs depending on the number of connected wires.

We’ll add one node first, just connecting the current source and R1 for now.

class CurrentShunt(Scene):
    def construct(self):
        r1 = Resistor().rotate(90 * DEGREES)
        r2 = Resistor().rotate(90 * DEGREES).shift(2 * RIGHT)
        isource = CurrentSource().rotate(90 * DEGREES).shift(2 * LEFT)
        top_node = Node().shift(UP)

        r1.set_label("R_1")
        r2.set_label("R_2")
        isource.set_current("I_0")

        self.add(r1, r2, isource, top_node)
        self.add(
            Wire(isource.right, top_node.left).attach(),
            Wire(r1.right, top_node.down).attach(),
        )

You may notice the extra method call of attach() — this is tells the wire to tell the terminals it is attached to them. For most terminals this doesn’t matter, but nodes’ terminals are only visible when something is attached — we just need to tell the terminals that something is attached to them! This gives us the below.

../_images/CurrentShunt-7.png

Let’s add in the third connection to the top node.

class CurrentShunt(Scene):
    def construct(self):
        r1 = Resistor().rotate(90 * DEGREES)
        r2 = Resistor().rotate(90 * DEGREES).shift(2 * RIGHT)
        isource = CurrentSource().rotate(90 * DEGREES).shift(2 * LEFT)
        top_node = Node().shift(UP)

        r1.set_label("R_1")
        r2.set_label("R_2")
        isource.set_current("I_0")

        self.add(r1, r2, isource, top_node)
        self.add(
            Wire(isource.right, top_node.left).attach(),
            Wire(r1.right, top_node.down).attach(),
            Wire(r2.right, top_node.right).attach(),
        )
../_images/CurrentShunt-8.png

Hang on a minute — wasn’t the whole point of this to get a solder blob? Well, yes, but there’s a problem — nodes use updaters to introduce or remove solder blobs, which run every animation frame. The problem is that our current project is a still frame, and so updaters never get run. We’ve just got to add one extra line: self.update_mobjects(0).

class CurrentShunt(Scene):
    def construct(self):
        r1 = Resistor().rotate(90 * DEGREES)
        r2 = Resistor().rotate(90 * DEGREES).shift(2 * RIGHT)
        isource = CurrentSource().rotate(90 * DEGREES).shift(2 * LEFT)
        top_node = Node().shift(UP)

        r1.set_label("R_1")
        r2.set_label("R_2")
        isource.set_current("I_0")

        self.add(r1, r2, isource, top_node)
        self.add(
            Wire(isource.right, top_node.left).attach(),
            Wire(r1.right, top_node.down).attach(),
            Wire(r2.right, top_node.right).attach(),
        )
        self.update_mobjects(0)

The update_mobjects method requires a delta time dt to pass to time-dependent animations. We don’t have any of those, so we just pass 0. Our result is then what we expected.

../_images/CurrentShunt-9.png

Note

We could’ve updated the node directly using the below.

top_node.update()

Using the update_mobjects method means that when we add the second node we don’t have to add a second updating line.

Speaking of adding the second node, let’s do just that!

class CurrentShunt(Scene):
    def construct(self):
        r1 = Resistor().rotate(90 * DEGREES)
        r2 = Resistor().rotate(90 * DEGREES).shift(2 * RIGHT)
        isource = CurrentSource().rotate(90 * DEGREES).shift(2 * LEFT)
        top_node = Node().shift(UP)
        bottom_node = Node().shift(DOWN)

        r1.set_label("R_1")
        r2.set_label("R_2")
        isource.set_current("I_0")

        self.add(r1, r2, isource, top_node, bottom_node)
        self.add(
            Wire(isource.right, top_node.left).attach(),
            Wire(r1.right, top_node.down).attach(),
            Wire(r2.right, top_node.right).attach(),
            Wire(isource.left, bottom_node.left).attach(),
            Wire(r1.left, bottom_node.up).attach(),
            Wire(r2.left, bottom_node.right).attach(),
        )
        self.update_mobjects(0)
../_images/CurrentShunt-10.png

Doesn’t that look good! Now, that was a lot of work, so we’re now going to take a look at a way of doing this more easily — the Circuit class.