Customizing Elements

Reusing groups of elements

If a set of circuit elements are to be reused multiple times, they can be grouped into a single element. Create and populate a drawing, but don’t call draw on it. Instead, use group_elements(), then add the result as an element to another drawing

SchemDraw.group_elements(drawing, anchors=None)

Create a new element definition based on all the elements in the drawing.

Parameters
  • drawing – SchemDraw.Drawing with elements to group

  • anchors – dictionary of anchor names: locations within the group

d1 = SchemDraw.Drawing()
d1.add(elm.RES)
d1.push()
d1.add(elm.CAP, d='down')
d1.add(elm.LINE, d='left')
d1.pop()
RC = SchemDraw.group_elements(d1)   # Create the group to reuse

d2 = SchemDraw.Drawing()   # Add the group to another drawing several times
for i in range(3):
    d2.add(RC)
d2.draw()
../_images/customizing_1_0.svg

Defining custom elements

New elements can be defined by creating a dictionary describing how the element should be drawn. An element is made up of paths and/or shapes. A path is simply a list of xy coordinates (drawn using Matplotlib’s plot function). A shape can be a circle, polygon, arrow, or arc (a Matplotlib patch).

Coordinates are all defined in element cooridnates, where the element begins at [0, 0] and is drawn from left to right. The drawing engine will then rotate and translate the element to its final position. A standard resistor is 1 drawing unit long, and with default lead extension will become 3 units long.

Possible dictionary keys:

  • name: A name string for the element. Currently only used for documentation and testing.

  • paths: A list of each path line in the element. For example, a capacitor has two paths, one for each capacitor “plate”. On 2-terminal elements, the leads will be automatically extended away from the first and last points of the first path, and don’t need to be included in the path.

  • base: Dictionary defining a base element. For example, the variable resistor has a base of resistor, then adds an additional path.

  • shapes: A list of shape dictionaries. See below for options.

  • theta: Default angle (in degrees) for the element. Overrides the current drawing angle.

  • anchors: A dictionary defining named positions within the element. For example, the NFET element has a ‘source’, ‘gate’, and ‘drain’ anchor. Each anchor will become an attribute of the element class which can then be used for connecting other elements.

  • extend (bool) Extend the leads to fill the full element length.

  • move_cur: (bool) Move the drawing cursor location after drawing.

  • color: A matplotlib-compatible color for the element. Examples include ‘red’, ‘blue’, ‘#34ac92’

  • drop: Final location to leave drawing cursor.

  • lblloc: [‘top’, ‘bot’, ‘lft’, ‘rgt’] default location for text label. Defaults to ‘top’.

  • lblofst: Default distance between element and text label.

  • labels: List of (label, pos) tuples defining text labels to always draw in the element.

  • ls: [‘:’, ‘–’, ‘-‘] linestyle (same as matplotlib). Only applies to paths.

In the shapes list, each shape is defined by a dictionary. All shape dictionaries contain

  • shape: key can be [ ‘circle’, ‘poly’, ‘arc’, ‘arrow’ ]

  • zorder: drawing order within the element

The remaining keys depend on the type of shape as follows.

Circle:

  • center: [x, y] center coordinate

  • radius: radius of circle

  • fill: (bool) fill the circle

  • fillcolor: color for fill

Poly:

  • xy: List of xy coordinates defining polygon

  • closed: (bool) Close the polygon

  • fill: (bool) fill the polygon

  • fillcolor: color for fill

Arc:

  • center: Center coordinate of arc

  • width, height’ width and height of arc

  • theta1: Starting angle (degrees)

  • theta2: Ending angle (degrees)

  • angle: Rotation angle of entire arc

  • arrow: [‘cw’, ‘ccw’] Add an arrowhead, clockwise or counterclockwise

Arrow:

  • start: [x, y] start of arrow

  • end: [x, y] end of arrow

  • headwidth: width of arrowhead

  • headlength: length of arrowhead

