Components ========== The :class:`~.Component` class is the core element [#actually]_ of manim-eng, and all circuit symbols are derived from it. It is therefore useful to understand what functionality it provides you with. This is not a replacement for the API documentation, but instead aims to give a more general overview of the wider family of components and their functionality. Setting labels and annotations ------------------------------ All components have two ways for setting labels and annotations: the :meth:`~.Component.set_label` and :meth:`~.Component.set_annotation` methods, and the ``label`` and ``annotation`` parameters to the components' constructors. .. warning:: Monopoles (components with one terminal) only support labels and *not* annotations. They will throw an error if you attempt to set an annotation. .. code-block:: python class SettingLabelsAndAnnotations(Scene): def construct(self): r1 = Resistor(label="R_1", annotation=1 * KILO * OHM).shift(LEFT) r2 = Resistor().set_label("R_2").set_annotation(1 * KILO * OHM).shift(RIGHT) self.add(r1, r2) .. manim:: SettingLabelsAndAnnotations :hide_source: :save_last_frame: from manim_eng import * config_eng.debug = False class SettingLabelsAndAnnotations(Scene): def construct(self): r1 = Resistor(label="R_1", annotation=1 * KILO * OHM).shift(LEFT) r2 = Resistor().set_label("R_2").set_annotation(1 * KILO * OHM).shift(RIGHT) self.add(r1, r2) Components also have :meth:`~.Component.clear_label` and :meth:`~.Component.clear_annotation` methods for removing annotations. Of course, all of these methods support animation overrides as well --- let's take a look at both of these in action together. .. code-block:: python class SettingLabelsAndAnnotations(Scene): def construct(self): r = Resistor() self.add(r) self.play( r.animate.set_label("R"), r.animate.set_annotation(1 * KILO * OHM), ) self.wait() self.play( r.animate.set_label("R_1"), r.animate.set_annotation(10 * KILO * OHM), ) self.wait() self.play( r.animate.clear_label(), r.animate.clear_annotation(), ) self.wait() .. manim:: SettingLabelsAndAnnotations :hide_source: from manim_eng import * config_eng.debug = False class SettingLabelsAndAnnotations(Scene): def construct(self): r = Resistor() self.add(r) self.play( r.animate.set_label("R"), r.animate.set_annotation(1 * KILO * OHM), ) self.wait() self.play( r.animate.set_label("R_1"), r.animate.set_annotation(10 * KILO * OHM), ) self.wait() self.play( r.animate.clear_label(), r.animate.clear_annotation(), ) self.wait() Labels and annotations are additionally discussed further in the :doc:`next section `. Setting terminal current labels ------------------------------- As alluded to in :doc:`../first_circuit_diagram/index`, there are two ways to set the current labels on terminals of a component. The first is to call the :meth:`~.Terminal.set_current()` method on the terminal, and the second is to call the :meth:`~.Component.set_current()` method on the component. As the second is a wrapper around the first, we will focus on the first here --- but this is all applicable to both. When setting a terminal current label, you have a few options, namely: - What the label should be (the ``label`` parameter); - Which direction the autogenerated arrow should point (the ``out`` parameter); and - The side of the terminal the label should be placed on (the ``below`` parameter). You can provide any number of these parameters to any given call, as long as at least one is specified. ``set`` vs ``reset`` ^^^^^^^^^^^^^^^^^^^^ You may have noticed that, in addition to :meth:`~.Terminal.set_current()`, terminals also provide :meth:`~.Terminal.reset_current()` (with components defining a similarly named method). The difference between the two lies in how they handle non-specified label positioning arguments: - ``reset`` will *reset* any positioning not specified back to the defaults (arrows pointing *into* components and labels placed to the right of the terminal (as you look down it towards the end); and - ``set`` will respect any previously set values, and only fall back to the default if no value has ever been set for that terminal. Let's take a look at an example to see what this means in practice. .. code-block:: python class SetVsReset(Scene): def construct(self): r1 = Resistor(label=r"\mathrm{set}").shift(2 * LEFT) r2 = Resistor(label=r"\mathrm{reset}").shift(2 * RIGHT) self.add(r1, r2) self.play( r1.right.animate.set_current("I_1", out=True, below=True), r2.right.animate.reset_current("I_2", out=True, below=True), ) self.wait() self.play( r1.right.animate.set_current("i_1"), r2.right.animate.reset_current("i_2"), ) self.wait() self.play( r1.right.animate.clear_current(), r2.right.animate.clear_current(), ) self.wait() .. manim:: SetVsReset :hide_source: from manim_eng import * config_eng.debug = False class SetVsReset(Scene): def construct(self): r1 = Resistor(label=r"\mathrm{set}").shift(2 * LEFT) r2 = Resistor(label=r"\mathrm{reset}").shift(2 * RIGHT) self.add(r1, r2) self.play( r1.right.animate.set_current("I_1", out=True, below=True), r2.right.animate.reset_current("I_2", out=True, below=True), ) self.wait() self.play( r1.right.animate.set_current("i_1"), r2.right.animate.reset_current("i_2"), ) self.wait() self.play( r1.right.animate.clear_current(), r2.right.animate.clear_current(), ) self.wait() We see that ``set`` just changed the current label, but ``reset`` changed everything back to defaults. Clearing terminal current labels ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ As you may have seen in the above example, terminal labels are cleared using :meth:`~.Terminal.clear_current` (or the component equivalent :meth:`Component.clear_current`). Accessing terminals ------------------- All components have a property called :attr:`~.Component.terminals`, which is a list of the component's terminals. Regardless of component type, this list is *always* available. However, components also define a few other properties that refer to this terminal list depending on their type to make accessing terminals easier. Monopoles ^^^^^^^^^ Monopoles (such as grounds) define the :attr:`~.Monopole.terminal` property to refer to their single available terminal. Using this is often not necessary, as methods that take a :class:`~.Terminal` as input also accept a :class:`~.Monopole` (since it is obvious which terminal is intended in that situation). Bipoles ^^^^^^^ Bipoles (such as resistors, capacitors, etc.), define the :attr:`~.Bipole.left` and :attr:`~.Bipole.right` properties, which refer to their left and right terminals (when the bipole is unrotated). .. warning:: These properties do not change with component orientation! If you rotate a bipole through 180 degrees, then ``left`` will be on the right and vice versa. There are a few further special cases, which define further terminals on top of ``left`` and ``right`` --- these are sources and diodes. They define the same set of properties, but for the sake of providing API links to all of these properties, the full set is tabulated below. +-------------------+---------------------------+-------------------------------------+ | Component type | Defined property | Refers to | +===================+===========================+=====================================+ | :class:`~.Source` | :attr:`~.Source.negative` | The negative terminal of the source | | +---------------------------+ | | | :attr:`~.Source.cathode` | | | +---------------------------+-------------------------------------+ | | :attr:`~.Source.positive` | The positive terminal of the source | | +---------------------------+ | | | :attr:`~.Source.anode` | | +-------------------+---------------------------+-------------------------------------+ | :class:`~.Diode` | :attr:`~.Diode.negative` | The negative terminal of the diode | | +---------------------------+ (the terminal into which current | | | :attr:`~.Diode.cathode` | cannot pass) | | +---------------------------+-------------------------------------+ | | :attr:`~.Diode.positive` | The positive terminal of the source | | +---------------------------+ (the terminal into which current | | | :attr:`~.Diode.anode` | is allowed to pass) | +-------------------+---------------------------+-------------------------------------+ Aligning components ------------------- On top of Manim's existing alignment methods, manim-eng provides :meth:`~.Component.align_terminal` to align the terminal of a component to a point or another component's terminal. Let's take a look at an example. .. code-block:: python class AlignTerminal(Scene): def construct(self): r = Resistor().shift(UP * 1.5) l = Inductor().shift(DOWN * 1.5).rotate(-45 * DEGREES) self.add(r, l) self.play(l.animate.align_terminal("left", r.right)) .. manim:: AlignTerminal :hide_source: from manim_eng import * config_eng.debug = False class AlignTerminal(Scene): def construct(self): r = Resistor().shift(UP * 1.5) l = Inductor().shift(DOWN * 1.5).rotate(-45 * DEGREES) self.add(r, l) self.play(l.animate.align_terminal("left", r.right)) Monopoles ^^^^^^^^^ Since monopoles only have one terminal, it's a bit annoying to have to tell :meth:`~.Component.align_terminal` the terminal we mean. To overcome this, manim-eng also provides :meth:`~.Monopole.align_monopole`, which offers identical functionality but without requiring the terminal to be specified. .. code-block:: python class AlignMonopole(Scene): def construct(self): r = Resistor().shift(UP * 1.5) g = Ground().shift(DOWN * 1.5) self.add(r, g) self.play(g.animate.align_monopole(r.right)) .. manim:: AlignMonopole :hide_source: from manim_eng import * config_eng.debug = False class AlignMonopole(Scene): def construct(self): r = Resistor().shift(UP * 1.5) g = Ground().shift(DOWN * 1.5) self.add(r, g) self.play(g.animate.align_monopole(r.right)) .. rubric:: Footnotes .. [#actually] Strictly speaking the core element of manim-eng would be the :class:`~.Markable` class, but you don't need to know about that unless you want to extend manim-eng with your own elements.