Convert TwoTerminal to a more general MultiTerminal in Circ.asy.
authorW. Trevor King <wking@tremily.us>
Wed, 2 May 2012 19:15:37 +0000 (15:15 -0400)
committerW. Trevor King <wking@tremily.us>
Wed, 2 May 2012 19:55:56 +0000 (15:55 -0400)
This allows me to add switchSPDT, which has three terminals.

Label handling is also more flexible in the new system.  By default,
labels will stay in their current positions (above/below in element
coordinates for label/value).  However, the label text is no longer
rotated along with the element.  If you want rotated text, you'll have
to rotate it yourself:

  MultiTerminal Rpos = resistor(..., dir=90, ...
      label=rotate(90)*Label("my label", align=W), ...);

The benefit to this approach is that you can now set your own
alignment, which will override the default above/below positioning.

This commit also makes some whitespace normalizations and bumps
Circ.asy to version 0.2.  To upgrade code using Circ 0.1, make the
following changes:

* Replace TwoTerminal with MultiTerminal.
* Replace ang=... with dir=... in MultiTerminal initializations.
* Replace x.beg with x.terminal[0] and x.end with x.terminal[1].
* Adjust labeling as described above.
* Replace centerto with two_terminal_centerto.

asymptote/Circ-test.asy
asymptote/Circ.asy