Here’s the definition of our favorite element, the resistor:

RES = {
    'name': 'RES',
    'paths': [
              [[0, 0], [0.5*_rw, _rh], [1.5*_rw, -_rh], [2.5*_rw, _rh], [3.5*_rw, -_rh], [4.5*_rw, _rh], [5.5*_rw, -_rh], [6*_rw, 0]]
             ]
      }

The resistor is made of just one path. _rw and _rh are constants that define the height and width of the resistor. Browse the source code in elements.py to see the definitions of the other built-in elements.

Flux Capacitor Example

For an example, let’s make a flux capacitor circuit element. Here, we’ll start by defining the fclen variable as the length of one leg so we can change it easily. Remember a resistor is 1 unit long.

fclen = 0.5

The custom element is a dictionary of parameters. We want a dot in the center of our flux capacitor, so use the base key to start with the already defined DOT element.

FLUX_CAP = {
    'base': elm.DOT,

Next, add the paths, which are drawn as lines. The flux capacitor will have three paths, all extending from the center dot:

'paths': [[[0, 0], [0, -fclen*1.41]],  # Leg going down
          [[0, 0], [fclen, fclen]],    # Leg going up/right
          [[0, 0], [-fclen, fclen]]],  # Leg going up/left

And at the end of each path is an open circle. These are added to the dictionary using the shapes key as a list of shape dictionaries.

'shapes': [{'shape': 'circle', 'center': [0, -fclen*1.41], 'radius': .2, 'fill': False},
           {'shape': 'circle', 'center': [fclen, fclen], 'radius': .2, 'fill': False},
           {'shape': 'circle', 'center': [-fclen, fclen], 'radius': .2, 'fill': False}],

Finally, we need to define anchor points so that other elements can be connected to the right places. Here, they’re called p1, p2, and p3 for lack of better names (what do you call the inputs to a flux capacitor?)

'anchors': {'p1': [-fclen, fclen], 'p2': [fclen, fclen], 'p3': [0, -fclen]}

Here’s the element dictionary all in one:

fclen = 0.5
FLUX_CAP = {
    'base': elm.DOT,
    'paths': [[[0, 0], [0, -fclen*1.41]],  # Leg going down
              [[0, 0], [fclen, fclen]],    # Leg going up/right
              [[0, 0], [-fclen, fclen]]],  # Leg going up/left
    'shapes': [{'shape': 'circle', 'center': [0, -fclen*1.41], 'radius': .2, 'fill': False},
               {'shape': 'circle', 'center': [fclen, fclen], 'radius': .2, 'fill': False},
               {'shape': 'circle', 'center': [-fclen, fclen], 'radius': .2, 'fill': False}],
    'anchors': {'p1': [-fclen, fclen], 'p2': [fclen, fclen], 'p3': [0, -fclen]}
    }

Test it out by adding the new custom element to a drawing:

d = SchemDraw.Drawing()
fc = d.add(FLUX_CAP)
d.draw()
../_images/customizing_3_0.svg

Segment objects

Each path and shape in the element definition is translated into drawing coordinates and becomes a SchemDraw.Segment object contained in segments list attribute of the SchemDraw.Element instance. For even more control over individual pieces of an element, the parameters of a Segment can be changed.

n = d.add(logic.NAND2)
n.segments[-1].color = 'red'
n.segments[-1].zorder = 5  # Put the bubble on top
d.draw()
../_images/customizing_5_0.svg

Matplotlib axis

As a final customization option, remember that SchemDraw draws everything on a Matplotlib axis. This axis can be obtained using plt.gca() and used for whatever purpose.

import matplotlib.pyplot as plt
d = SchemDraw.Drawing()
d.add(elm.RES)
d.draw()
ax = plt.gca()
ax.axvline(.5, color='purple', ls='--')
ax.axvline(2.5, color='orange', ls='-', lw=3);
../_images/customizing_6_0.svg