index 44bfc7c..b57a643 100644 (file)
@@ -1,6 +1,6 @@
 /* Test suite for Circ.asy.
  *
- * Copyright (C) 2008-2009 W. Trevor King <wking@drexel.edu>
+ * Copyright (C) 2008-2012 W. Trevor King <wking@drexel.edu>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -22,67 +22,80 @@ import Circ;
 real u = 2cm, v=0;
 
 write("resistor");
-TwoTerminal Rn = resistor((0,v), 0, normal, "$R_{normal}$", "30\ohm");
-dot("beg", Rn.beg, NW, red);
-dot("end", Rn.end, NE, red);
-dot("mid", Rn.mid, S, red);
-TwoTerminal Rvb = resistor((2u,v), 0, variable, "$R_{variable}$", "30\kohm");
+MultiTerminal Rn = resistor(beg=(0,v), dir=0, type=normal,
+    label=Label("$R_{normal}$", align=N), value=Label("$30\ohm$", align=S));
+dot("beg", Rn.terminal[0], NW, red);
+dot("end", Rn.terminal[1], NE, red);
+dot("center", Rn.center, S, red);
+
+MultiTerminal Rvb = resistor(beg=(2u,v), dir=0, type=variable,
+    label=Label("$R_{variable}$", align=NW),
+    value=Label("$30\kohm$", align=SE));
 v -= u;
 
 write("capacitor");
-TwoTerminal Cn = capacitor((0,v), 0, normal, "$C_{normal}$", "30 $\mu$F");
-TwoTerminal Ce = capacitor((u,v), 0, electrolytic, "$C_{electrolytic}$", "30 $\mu$F");
-TwoTerminal Cvb = capacitor((2u,v), 0, variable, "$C_{variable}$", "30 $\mu$F");
-TwoTerminal Cvt = capacitor((3u,v), 0, variant, "$C_{variant}$", "30 $\mu$F");
+MultiTerminal Cn = capacitor((0,v), type=normal, "$C_{normal}$",
+    "30 $\mu$F");
+MultiTerminal Ce = capacitor((u,v), type=electrolytic, "$C_{electrolytic}$",
+    "30 $\mu$F");
+MultiTerminal Cvb = capacitor((2u,v), type=variable, "$C_{variable}$",
+    "30 $\mu$F");
+MultiTerminal Cvt = capacitor((3u,v), type=variant, "$C_{variant}$",
+    "30 $\mu$F");
 v -= u;
 
 write("inductor");
-TwoTerminal Lup = inductor((0,v), 0, Up, "$L_{Up}$", "30 H");
-TwoTerminal Ldown = inductor((u,v), 0, Down, "$L_{Down}$", "30 H");
+MultiTerminal Lup = inductor((0,v), type=Up, "$L_{Up}$", "30 H");
+MultiTerminal Ldown = inductor((u,v), type=Down, "$L_{Down}$", "30 H");
 v -= u;
 
 write("diode");
-TwoTerminal Dn = diode((0,v), 0, normal, "$D_{normal}$", "1.3 V");
-TwoTerminal Dz = diode((u,v), 0, zener, "$D_{zener}$", "1.3 V");
-TwoTerminal Dled = diode((2u,v), 0, LED, "$D_{LED}$", "1.7 V");
+MultiTerminal Dn = diode((0,v), type=normal, "$D_{normal}$", "1.3 V");
+MultiTerminal Dz = diode((u,v), type=zener, "$D_{zener}$", "1.3 V");
+MultiTerminal Dled = diode((2u,v), type=LED, "$D_{LED}$", "1.7 V");
 v -= u;
 
 write("battery");
-TwoTerminal B = battery((0,v), 0, "Battery", "1.5 V");
+MultiTerminal B = battery((0,v), "Battery", "1.5 V");
 v -= u;
 
 write("switch");
-TwoTerminal B = switchSPST((0,v), 0, NO, "$S_{NO}$", "Open");
-TwoTerminal B = switchSPST((u,v), 0, NC, "$S_{NC}$", "Closed");
+MultiTerminal So = switchSPST((0,v), type=open, "$S$", "Open");
+MultiTerminal Sc = switchSPST((u,v), type=closed, "$S$", "Closed");
+MultiTerminal SPDT_o = switchSPDT((2u,v), type=open, "$S$", "Open");
+MultiTerminal SPDT_a = switchSPDT((3u,v), type=closed_a, "$S$", "Closed-A");
+MultiTerminal SPDT_b = switchSPDT((4u,v), type=closed_b, "$S$", "Closed-B");
 v -= u;
 
 write("current");
-TwoTerminal Icurr = current((0,v), 0, "I", "10 A");
+MultiTerminal Icurr = current((0,v), "I", "10 A");
 v -= u;
 
 write("source");
-TwoTerminal Sdc = source((0,v), 0, DC, "DC", "5 V");
-TwoTerminal Sac = source((u,v), 0, AC, "AC", "5 V$_{pp}$");
-TwoTerminal Si = source((2u,v), 0, I, "I", "5 A");
-TwoTerminal Sv = source((3u,v), 0, V, "V", "5 V");
+MultiTerminal Sdc = source((0,v), type=DC, "DC", "5 V");
+MultiTerminal Sac = source((u,v), type=AC, "AC", "5 V$_{pp}$");
+MultiTerminal Si = source((2u,v), type=I, "I", "5 A");
+MultiTerminal Sv = source((3u,v), type=V, "V", "5 V");
 v -= u;
 
 write("lamp");
-TwoTerminal Ln = lamp((0,v), 0, normal, "indicator", "5\ohm");
-TwoTerminal Li = lamp((u,v), 0, illuminating, "illuminator", "5\ohm");
+MultiTerminal Ln = lamp((0,v), 0, normal, "indicator", "5\ohm");
+MultiTerminal Li = lamp((u,v), 0, illuminating, "illuminator", "5\ohm");
 v -= 1.5u;
 
 write("positioning");
-TwoTerminal Spos = source((u,v), 90, DC, "DC", "5 V");
-TwoTerminal Rpos = resistor((0,0), 0, normal, "+offset", "5 \ohm", draw=false);
-Rpos.centerto(Spos.beg, Spos.end, offset=u);
+MultiTerminal Spos = source((u,v), dir=90, type=DC, "DC", "5 V");
+MultiTerminal Rpos = resistor(type=normal,
+    label=rotate(90)*Label("+offset", align=W),
+    value=rotate(90)*Label("5 \ohm", align=E), draw=false);
+Rpos.centerto(Spos.terminal[0], Spos.terminal[1], offset=u);
 Rpos.draw();
-dot("Sa", Spos.beg, S);
-dot("Sb", Spos.end, N);
-dot("Ra", Rpos.beg, S);
-dot("Rb", Rpos.end, N);
-TwoTerminal Cpos = capacitor((0,0), 0, normal, "-2offset", "4 F",draw=false);
-Cpos.centerto(Spos.beg, Spos.end, offset=-2u);
+dot("Sa", Spos.terminal[0], S);
+dot("Sb", Spos.terminal[1], N);
+dot("Ra", Rpos.terminal[0], S);
+dot("Rb", Rpos.terminal[1], N);
+MultiTerminal Cpos = capacitor(type=normal, "-2offset", "4 F",draw=false);
+Cpos.centerto(Spos.terminal[0], Spos.terminal[1], offset=-2u);
 Cpos.draw();
 v -= u;
 
@@ -106,17 +119,19 @@ v -= u;
 
 write("circuit");
 v -= u/2;  // this circuit takes up more height than the previous lines.
-TwoTerminal Ccirc = capacitor((0,v), ang=90);
-TwoTerminal Rcirc = resistor(draw=false);
-Rcirc.centerto(Ccirc.beg, Ccirc.end);
+MultiTerminal Ccirc = capacitor((0,v), dir=90);
+MultiTerminal Rcirc = resistor(draw=false);
+two_terminal_centerto(Ccirc, Rcirc);
 Rcirc.shift((u,0));  // gratuitous use of shift, but whatever...
 Rcirc.draw();
 // In the following, we assume the resistor is longer than the
 // capacitor.  If that is not true, you can find the corners of the
 // circuit programatically and use those corners.
-wire(Rcirc.end, Ccirc.end, rlsq);
-wire(Rcirc.beg, Ccirc.beg, rlsq);
-pair Pcirc[] = {(Ccirc.beg.x,Rcirc.beg.y), (Ccirc.end.x,Rcirc.end.y),
-               Rcirc.end, Rcirc.beg};
+wire(Rcirc.terminal[1], Ccirc.terminal[1], rlsq);
+wire(Rcirc.terminal[0], Ccirc.terminal[0], rlsq);
+pair Pcirc[] = {
+  (Ccirc.terminal[0].x,Rcirc.terminal[0].y),
+  (Ccirc.terminal[1].x,Rcirc.terminal[1].y),
+  Rcirc.terminal[1], Rcirc.terminal[0]};
 kirchhoff_loop(Pcirc);
 v -= u;
index 7f6cc91..078ea9c 100644 (file)
@@ -1,8 +1,8 @@
 /* Useful functions for drawing circuit diagrams.
- * Version 0.1
+ * Version 0.2
  *
  * Copyright (C) 2003 GS Bustamante ArgaƱaraz
- *               2008-2011 W. Trevor King <wking@drexel.edu>
+ *               2008-2012 W. Trevor King <wking@drexel.edu>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -78,15 +78,6 @@ path bjtahead (path p)
   return (a & reverse(a) & b & reverse(b))--cycle;
 }
 
-path txtahead (path p)
-{
-  pair A = point(p,reltime(p,1.0));
-  pair B = point(p,reltime(p,0.97));
-  pair u = dirtime(p,reltime(p,1.0));
-  return A{-u}..(A - scale(7)*rotate(15)*u)--B
-                 --(A - scale(7)*rotate(-15)*u)..A{u}--cycle;
-}
-
 // function to wire the symbols together
 
 int nsq=0, udsq=2, rlsq=3;
@@ -106,183 +97,136 @@ void wire(pair pin_beg, pair pin_end, int type=nsq, real dist=0)
   } else {
     if (type==udsq) {
       draw(pin_beg--(pin_beg+(0,dist))--(pin_end.x,pin_beg.y + dist)
-          --pin_end, line);
+           --pin_end, line);
     } else if (type==rlsq) {
       draw(pin_beg--(pin_beg+(dist,0))--(pin_beg.x + dist,pin_end.y)
-          --pin_end, line);
+           --pin_end, line);
     } else {
       write("Error, unrecognized wire type ",type);
     }
   }
 }
 
-// Macro to center text among two pins
-
-bool witharrow=false, noarrow=true;
-
-void ctext(pair pin_beg, pair pin_end, string txt, bool type)
-{
-  picture ctxt;
-  if (type==noarrow) {
-    ; // pass
-  } else if (type==witharrow) {
-    draw(ctxt, pin_beg--pin_end);
-    draw(ctxt, txtahead(pin_beg--pin_end));
-    draw(ctxt, txtahead(reverse(pin_beg--pin_end)));
-  } else {
-    write("Error, unrecognized ctext type ",type);
-  }
-  label(ctxt, txt, 0.5(pin_beg+pin_end));
-  add(currentpicture, ctxt);
-}
-
-// Definition of lbsep (label separation), distance among symbol and label.
-
-real lbsep=3mm;
-
-
 // ----- Here the symbols begin --------
 
-struct TwoTerminal {
-  pair beg;
-  pair end;
-  pair mid;
-  real len;
-  real ang;
-  real lchar;
-  real lcharv;
-  string name;
-  string val;
+transform _shift_transform(pair z) = shift;
+
+struct MultiTerminal {
+  pair center;
+  real dir;
+  pair terminal[];
+  pair terminal_offset[];
+  Label label;
+  Label value;
+  real label_offset;
+  real value_offset;
   path pLine[]; // cyclic paths are filled in
   path pMisc[];
-  
-  void operator init(pair beg, real len, real ang, real lchar, real lcharv, string name, string val, path pLine[]={}, path pMisc[]={}) {
-    this.beg = beg;
-    this.end = beg+rotate(ang)*(len,0);
-    this.mid = (this.beg + this.end)/2;
-    this.len = len;
-    this.ang = ang % 360;
-    this.lchar = lchar;
-    this.lcharv = lcharv;
-    this.name = name;
-    this.val = val;
-    this.pLine = pLine;
-    this.pMisc = pMisc;
-  }
+  pen line;
+  pen misc;
 
-  void putlabel(picture pic=currentpicture) {
-    picture picL;
-    pair pName, pVal; // point
-    real rName, rVal; // rotated by
-    align aName, aVal;
-    if (labeling==rotatelabel) {
-      pName = (lchar+lbsep)*dir (90+ang);
-      pVal = (lcharv+lbsep)*dir (270+ang);
-      aName = NoAlign;
-      aVal = NoAlign;
-      if (ang <= 90 || ang > 270) {
-       rName = ang;
-       rVal = ang;
-      } else if (ang > 90 && ang <= 270) {
-       rName = 180+ang;
-       rVal = 180+ang;
-      }
-    } else if (labeling==norotatelabel) {
-      rName = 0;
-      rVal = 0;
-      if (ang == 0) {
-       pName = (lchar+.25lbsep)*dir (90+ang);
-       pVal = (lcharv+.25lbsep)*dir (270+ang);
-       aName = N;
-       aVal = S;
-      } else if (ang < 90) {
-       pName = (lchar)*dir (90+ang);
-       pVal = (lcharv)*dir (270+ang);
-       aName = NW;
-       aVal = SE;
-      } else if (ang == 90) {
-       pName = (lchar+.25lbsep)*dir (90+ang);
-       pVal = (lcharv+.25lbsep)*dir (270+ang);
-       aName = W;
-       aVal = E;
-      } else if (90 < ang && ang < 180) {
-       pName = (lchar)*dir (90+ang);
-       pVal = (lcharv)*dir (270+ang);
-       aName = SW;
-       aVal = NE;
-      } else if (ang == 180) {
-       pName = (lchar+.25lbsep)*dir (90+ang);
-       pVal = (lcharv+.25lbsep)*dir (270+ang);
-       aName = S;
-       aVal = N;
-      } else if (ang > 180 && ang < 270) {
-       pName = (lchar)*dir (90+ang);
-       pVal = (lcharv)*dir (270+ang);
-       aName = SE;
-       aVal = NW;
-      } else if (ang == 270) {
-       pName = (lchar+.25lbsep)*dir (90+ang);
-       pVal = (lcharv+.25lbsep)*dir (270+ang);
-       aName = E;
-       aVal = W;
-      } else if (270 < ang && ang < 360) {
-       pName = (lchar)*dir (90+ang);
-       pVal = (lcharv)*dir (270+ang);
-       aName = NE;
-       aVal = SW;
-      }
+  void set_terminals() {
+    for (int i=0; i < this.terminal_offset.length; i+=1) {
+      this.terminal[i] = this.center +
+          rotate(this.dir)*this.terminal_offset[i];
     }
-    Label lName = rotate(rName)*Label(name);
-    label(picL, lName, pName, aName);    
-    Label lVal = rotate(rVal)*Label(val);
-    label(picL, lVal, pVal, aVal);
-    add(pic, picL, (end-beg)/2);
+  }
+
+  void operator init(pair center=(0,0), real dir=0,
+                     pair terminal[]={}, pair terminal_offset[]={},
+                     Label label="", Label value="",
+                     real label_offset=0, real value_offset=0,
+                     path pLine[]={}, path pMisc[]={},
+                     pen line=line, pen misc=misc) {
+    this.center = center;
+    this.dir = dir % 360;
+    this.terminal = terminal;
+    this.terminal_offset = terminal_offset;
+    this.set_terminals();
+    this.label = label;
+    this.value = value;
+    this.label_offset = label_offset;
+    this.value_offset = value_offset;
+    this.pLine = pLine;
+    this.pMisc = pMisc;
+    this.line = line;
+    this.misc = misc;
   }
 
   /* Shift position by a */
   void shift(pair a) {
-    this.beg += a;
-    this.end += a;
-    this.mid += a;
+    this.center += a;
+    this.set_terminals();
   }
 
-  /* Rather than placing the element with a point and direction (beg,
-   * ang), center an element between the pairs a and b.  The optional
+  void draw_label(picture pic=currentpicture, Label L=null, real offset=0,
+                  pair default_direction=(0,0)) {
+    align a;
+    if (L == null) {
+      L = this.label;
+    }
+    a = L.align;
+    if ((L.align == NoAlign || L.align.dir == (0,0)) &&
+        default_direction != (0,0)) {
+      L.align = rotate(this.dir)*default_direction;
+    }
+    if (L.align != NoAlign && L.align != Align) {
+      real m = labelmargin(L.p);
+      real scale = (m + offset)/m;
+      if (L.align.is3D) {
+        L.align.dir3 *= scale;
+      } else {
+        L.align.dir *= scale;
+      }
+    }
+    label(pic=pic, L=L, position=this.center);
+    L.align = a;
+  }
+
+  /* Rather than placing the element with a point and direction (mid,
+   * dir), center an element between the pairs a and b.  The optional
    * offset shifts the element in the direction rotated(90)*(b-a)
    * (i.e. up for offset > 0 if b is directly right of a).
    */
   void centerto(pair a, pair b, real offset=0) {
-    this.ang = degrees(b-a);
-    this.beg = (a+b)/2
-      - unit(b-a)*this.len/2 + offset*dir(this.ang+90);
-    this.end = this.beg+rotate(ang)*(this.len,0);
-    this.mid = (this.beg + this.end)/2;
+    this.dir = degrees(b-a);
+    this.center = (a+b)/2 + offset*dir(this.dir+90);
+    this.set_terminals();
   }
 
   void draw(picture pic=currentpicture) {
-    picture picT;
     for (int i=0; i< pLine.length; i+=1) {
-      draw(picT, rotate(ang)*pLine[i], line);
-      if (cyclic(pLine[i]))
-       fill(picT, rotate(ang)*pLine[i], line);
+      path p = _shift_transform(this.center)*rotate(this.dir)*pLine[i];
+      if (cyclic(p))
+        filldraw(pic, p, this.line, this.line);
+      else
+        draw(pic, p, this.line);
     }
     for (int i=0; i< pMisc.length; i+=1) {
-      draw(picT, rotate(ang)*pMisc[i], misc);
-      if (cyclic(pMisc[i]))
-       fill(picT, rotate(ang)*pMisc[i], misc);
+      path p = _shift_transform(this.center)*rotate(this.dir)*pMisc[i];
+      if (cyclic(p))
+        filldraw(pic, p, this.misc, this.misc);
+      else
+        draw(pic, p, this.misc);
     }
-    putlabel(picT);
-    add(pic, picT, beg);
+    this.draw_label(pic=pic, L=this.label, offset=this.label_offset,
+        default_direction=N);
+    this.draw_label(pic=pic, L=this.value, offset=this.value_offset,
+        default_direction=S);
   }
 }
 
-void centerto(TwoTerminal reference, TwoTerminal target, real offset=0,
-             bool reverse=false)
+pair _offset(pair beg=(0,0), real length=1, real dir=0) {
+  return beg + rotate(dir)*(length, 0);
+}
+
+void two_terminal_centerto(MultiTerminal reference, MultiTerminal target,
+                           real offset=0, bool reverse=false)
 {
   if (reverse == false)
-    target.centerto(reference.beg, reference.end, offset);
+    target.centerto(reference.terminal[0], reference.terminal[1], offset);
   else
-    target.centerto(reference.end, reference.beg, offset);
+    target.centerto(reference.terminal[1], reference.terminal[0], offset);
 }
 
 // --- Resistor (Resistencia) ---
@@ -290,26 +234,34 @@ void centerto(TwoTerminal reference, TwoTerminal target, real offset=0,
 real rstlth=2mm;
 int normal=0, variable=2;
 
-TwoTerminal resistor(pair beg=(0,0), real ang=0, int type=normal,
-                    string name="", string val="", bool draw=true)
+MultiTerminal resistor(pair beg=(0,0), real dir=0, int type=normal,
+                       Label label="", Label value="", bool draw=true)
 {
-  path pLine, pMisc[]={};
-  TwoTerminal term;
-
-  pLine = (0,0)--(2rstlth,0)--(2.25rstlth,.75rstlth);
+  path pLine, pLines[]={}, pMisc[]={};
+  real len = 7rstlth;
+  MultiTerminal term;
+  pair center = _offset(beg=beg, length=len/2, dir=dir);
+  pair terminal_offset[] = {
+      (-len/2, 0),
+      (len/2, 0)};
+
+  pLine = (-len/2,0)--(-1.5rstlth,0)--(-1.25rstlth,.75rstlth);
   for (real i=.5; i<=2.5; i+=0.5)
-    pLine = pLine--((2.25+i)*rstlth,((-1)**(2i))*.75rstlth);
-  pLine = pLine -- (5rstlth,0)--(7rstlth,0);
+    pLine = pLine--((-1.25+i)*rstlth,((-1)**(2i))*.75rstlth);
+  pLine = pLine -- (1.5rstlth,0)--(len/2,0);
   if (type==normal) {
     ; //pass
   } else if (type==variable) {
     ahlength=.8rstlth;
-    pMisc.push((2rstlth,-rstlth)--(5.5rstlth,rstlth));
-    pMisc.push(miscahead((2rstlth,-rstlth)--(5.5rstlth,rstlth)));
+    pMisc.push((-1.5rstlth,-rstlth)--(1.5rstlth,rstlth));
+    pMisc.push(miscahead((-1.5rstlth,-rstlth)--(1.5rstlth,rstlth)));
   } else {
-    write("Error, unrecognized resistor type ",type);    
+    write("Error, unrecognized resistor type ",type);
   }
-  term = TwoTerminal(beg, 7rstlth, ang, .8rstlth, .8rstlth, name, val, pLine, pMisc);
+  pLines.push(pLine);
+  term = MultiTerminal(center=center, dir=dir, terminal_offset=terminal_offset,
+      label=label, value=value, label_offset=.8rstlth, value_offset=.8rstlth,
+      pLine=pLines, pMisc=pMisc);
   if (draw == true)
     term.draw();
   return term;
@@ -320,28 +272,33 @@ TwoTerminal resistor(pair beg=(0,0), real ang=0, int type=normal,
 real coil=2mm;
 int Up=0, Down=1;
 
-TwoTerminal inductor(pair beg=(0,0), real ang=0, int type=Up, string name="",
-                    string val="", bool draw=true)
+MultiTerminal inductor(pair beg=(0,0), real dir=0, int type=Up,
+                       Label label="", Label value="", bool draw=true)
 {
   path pLine;
-  TwoTerminal term;
-  
+  real len = 6coil;
+  MultiTerminal term;
+  pair center = _offset(beg=beg, length=len/2, dir=dir);
+  pair terminal_offset[] = {
+      (-len/2, 0),
+      (len/2, 0)};
+
+  pLine = (-len/2,0) -- (-2coil,0);
+
   if (type==Up) {
-    pLine = (0,0)--(coil,0);
-    for (int i=2; i<=4; i+=1)
+    for (int i=-1; i<=2; i+=1)
       pLine = pLine{N}..{S}(i*coil,0);
-    pLine = pLine{N}..{S}(5coil,0)--(6coil,0);
   } else if (type==Down) {
-    pLine = (0,0)--(coil,0);
-    for (int i=2; i<=4; i+=1)
+    for (int i=-1; i<=2; i+=1)
       pLine = pLine{S}..{N}(i*coil,0);
-    pLine = pLine{S}..{N}(5coil,0)--(6coil,0);
-    // the original makecirc changed labelangle to ang-180
   } else {
     write("Error, unrecognized inductor type ",type);
   }
-  term = TwoTerminal(beg, 6coil, ang, coil, coil, name, val, pLine);
-  // the original makecirc used .5coil for lcharv
+  pLine = pLine -- (len/2,0);
+
+  term = MultiTerminal(center=center, dir=dir, terminal_offset=terminal_offset,
+      label=label, value=value, label_offset=.8rstlth, value_offset=.8rstlth,
+      pLine=pLine);
   if (draw == true)
     term.draw();
   return term;
@@ -352,35 +309,42 @@ TwoTerminal inductor(pair beg=(0,0), real ang=0, int type=Up, string name="",
 real platsep=1mm;
 int normal=0, electrolytic=1, variable=2, variant=3;
 
-TwoTerminal capacitor(pair beg=(0,0), real ang=0, int type=normal,
-                     string name="", string val="", bool draw=true)
+MultiTerminal capacitor(pair beg=(0,0), real dir=0, int type=normal,
+                        Label label="", Label value="", bool draw=true)
 {
   path pLine[]={}, pMisc[]={};
-  TwoTerminal term;
+  real len = 7platsep;
+  MultiTerminal term;
+  pair center = _offset(beg=beg, length=len/2, dir=dir);
+  pair terminal_offset[] = {
+      (-len/2, 0),
+      (len/2, 0)};
 
-  pLine.push((0,0)--(3platsep,0));
-  pLine.push((4platsep,0)--(7platsep,0));
+  pLine.push((-len/2,0)--(-platsep/2,0));
+  pLine.push((platsep/2,0)--(len/2,0));
 
   if (type==normal) {
-    pLine.push((3platsep,-2.5platsep)--(3platsep,2.5platsep));
-    pLine.push((4platsep,-2.5platsep)--(4platsep,2.5platsep));
+    pLine.push((-platsep/2,-2.5platsep)--(-platsep/2,2.5platsep));
+    pLine.push((platsep/2,-2.5platsep)--(platsep/2,2.5platsep));
   } else if (type==electrolytic) {
-    pLine.push((3platsep,-1.8platsep)--(3platsep,1.8platsep));
-    pLine.push((2platsep,-2.5platsep)--(4platsep,-2.5platsep)
-              --(4platsep,+2.5platsep)--(2platsep,2.5platsep));
+    pLine.push((-platsep/2,-1.8platsep)--(-platsep/2,1.8platsep));
+    pLine.push((-1.5platsep,-2.5platsep)--(platsep/2,-2.5platsep)
+               --(platsep/2,+2.5platsep)--(-1.5platsep,2.5platsep));
   } else if (type==variable) {
-    pLine.push((3platsep,-2.5platsep)--(3platsep,2.5platsep));
-    pLine.push((4platsep,-2.5platsep)--(4platsep,2.5platsep));
-    pMisc.push((platsep,-2.5platsep)--(6platsep,2.5platsep));
+    pLine.push((-platsep/2,-2.5platsep)--(-platsep/2,2.5platsep));
+    pLine.push((platsep/2,-2.5platsep)--(platsep/2,2.5platsep));
+    pMisc.push((-2.5platsep,-2.5platsep)--(2.5platsep,2.5platsep));
     ahlength=1.7platsep;
-    pMisc.push(miscahead((platsep,-2.5platsep)--(6platsep,2.5platsep)));
+    pMisc.push(miscahead((-2.5platsep,-2.5platsep)--(2.5platsep,2.5platsep)));
   } else if (type==variant) {
-    pLine.push((3platsep,-2.5platsep)--(3platsep,2.5platsep));
-    pLine.push((4.5platsep,-2.5platsep)..(4platsep,0)..(4.5platsep,2.5platsep));
+    pLine.push((-platsep/2,-2.5platsep)--(-platsep/2,2.5platsep));
+    pLine.push((platsep,-2.5platsep)..(platsep/2,0)..(platsep,2.5platsep));
   } else {
     write("Error, unrecognized capacitor type ",type);
   }
-  term = TwoTerminal(beg, 7platsep, ang, 2.5platsep, 2.5platsep, name, val, pLine, pMisc);
+  term = MultiTerminal(center=center, dir=dir, terminal_offset=terminal_offset,
+      label=label, value=value, label_offset=2.5platsep,
+      value_offset=2.5platsep, pLine=pLine, pMisc=pMisc);
   if (draw == true)
     term.draw();
   return term;
@@ -388,41 +352,49 @@ TwoTerminal capacitor(pair beg=(0,0), real ang=0, int type=normal,
 
 // --- Diode (diodo) ---
 
-real diodeht=3.5mm;
+real diode_height = 3.5mm;
 int zener=1, LED=2;
 // I droped the pin parameters, since other device (e.g. electrolytic
 // capacitors) are also polarized.  The positioning method centerto(),
 // provides enough flexibility.
 
-TwoTerminal diode(pair beg=(0,0), real ang=0, int type=normal, string name="",
-                 string val="", bool draw=true)
+MultiTerminal diode(pair beg=(0,0), real dir=0, int type=normal,
+                    Label label="", Label value="", bool draw=true)
 {
   path pLine[]={}, pMisc[]={};
-  real lchar, lcharv;
-  TwoTerminal term;
+  real len = 3*diode_height;
+  real r = diode_height/2;
+  real label_offset, value_offset;
+  MultiTerminal term;
+  pair center = _offset(beg=beg, length=len/2, dir=dir);
+  pair terminal_offset[] = {
+      (-len/2, 0),
+      (len/2, 0)};
+
+  label_offset = value_offset = r;
+  pLine.push((-len/2,0)--(-r,0)--(-r,r)--(r,0)--(-r,-r)--(-r,0));
+  pLine.push((r,0)--(len/2,0));
 
-  pLine.push((0,0)--(diodeht,0)--(diodeht,.5diodeht)--(2diodeht,0)--(diodeht,-.5diodeht)--(diodeht,0));
-  pLine.push((2diodeht,0)--(3diodeht,0));
-  lchar = 0.6diodeht; lcharv = 0.6diodeht;
-  
   if (type==normal) {
-    pLine.push((2diodeht,-.5diodeht)--(2diodeht,.5diodeht));
+    pLine.push((r,-r)--(r,r));
   } else if (type==zener) {
-    pLine.push((2.5diodeht,-.5diodeht)--(2diodeht,-.5diodeht)--(2diodeht,.5diodeht)--(1.5diodeht,.5diodeht));
+    pLine.push((2r,-r)--(r,-r)--(r,r)--(0,r));
   } else if (type==LED) {
-    path a = (diodeht,.5diodeht)--(2diodeht,0);
-    pair u = unit((.5,1)); // perpendicular to a
-    path b = (0,0)--diodeht*u;
-    pLine.push((2diodeht,-.5diodeht)--(2diodeht,.5diodeht));
-    lchar = 1.5diodeht;
-    pMisc.push(shift(point(a,reltime(a,0.25))+point(b,0.33))*((0,0)--diodeht*u));
-    pMisc.push(shift(point(a,reltime(a,0.60))+point(b,0.33))*((0,0)--diodeht*u));
-    pMisc.push(fullhead(pMisc[0], 0.4diodeht, 30));
-    pMisc.push(fullhead(pMisc[1], 0.4diodeht, 30));
+    path a = (-r,r)--(r,0);
+    pair u = unit((0.5, 1)); // perpendicular to a
+    path b = (0,0)--diode_height*u;
+    pLine.push((r,-r)--(r,r));
+    label_offset = 1.5*diode_height;
+    pMisc.push(shift(point(a,reltime(a,0.25))+point(b,0.33))*((0,0)--diode_height*u));
+    pMisc.push(shift(point(a,reltime(a,0.60))+point(b,0.33))*((0,0)--diode_height*u));
+    pMisc.push(fullhead(pMisc[0], 0.4*diode_height, 30));
+    pMisc.push(fullhead(pMisc[1], 0.4*diode_height, 30));
   } else {
-    write("Error, unrecognized capacitor type ",type);
+    write("Error, unrecognized diode type ",type);
   }
-  term = TwoTerminal(beg, 3diodeht, ang, lchar, lcharv, name, val,pLine,pMisc);
+  term = MultiTerminal(center=center, dir=dir, terminal_offset=terminal_offset,
+      label=label, value=value, label_offset=label_offset,
+      value_offset=value_offset, pLine=pLine, pMisc=pMisc);
   if (draw == true)
     term.draw();
   return term;
@@ -430,25 +402,34 @@ TwoTerminal diode(pair beg=(0,0), real ang=0, int type=normal, string name="",
 
 //--- Battery (bater'ia) ---
 
-real bsize = 6mm;
+real battery_size = 6mm;
 
-TwoTerminal battery(pair beg=(0,0), real ang=0, string name="", string val="",
-                   bool draw=true)
+MultiTerminal battery(pair beg=(0,0), real dir=0,
+                      Label label="", Label value="", bool draw=true)
 {
   path pLine[]={}, pMisc[]={};
-  real lchar, lcharv;
-  TwoTerminal term;
-
-  pLine.push((0,0)--(0.4bsize,0));
-  pLine.push((1.4bsize,0)--(1.8bsize,0));
-  
+  real len = 1.8*battery_size;
+  real r = battery_size/2;
+  real x = 0.4*battery_size;
+  real Y = 0.6*battery_size;
+  real y = 0.2*battery_size;
+  real label_offset, value_offset;
+  MultiTerminal term;
+  pair center = _offset(beg=beg, length=len/2, dir=dir);
+  pair terminal_offset[] = {
+      (-len/2, 0),
+      (len/2, 0)};
+
+  label_offset = value_offset = Y;
+  pLine.push((-len/2,0)--(-r,0));
+  pLine.push((r,0)--(len/2,0));
   for (int i=0; i<3; i+=1) {
-    pLine.push(((0.4+0.4i)*bsize, -0.2bsize)--((0.4+0.4i)*bsize, 0.2bsize));
-    pLine.push(((0.6+0.4i)*bsize, -0.6bsize)--((0.6+0.4i)*bsize, 0.6bsize));
+    pLine.push((-r+x*i, -y)--(-r+x*i, y));
+    pLine.push((-r+x*(i+0.5), -Y)--(-r+x*(i+0.5), Y));
   }
-  lchar = 0.6bsize; lcharv = 0.6bsize;
-
-  term = TwoTerminal(beg, 1.8bsize, ang, lchar, lcharv, name, val,pLine,pMisc);
+  term = MultiTerminal(center=center, dir=dir, terminal_offset=terminal_offset,
+      label=label, value=value, label_offset=label_offset,
+      value_offset=value_offset, pLine=pLine, pMisc=pMisc);
   if (draw == true)
     term.draw();
   return term;
@@ -456,31 +437,83 @@ TwoTerminal battery(pair beg=(0,0), real ang=0, string name="", string val="",
 
 //--- Switches (Llaves) ---
 
-int NO=0, NC=1;
-real ssep=3mm, swt=1.2ssep; // TODO remove swt?
+int open=0, closed=1;
+real switch_size = 3mm;
+
+/* Helper function for constructing switch paths. */
+path[] _switch_lines(pair terminal_offset[], real theta)
+{
+  path pLine[] = {};
+  real r = switch_size/2;
+  real dy = switch_size/6;  // little nub where the switch closes
+  pLine.push((-r,0)--((-r,0)+switch_size*dir(theta)));
+  for (int i=0; i < terminal_offset.length; i+=1) {
+    pLine.push(shift(terminal_offset[i])*scale(dy)*unitcircle);
+  }
+  return pLine;
+}
 
 /* `switch' is a Asymptote keyword (or it should be), so append SPST
  * for Single Pole Single Throw.
  */
-TwoTerminal switchSPST(pair beg=(0,0), real ang=0, int type=NO, string name="",
-                      string val="", bool draw=true)
+MultiTerminal switchSPST(pair beg=(0,0), real dir=0, int type=closed,
+                         Label label="", Label value="", bool draw=true)
 {
-  path pLine[]={}, pMisc[]={};
-  real lchar, lcharv;
-  TwoTerminal term;
+  path pLine[] = {};
+  real theta, label_offset, value_offset;
+  MultiTerminal term;
+  pair center = _offset(beg=beg, length=switch_size/2, dir=dir);
+  pair terminal_offset[] = {
+      (-switch_size/2, 0),
+      (switch_size/2, 0)};
+
+  value_offset = 0;
+  if (type==open) {
+    theta = 35;
+  } else if (type==closed) {
+    theta = 10;
+  } else {
+    write("Error, unrecognized switchSPST type ",type);
+  }
+  pLine = _switch_lines(terminal_offset=terminal_offset, theta=theta);
+  label_offset = switch_size*Sin(theta);
+  term = MultiTerminal(center=center, dir=dir, terminal_offset=terminal_offset,
+      label=label, value=value, label_offset=label_offset,
+      value_offset=value_offset, pLine=pLine);
+  if (draw == true)
+    term.draw();
+  return term;
+}
 
-  pLine.push((0,0)--(0.7ssep,0));
-  pLine.push((1.7ssep,ssep/3)--(1.7ssep,0)--(2.4ssep,0));
-  lchar = 0.6ssep; lcharv = 0.6ssep;
+int closed_a=1, closed_b=2;
 
-  if (type==NO) {
-    pLine.push((0.7ssep,0)--(1.8ssep,0.7ssep));
-  } else if (type==NC) {
-    pLine.push((0.7ssep,0)--(2ssep,ssep/3));
+/* A Single Pole Double Throw switch. */
+MultiTerminal switchSPDT(pair beg=(0,0), real dir=0, int type=open,
+                         Label label="", Label value="", bool draw=true)
+{
+  path pLine[] = {};
+  real theta, label_offset, value_offset;
+  MultiTerminal term;
+  pair center = _offset(beg=beg, length=switch_size/2, dir=dir);
+  pair terminal_offset[] = {
+      (-switch_size/2, 0),
+      (switch_size*sqrt(3)/4, switch_size/2),
+      (switch_size*sqrt(3)/4, -switch_size/2)};
+
+  label_offset = value_offset = switch_size/2;
+  if (type==open) {
+    theta = 0;
+  } else if (type==closed_a) {
+    theta = 20;
+  } else if (type==closed_b) {
+    theta = -20;
   } else {
     write("Error, unrecognized switchSPST type ",type);
   }
-  term = TwoTerminal(beg, 2.4ssep, ang, lchar, lcharv, name, val, pLine,pMisc);
+  pLine = _switch_lines(terminal_offset=terminal_offset, theta=theta);
+  term = MultiTerminal(center=center, dir=dir, terminal_offset=terminal_offset,
+      label=label, value=value, label_offset=label_offset,
+      value_offset=value_offset, pLine=pLine);
   if (draw == true)
     term.draw();
   return term;
@@ -488,20 +521,27 @@ TwoTerminal switchSPST(pair beg=(0,0), real ang=0, int type=NO, string name="",
 
 //--- Current (Corriente) ---
 
-real isize=2mm;
+real current_size = 2mm;
 
-// adjusted from makecirc original to center arrowhead under text
-TwoTerminal current(pair beg=(0,0), real ang=0, string name="", string val="",
-                   bool draw=true)
+MultiTerminal current(pair beg=(0,0), real dir=0,
+                      Label label="", Label value="", bool draw=true)
 {
   path pLine[]={}, pMisc[]={};
-  real lchar, lcharv;
-  TwoTerminal term;
-  lchar = 0.4isize; lcharv = 0.4isize;
-  pLine.push((0,0)--(1.5isize,0));
-  pLine.push((1.5isize,0)--(2isize,0));
-  pMisc.push(fullhead(pLine[0], isize, 45));
-  term = TwoTerminal(beg, 2isize, ang, lchar, lcharv, name, val, pLine, pMisc);
+  real label_offset, value_offset;
+  real len = 2*current_size;
+  MultiTerminal term;
+  pair center = _offset(beg=beg, length=len/2, dir=dir);
+  pair terminal_offset[] = {
+      (-len/2, 0),
+      (len/2, 0)};
+
+  label_offset = value_offset = 0.4*current_size;
+  pLine.push((-len/2, 0) -- (current_size/2, 0));
+  pLine.push((current_size/2, 0) -- (len/2, 0));
+  pMisc.push(fullhead(pLine[0], current_size, 45));
+  term = MultiTerminal(center=center, dir=dir, terminal_offset=terminal_offset,
+      label=label, value=value, label_offset=label_offset,
+      value_offset=value_offset, pLine=pLine, pMisc=pMisc);
   if (draw == true)
     term.draw();
   return term;
@@ -509,23 +549,28 @@ TwoTerminal current(pair beg=(0,0), real ang=0, string name="", string val="",
 
 //--- Circle-based symbols (for internal use) --
 
-real circle_size=6mm;
+real circle_size = 6mm;
 
-TwoTerminal _two_terminal_circle(pair beg=(0,0), real ang=0, string name="",
-                                 string val="")
+MultiTerminal _circle_symbol(pair beg=(0,0), real dir=0,
+                             Label label="", Label value="")
 {
-  path pLine[]={}, pMisc[]={};
-  real len, lchar, lcharv, r = circle_size/2;
-  pair c = (2r,0);
-  TwoTerminal term;
-
-  len = 2*circle_size;
-  lchar = lcharv = r;
-  c = (circle_size, 0);
-  pLine.push((0,0)--(r,0));
-  pLine.push(shift(c)*scale(r)*(E..N..W..S..E));
-  pLine.push((3r,0)--(4r,0));
-  term = TwoTerminal(beg, len, ang, lchar, lcharv, name, val, pLine, pMisc);
+  path pLine[]={};
+  real label_offset, value_offset;
+  real len = 2*circle_size;
+  real r = circle_size/2;
+  MultiTerminal term;
+  pair center = _offset(beg=beg, length=r, dir=dir);
+  pair terminal_offset[] = {
+      (-len/2, 0),
+      (len/2, 0)};
+
+  label_offset = value_offset = r;
+  pLine.push((-len/2, 0) -- (-r, 0));
+  pLine.push((r, 0) -- (len/2, 0));
+  pLine.push(scale(r)*(E..N..W..S..E));
+  term = MultiTerminal(center=center, dir=dir, terminal_offset=terminal_offset,
+      label=label, value=value, label_offset=label_offset,
+      value_offset=value_offset, pLine=pLine);
   return term;
 }
 
@@ -533,34 +578,45 @@ TwoTerminal _two_terminal_circle(pair beg=(0,0), real ang=0, string name="",
 
 int AC=0,DC=1,I=2,V=3;
 
-TwoTerminal source(pair beg=(0,0), real ang=0, int type=AC, string name="",
-                   string val="", bool draw=true)
+MultiTerminal source(pair beg=(0,0), real dir=0, int type=AC,
+                     Label label="", Label value="", bool draw=true)
 {
-  TwoTerminal term;
+  MultiTerminal term;
 
   if (type == AC || type == I || type == V) {
-    real s = circle_size;
-    term = _two_terminal_circle(beg=beg, ang=ang, name=name, val=val);
+    real r = circle_size/2;
+    term = _circle_symbol(beg=beg, dir=dir, label=label, value=value);
     if (type == AC) {
-      term.pLine.push((2s/3,0){NE}..{E}((1/3+.5)*s,.2s)..{SE}(s,0)..{E}((2/3+.5)*s,-.2s)..{NE}(4s/3,0));
+      term.pLine.push((-2r/3,0){NE}..{E}(-r/3,.4r)..{SE}(0,0)..{E}(r/3,-.4r)..{NE}(2r/3,0));
     } else if (type == I) {
-      term.pLine.push((2s/3,0)--(4s/3,0));
-      term.pLine.push(fullhead(term.pLine[3], 4s/15, 30));
+      term.pLine.push((-2r/3,0)--(2r/3,0));
+      term.pLine.push(fullhead(term.pLine[3], 8r/15, 30));
     } else if (type == V) {
-      term.pLine.push((1.05s,0)--(1.45s,0));
-      term.pLine.push((1.25s,-.2s)--(1.25s,.2s));
-      term.pLine.push((.95s,0)--(.55s,0));      
+      term.pLine.push((-0.9r,0)--(-0.1r,0));
+      term.pLine.push((0.5r,-.4r)--(0.5r,.4r));
+      term.pLine.push((0.1r,0)--(0.9r,0));
     }
   } else if (type == DC) {
-    path pLine[] = {};
-    real len, lchar, lcharv;
-    len = circle_size;
-    lchar = 0.6len; lcharv = .6len;
-    pLine.push((0,0)--(0.4len,0));
-    pLine.push((.6len,0)--(len,0));
-    pLine.push((.4len,-.2len)--(.4len,.2len));
-    pLine.push((.6len,-.6len)--(.6len,.6len));
-    term = TwoTerminal(beg, len, ang, lchar, lcharv, name, val, pLine);
+    path pLine[]={};
+    real label_offset, value_offset;
+    real len = battery_size;
+    real x = 0.2*battery_size;
+    real Y = 0.6*battery_size;
+    real y = 0.2*battery_size;
+    pair center = _offset(beg=beg, length=len/2, dir=dir);
+    pair terminal_offset[] = {
+        (-len/2, 0),
+        (len/2, 0)};
+
+    label_offset = value_offset = Y;
+    pLine.push((-len/2,0)--(-x/2,0));
+    pLine.push((x/2,0)--(len/2,0));
+    pLine.push((-x/2, -y)--(-x/2, y));
+    pLine.push((x/2, -Y)--(x/2, Y));
+    term = MultiTerminal(center=center, dir=dir,
+        terminal_offset=terminal_offset,
+        label=label, value=value, label_offset=label_offset,
+        value_offset=value_offset, pLine=pLine);
   }
   if (draw == true)
     term.draw();
@@ -690,7 +746,7 @@ TwoTerminal motor(pair beg, real ang, string name, string val)
 {
   path pLine;
   TwoTerminal term;
-  
+
   if (type==Up) {
     pLine = (0,0)--(coil,0);
     for (int i=2; i<=4; i+=1)
@@ -888,22 +944,20 @@ enddef;
 
 int illuminating = 1;
 
-TwoTerminal lamp(pair beg=(0,0), real ang=0, int type=normal,
-                 string name="", string val="", bool draw=true)
+MultiTerminal lamp(pair beg=(0,0), real dir=0, int type=normal,
+                   Label label="", Label value="", bool draw=true)
 {
   real r = 0.5*circle_size;
-  pair c;
-  TwoTerminal term;
+  MultiTerminal term;
 
-  term = _two_terminal_circle(beg=beg, ang=ang, name=name, val=val);
-  pair c = (2r, 0);
+  term = _circle_symbol(beg=beg, dir=dir, label=label, value=value);
   if (type==normal) {
-    term.pLine.push((c - r*dir(45)) -- (c + r*dir(45)));
-    term.pLine.push((c - r*dir(-45)) -- (c + r*dir(-45)));
+    term.pLine.push(-r*dir(45) -- r*dir(45));
+    term.pLine.push(-r*dir(-45) -- r*dir(-45));
   } else if (type==illuminating) {
-    term.pLine.push((c - (r,0)) -- (c - (r/2,0)));
-    term.pLine.push((c + (r/2,0)) -- (c + (r,0)));    
-    term.pLine.push(shift(c)*scale(r/2)*(E..N..W));
+    term.pLine.push((-r,0) -- (-r/2,0));
+    term.pLine.push((r/2,0) -- (r,0));
+    term.pLine.push(scale(r/2)*(E..N..W));
   }
   if (draw == true)
     term.draw();
@@ -1010,7 +1064,7 @@ enddef;
 // Loop symbols for Kirchhoff's rules.
 
 void kirchhoff_loop(picture pic=currentpicture, pair points[],
-                   pen outline=kirchhoff_pen, real aspace=-1) {
+                    pen outline=kirchhoff_pen, real aspace=-1) {
   // draw kirchhoff loop underneath currentpicture
   picture newpic;
   int i;