Buscar

Programa Circuits para análise de circuitos elétricos

Esta é uma pré-visualização de arquivo. Entre para ver o arquivo original

ACRailElm.java
 class ACRailElm extends RailElm {
	public ACRailElm(int xx, int yy) { super(xx, yy, WF_AC); }
	Class getDumpClass() { return RailElm.class; }
	int getShortcut() { return 0; }
 }
ACVoltageElm.java
 class ACVoltageElm extends VoltageElm {
	public ACVoltageElm(int xx, int yy) { super(xx, yy, WF_AC); }
	Class getDumpClass() { return VoltageElm.class; }
 }
CC2Elm.java
import java.awt.*;
import java.util.StringTokenizer;
 class CC2Elm extends ChipElm {
	double gain;
	public CC2Elm(int xx, int yy) { super(xx, yy); gain = 1; }
	public CC2Elm(int xx, int yy, int g) { super(xx, yy); gain = g; }
	public CC2Elm(int xa, int ya, int xb, int yb, int f,
		 StringTokenizer st) {
	 super(xa, ya, xb, yb, f, st);
	 gain = new Double(st.nextToken()).doubleValue();
	}
	String dump() {
	 return super.dump() + " " + gain;
	}
	String getChipName() { return "CC2"; }
	void setupPins() {
	 sizeX = 2;
	 sizeY = 3;
	 pins = new Pin[3];
	 pins[0] = new Pin(0, SIDE_W, "X");
	 pins[0].output = true;
	 pins[1] = new Pin(2, SIDE_W, "Y");
	 pins[2] = new Pin(1, SIDE_E, "Z");
	}
	void getInfo(String arr[]) {
	 arr[0] = (gain == 1) ? "CCII+" : "CCII-";
	 arr[1] = "X,Y = " + getVoltageText(volts[0]);
	 arr[2] = "Z = " + getVoltageText(volts[2]);
	 arr[3] = "I = " + getCurrentText(pins[0].current);
	}
	//boolean nonLinear() { return true; }
	void stamp() {
	 // X voltage = Y voltage
	 sim.stampVoltageSource(0, nodes[0], pins[0].voltSource);
	 sim.stampVCVS(0, nodes[1], 1, pins[0].voltSource);
	 // Z current = gain * X current
	 sim.stampCCCS(0, nodes[2], pins[0].voltSource, gain);
	}
	void draw(Graphics g) {
	 pins[2].current = pins[0].current * gain;
	 drawChip(g);
	}
	int getPostCount() { return 3; }
	int getVoltageSourceCount() { return 1; }
	int getDumpType() { return 179; }
 }
class CC2NegElm extends CC2Elm {
 public CC2NegElm(int xx, int yy) { super(xx, yy, -1); }
 Class getDumpClass() { return CC2Elm.class; }
}
ADCElm.java
import java.awt.*;
import java.util.StringTokenizer;
class ADCElm extends ChipElm {
 public ADCElm(int xx, int yy) { super(xx, yy); }
 public ADCElm(int xa, int ya, int xb, int yb, int f,
		 StringTokenizer st) {
	super(xa, ya, xb, yb, f, st);
 }
 String getChipName() { return "ADC"; }
 boolean needsBits() { return true; }
 void setupPins() {
	sizeX = 2;
	sizeY = bits > 2 ? bits : 2;
	pins = new Pin[getPostCount()];
	int i;
	for (i = 0; i != bits; i++) {
	 pins[i] = new Pin(bits-1-i, SIDE_E, "D" + i);
	 pins[i].output = true;
	}
	pins[bits] = new Pin(0, SIDE_W, "In");
	pins[bits+1] = new Pin(sizeY-1, SIDE_W, "V+");
	allocNodes();
 }
 void execute() {
	int imax = (1<<bits)-1;
	// if we round, the half-flash doesn't work
	double val = imax*volts[bits]/volts[bits+1]; // + .5;
	int ival = (int) val;
	ival = min(imax, max(0, ival));
	int i;
	for (i = 0; i != bits; i++)
	 pins[i].value = ((ival & (1<<i)) != 0);
 }
 int getVoltageSourceCount() { return bits; }
 int getPostCount() { return bits+2; }
 int getDumpType() { return 167; }
}
 
EditOptions.java
class EditOptions implements Editable {
 CirSim sim;
 public EditOptions(CirSim s) { sim = s; }
 public EditInfo getEditInfo(int n) {
	if (n == 0)
	 return new EditInfo("Time step size (s)", sim.timeStep, 0, 0);
	if (n == 1)
	 return new EditInfo("Range for voltage color (V)",
				CircuitElm.voltageRange, 0, 0);
	 
	return null;
 }
 public void setEditValue(int n, EditInfo ei) {
	if (n == 0 && ei.value > 0)
	 sim.timeStep = ei.value;
	if (n == 1 && ei.value > 0)
	 CircuitElm.voltageRange = ei.value;
 }
};
AnalogSwitch2Elm.java
import java.awt.*;
import java.util.StringTokenizer;
class AnalogSwitch2Elm extends AnalogSwitchElm {
 public AnalogSwitch2Elm(int xx, int yy) {
	super(xx, yy);
 }
 public AnalogSwitch2Elm(int xa, int ya, int xb, int yb, int f,
			 StringTokenizer st) {
	super(xa, ya, xb, yb, f, st);
 }
 final int openhs = 16;
 Point swposts[], swpoles[], ctlPoint;
 void setPoints() {
	super.setPoints();
	calcLeads(32);
	swposts = newPointArray(2);
	swpoles = newPointArray(2);
	interpPoint2(lead1, lead2, swpoles[0], swpoles[1], 1, openhs);
	interpPoint2(point1, point2, swposts[0], swposts[1], 1, openhs);
	ctlPoint = interpPoint(point1, point2, .5, openhs);
 }
 int getPostCount() { return 4; }
 void draw(Graphics g) {
	setBbox(point1, point2, openhs);
	// draw first lead
	setVoltageColor(g, volts[0]);
	drawThickLine(g, point1, lead1);
	// draw second lead
	setVoltageColor(g, volts[1]);
	drawThickLine(g, swpoles[0], swposts[0]);
	 
	// draw third lead
	setVoltageColor(g, volts[2]);
	drawThickLine(g, swpoles[1], swposts[1]);
	// draw switch
	g.setColor(lightGrayColor);
	int position = (open) ? 1 : 0;
	drawThickLine(g, lead1, swpoles[position]);
	 
	updateDotCount();
	drawDots(g, point1, lead1, curcount);
	drawDots(g, swpoles[position], swposts[position], curcount);
	drawPosts(g);
 }
	
 Point getPost(int n) {
	return (n == 0) ? point1 : (n == 3) ? ctlPoint : swposts[n-1];
 }
 int getDumpType() { return 160; }
 void calculateCurrent() {
	if (open)
	 current = (volts[0]-volts[2])/r_on;
	else
	 current = (volts[0]-volts[1])/r_on;
 }
	
 void stamp() {
	sim.stampNonLinear(nodes[0]);
	sim.stampNonLinear(nodes[1]);
	sim.stampNonLinear(nodes[2]);
 }
 void doStep() {
	open = (volts[3] < 2.5);
	if ((flags & FLAG_INVERT) != 0)
	 open = !open;
	if (open) {
	 sim.stampResistor(nodes[0], nodes[2], r_on);
	 sim.stampResistor(nodes[0], nodes[1], r_off);
	} else {
	 sim.stampResistor(nodes[0], nodes[1], r_on);
	 sim.stampResistor(nodes[0], nodes[2], r_off);
	}
 }
	
 boolean getConnection(int n1, int n2) {
	if (n1 == 3 || n2 == 3)
	 return false;
	return true;
 }
 void getInfo(String arr[]) {
	arr[0] = "analog switch (SPDT)";
	arr[1] = "I = " + getCurrentDText(getCurrent());
 }
}
AnalogSwitchElm.java
import java.awt.*;
import java.util.StringTokenizer;
class AnalogSwitchElm extends CircuitElm {
 final int FLAG_INVERT = 1;
 double resistance, r_on, r_off;
 public AnalogSwitchElm(int xx, int yy) {
	super(xx, yy);
	r_on = 20;
	r_off = 1e10;
 }
 public AnalogSwitchElm(int xa, int ya, int xb, int yb, int f,
			 StringTokenizer st) {
	super(xa, ya, xb, yb, f);
	r_on = 20;
	r_off = 1e10;
	try {
	 r_on = new Double(st.nextToken()).doubleValue();
	 r_off = new Double(st.nextToken()).doubleValue();
	} catch (Exception e) { }
	
 }
 String dump() {
	return super.dump() + " " + r_on + " " + r_off;
 }
 
 int getDumpType() { return 159; }
 boolean open;
	
 Point ps, point3, lead3;
 void setPoints() {
	super.setPoints();
	calcLeads(32);
	ps = new Point();
	int openhs = 16;
	point3 = interpPoint(point1, point2, .5, -openhs);
	lead3 = interpPoint(point1, point2, .5, -openhs/2);
 }
	
 void draw(Graphics g) {
	int openhs = 16;
	int hs = (open) ? openhs : 0;
	setBbox(point1, point2, openhs);
	draw2Leads(g);
	 
	g.setColor(lightGrayColor);
	interpPoint(lead1, lead2, ps, 1, hs);
	drawThickLine(g, lead1, ps);
	setVoltageColor(g, volts[2]);
	drawThickLine(g, point3, lead3);
	 
	if (!open)
	 doDots(g);
	drawPosts(g);
 }
 void calculateCurrent() {
	current = (volts[0]-volts[1])/resistance;
 }
	
 // we need this to be able to change the matrix for each step
 boolean nonLinear() { return true; }
 void stamp() {
	sim.stampNonLinear(nodes[0]);
	sim.stampNonLinear(nodes[1]);
 }
 void doStep() {
	open = (volts[2] < 2.5);
	if ((flags & FLAG_INVERT) != 0)
	 open = !open;
	resistance = (open)
? r_off : r_on;
	sim.stampResistor(nodes[0], nodes[1], resistance);
 }
 void drag(int xx, int yy) {
	xx = sim.snapGrid(xx);
	yy = sim.snapGrid(yy);
	if (abs(x-xx) < abs(y-yy))
	 xx = x;
	else
	 yy = y;
	int q1 = abs(x-xx)+abs(y-yy);
	int q2 = (q1/2) % sim.gridSize;
	if (q2 != 0)
	 return;
	x2 = xx; y2 = yy;
	setPoints();
 }
 int getPostCount() { return 3; }
 Point getPost(int n) {
	return (n == 0) ? point1 : (n == 1) ? point2 : point3;
 }
 void getInfo(String arr[]) {
	arr[0] = "analog switch";
	arr[1] = open ? "open" : "closed";
	arr[2] = "Vd = " + getVoltageDText(getVoltageDiff());
	arr[3] = "I = " + getCurrentDText(getCurrent());
	arr[4] = "Vc = " + getVoltageText(volts[2]);
 }
 // we have to just assume current will flow either way, even though that
 // might cause singular matrix errors
 boolean getConnection(int n1, int n2) {
	if (n1 == 2 || n2 == 2)
	 return false;
	return true;
 }
 public EditInfo getEditInfo(int n) {
	if (n == 0) {
	 EditInfo ei = new EditInfo("", 0, -1, -1);
	 ei.checkbox = new Checkbox("Normally closed",
				 (flags & FLAG_INVERT) != 0);
	 return ei;
	}
	if (n == 1)
	 return new EditInfo("On Resistance (ohms)", r_on, 0, 0);
	if (n == 2)
	 return new EditInfo("Off Resistance (ohms)", r_off, 0, 0);
	return null;
 }
 public void setEditValue(int n, EditInfo ei) {
	if (n == 0)
	 flags = (ei.checkbox.getState()) ?
		(flags | FLAG_INVERT) :
		(flags & ~FLAG_INVERT);
	if (n == 1 && ei.value > 0)
	 r_on = ei.value;
	if (n == 2 && ei.value > 0)
	 r_off = ei.value;
 }
}
AndGateElm.java
import java.awt.*;
import java.util.StringTokenizer;
 class AndGateElm extends GateElm {
	public AndGateElm(int xx, int yy) { super(xx, yy); }
	public AndGateElm(int xa, int ya, int xb, int yb, int f,
			 StringTokenizer st) {
	 super(xa, ya, xb, yb, f, st);
	}
	void setPoints() {
	 super.setPoints();
	 
	 // 0=topleft, 1-10 = top curve, 11 = right, 12-21=bottom curve,
	 // 22 = bottom left
	 Point triPoints[] = newPointArray(23);
	 interpPoint2(lead1, lead2, triPoints[0], triPoints[22], 0, hs2);
	 int i;
	 for (i = 0; i != 10; i++) {
		double a = i*.1;
		double b = Math.sqrt(1-a*a);
		interpPoint2(lead1, lead2,
			 triPoints[i+1], triPoints[21-i],
			 .5+a/2, b*hs2);
	 }
	 triPoints[11] = new Point(lead2);
	 if (isInverting()) {
		pcircle = interpPoint(point1, point2, .5+(ww+4)/dn);
		lead2 = interpPoint(point1, point2, .5+(ww+8)/dn);
	 }
	 gatePoly = createPolygon(triPoints);
	}
	String getGateName() { return "AND gate"; }
	boolean calcFunction() {
	 int i;
	 boolean f = true;
	 for (i = 0; i != inputCount; i++)
		f &= getInput(i);
	 return f;
	}
	int getDumpType() { return 150; }
	int getShortcut() { return '2'; }
 }
AntennaElm.java
import java.awt.*;
import java.util.StringTokenizer;
 class AntennaElm extends RailElm {
	public AntennaElm(int xx, int yy) { super(xx, yy, WF_DC); }
	public AntennaElm(int xa, int ya, int xb, int yb, int f,
		 StringTokenizer st) {
	 super(xa, ya, xb, yb, f, st);
	 waveform = WF_DC;
	}
	double fmphase;
	void stamp() {
	 sim.stampVoltageSource(0, nodes[0], voltSource);
	}
	void doStep() {
	 sim.updateVoltageSource(0, nodes[0], voltSource, getVoltage());
	}
	double getVoltage() {
	 fmphase += 2*pi*(2200+Math.sin(2*pi*sim.t*13)*100)*sim.timeStep;
	 double fm = 3*Math.sin(fmphase);
	 return Math.sin(2*pi*sim.t*3000)*(1.3+Math.sin(2*pi*sim.t*12))*3 +
	 Math.sin(2*pi*sim.t*2710)*(1.3+Math.sin(2*pi*sim.t*13))*3 +
		 Math.sin(2*pi*sim.t*2433)*(1.3+Math.sin(2*pi*sim.t*14))*3 + fm;
	}
	int getDumpType() { return 'A'; }
	int getShortcut() { return 0; }
 }
BoxElm.java
import java.awt.*;
import java.util.StringTokenizer;
import java.util.Vector;
class BoxElm extends GraphicElm {
 public BoxElm(int xx, int yy) {
	super(xx, yy);
	x2 = xx + 16;
	y2 = yy + 16;
	setBbox(x, y, x2, y2);
 }
 public BoxElm(int xa, int ya, int xb, int yb, int f,
		 StringTokenizer st) {
	super(xa, ya, xb, yb, f);
	x2 = xb;
	y2 = yb;
/*	if ( st.hasMoreTokens() )
		x = new Integer(st.nextToken()).intValue();
	if ( st.hasMoreTokens() )
		y = new Integer(st.nextToken()).intValue();
	if ( st.hasMoreTokens() )
		x2 = new Integer(st.nextToken()).intValue();
	if ( st.hasMoreTokens() )
		y2 = new Integer(st.nextToken()).intValue();*/
	setBbox(x, y, x2, y2);
 }
 String dump() {
	return super.dump();
 }
 int getDumpType() { return 'b'; }
 void drag(int xx, int yy) {
	x = xx;
	y = yy;
 }
 void draw(Graphics g) {
	//g.setColor(needsHighlight() ? selectColor : lightGrayColor);
	g.setColor(needsHighlight() ? selectColor : Color.GRAY);
	setBbox(x, y, x2, y2);
	if ( x < x2 && y < y2 )
		g.fillRect(x,y, x2-x, y2-y);
	else if ( x > x2 && y < y2 )
		g.fillRect(x2,y, x-x2, y2-y);
	else if ( x < x2 && y > y2 )
		g.fillRect(x, y2, x2-x, y-y2);
	else
		g.fillRect(x2, y2, x-x2, y-y2);
 }
 public EditInfo getEditInfo(int n) {
	return null;
 }
 public void setEditValue(int n, EditInfo ei) {
 }
 void getInfo(String arr[]) {
 }
 @Override
 int getShortcut() { return 0; }
}
CapacitorElm.java
import java.awt.*;
import java.util.StringTokenizer;
 class CapacitorElm extends CircuitElm {
	double capacitance;
	double compResistance, voltdiff;
	Point plate1[], plate2[];
	public static final int FLAG_BACK_EULER = 2;
	public CapacitorElm(int xx, int yy) {
	 super(xx, yy);
	 capacitance = 1e-5;
	}
	public CapacitorElm(int xa, int ya, int xb, int yb, int f,
			 StringTokenizer st) {
	 super(xa, ya, xb, yb, f);
	 capacitance = new Double(st.nextToken()).doubleValue();
	 voltdiff = new Double(st.nextToken()).doubleValue();
	}
	boolean isTrapezoidal() { return (flags & FLAG_BACK_EULER) == 0; }
	void setNodeVoltage(int n, double c) {
	 super.setNodeVoltage(n, c);
	 voltdiff = volts[0]-volts[1];
	}
	void reset() {
	 current = curcount = 0;
	 // put small charge on caps when reset to start oscillators
	 voltdiff = 1e-3;
	}
	int getDumpType() { return 'c'; }
	String dump() {
	 return super.dump() + " " + capacitance + " " + voltdiff;
	}
	void setPoints() {
	 super.setPoints();
	 double f = (dn/2-4)/dn;
	 // calc leads
	 lead1 = interpPoint(point1, point2, f);
	 lead2 = interpPoint(point1, point2, 1-f);
	 // calc plates
	 plate1 = newPointArray(2);
	 plate2 = newPointArray(2);
	 interpPoint2(point1, point2, plate1[0], plate1[1], f, 12);
	 interpPoint2(point1, point2, plate2[0], plate2[1], 1-f, 12);
	}
	
	void draw(Graphics g) {
	 int hs = 12;
	 setBbox(point1, point2, hs);
	 
	 // draw first lead and plate
	 setVoltageColor(g, volts[0]);
	 drawThickLine(g, point1, lead1);
	 setPowerColor(g, false);
	 drawThickLine(g, plate1[0], plate1[1]);
	 if (sim.powerCheckItem.getState())
		g.setColor(Color.gray);
	 // draw second lead and plate
	 setVoltageColor(g, volts[1]);
	 drawThickLine(g, point2, lead2);
	 setPowerColor(g, false);
	 drawThickLine(g, plate2[0], plate2[1]);
	 
	 updateDotCount();
	 if (sim.dragElm != this) {
		drawDots(g, point1, lead1, curcount);
		drawDots(g, point2, lead2, -curcount);
	 }
	 drawPosts(g);
	 if (sim.showValuesCheckItem.getState()) {
		String s = getShortUnitText(capacitance, "F");
		drawValues(g, s, hs);
	 }
	}
	void stamp() {
	 // capacitor companion model using trapezoidal approximation
	 // (Norton equivalent) consists of a current source in
	 // parallel with a resistor. Trapezoidal is more accurate
	 // than backward euler but can cause oscillatory behavior
	 // if RC is small relative to the
timestep.
	 if (isTrapezoidal())
		compResistance = sim.timeStep/(2*capacitance);
	 else
		compResistance = sim.timeStep/capacitance;
	 sim.stampResistor(nodes[0], nodes[1], compResistance);
	 sim.stampRightSide(nodes[0]);
	 sim.stampRightSide(nodes[1]);
	}
	void startIteration() {
	 if (isTrapezoidal())
		curSourceValue = -voltdiff/compResistance-current;
	 else
		curSourceValue = -voltdiff/compResistance;
	 //System.out.println("cap " + compResistance + " " + curSourceValue + " " + current + " " + voltdiff);
	}
	void calculateCurrent() {
	 double voltdiff = volts[0] - volts[1];
	 // we check compResistance because this might get called
	 // before stamp(), which sets compResistance, causing
	 // infinite current
	 if (compResistance > 0)
		current = voltdiff/compResistance + curSourceValue;
	}
	double curSourceValue;
	void doStep() {
	 sim.stampCurrentSource(nodes[0], nodes[1], curSourceValue);
 	}
	void getInfo(String arr[]) {
	 arr[0] = "capacitor";
	 getBasicInfo(arr);
	 arr[3] = "C = " + getUnitText(capacitance, "F");
	 arr[4] = "P = " + getUnitText(getPower(), "W");
	 //double v = getVoltageDiff();
	 //arr[4] = "U = " + getUnitText(.5*capacitance*v*v, "J");
	}
	public EditInfo getEditInfo(int n) {
	 if (n == 0)
		return new EditInfo("Capacitance (F)", capacitance, 0, 0);
	 if (n == 1) {
		EditInfo ei = new EditInfo("", 0, -1, -1);
		ei.checkbox = new Checkbox("Trapezoidal Approximation", isTrapezoidal());
		return ei;
	 }
	 return null;
	}
	public void setEditValue(int n, EditInfo ei) {
	 if (n == 0 && ei.value > 0)
		capacitance = ei.value;
	 if (n == 1) {
		if (ei.checkbox.getState())
		 flags &= ~FLAG_BACK_EULER;
		else
		 flags |= FLAG_BACK_EULER;
	 }
	}
	int getShortcut() { return 'c'; }
 }
ChipElm.java
import java.awt.*;
import java.util.StringTokenizer;
 abstract class ChipElm extends CircuitElm {
	int csize, cspc, cspc2;
	int bits;
	final int FLAG_SMALL = 1;
	final int FLAG_FLIP_X = 1024;
	final int FLAG_FLIP_Y = 2048;
	public ChipElm(int xx, int yy) {
	 super(xx, yy);
	 if (needsBits())
		bits = (this instanceof DecadeElm) ? 10 : 4;
	 noDiagonal = true;
	 setupPins();
	 setSize(sim.smallGridCheckItem.getState() ? 1 : 2);
	}
	public ChipElm(int xa, int ya, int xb, int yb, int f,
		 StringTokenizer st) {
	 super(xa, ya, xb, yb, f);
	 if (needsBits())
		bits = new Integer(st.nextToken()).intValue();
	 noDiagonal = true;
	 setupPins();
	 setSize((f & FLAG_SMALL) != 0 ? 1 : 2);
	 int i;
	 for (i = 0; i != getPostCount(); i++) {
		if (pins[i].state) {
		 volts[i] = new Double(st.nextToken()).doubleValue();
		 pins[i].value = volts[i] > 2.5;
		}
	 }
	}
	boolean needsBits() { return false; }
	void setSize(int s) {
	 csize = s;
	 cspc = 8*s;
	 cspc2 = cspc*2;
	 flags &= ~FLAG_SMALL;
	 flags |= (s == 1) ? FLAG_SMALL : 0;
	}
	abstract void setupPins();
	void draw(Graphics g) {
	 drawChip(g);
	}
	void drawChip(Graphics g) {
	 int i;
	 Font f = new Font("SansSerif", 0, 10*csize);
	 g.setFont(f);
	 FontMetrics fm = g.getFontMetrics();
	 for (i = 0; i != getPostCount(); i++) {
		Pin p = pins[i];
		setVoltageColor(g, volts[i]);
		Point a = p.post;
		Point b = p.stub;
		drawThickLine(g, a, b);
		p.curcount = updateDotCount(p.current, p.curcount);
		drawDots(g, b, a, p.curcount);
		if (p.bubble) {
		 g.setColor(sim.printableCheckItem.getState() ?
			 Color.white : Color.black);
		 drawThickCircle(g, p.bubbleX, p.bubbleY, 1);
		 g.setColor(lightGrayColor);
		 drawThickCircle(g, p.bubbleX, p.bubbleY, 3);
		}
		g.setColor(whiteColor);
		int sw = fm.stringWidth(p.text);
		g.drawString(p.text, p.textloc.x-sw/2,
			 p.textloc.y+fm.getAscent()/2);
		if (p.lineOver) {
		 int ya = p.textloc.y-fm.getAscent()/2;
		 g.drawLine(p.textloc.x-sw/2, ya, p.textloc.x+sw/2, ya);
		}
	 }
	 g.setColor(needsHighlight() ? selectColor : lightGrayColor);
	 drawThickPolygon(g, rectPointsX, rectPointsY, 4);
	 if (clockPointsX != null)
		g.drawPolyline(clockPointsX, clockPointsY, 3);
	 for (i = 0; i != getPostCount(); i++)
		drawPost(g, pins[i].post.x, pins[i].post.y, nodes[i]);
	}
	int rectPointsX[], rectPointsY[];
	int clockPointsX[], clockPointsY[];
	Pin pins[];
	int sizeX, sizeY;
	boolean lastClock;
	void drag(int xx, int yy) {
	 yy = sim.snapGrid(yy);
	 if (xx < x) {
		xx = x; yy = y;
	 } else {
		y = y2 = yy;
		x2 = sim.snapGrid(xx);
	 }
	 setPoints();
	}
	void setPoints() {
	 if (x2-x > sizeX*cspc2 && this == sim.dragElm)
		setSize(2);
	 int hs = cspc;
	 int x0 = x+cspc2; int y0 = y;
	 int xr = x0-cspc;
	 int yr = y0-cspc;
	 int xs = sizeX*cspc2;
	 int ys = sizeY*cspc2;
	 rectPointsX = new int[] { xr, xr+xs, xr+xs, xr };
	 rectPointsY = new int[] { yr, yr, yr+ys, yr+ys };
	 setBbox(xr, yr, rectPointsX[2], rectPointsY[2]);
	 int i;
	 for (i = 0; i != getPostCount(); i++) {
		Pin p = pins[i];
		switch (p.side) {
		case SIDE_N: p.setPoint(x0, y0, 1, 0, 0, -1, 0, 0); break;
		case SIDE_S: p.setPoint(x0, y0, 1, 0, 0, 1, 0, ys-cspc2);break;
		case SIDE_W: p.setPoint(x0, y0, 0, 1, -1, 0, 0, 0); break;
		case SIDE_E: p.setPoint(x0, y0, 0, 1, 1, 0, xs-cspc2, 0);break;
		}
	 }
	}
	Point getPost(int n) {
	 return pins[n].post;
	}
	abstract int getVoltageSourceCount(); // output count
	void setVoltageSource(int j, int vs) {
	 int i;
	 for (i = 0; i != getPostCount(); i++) {
		Pin p = pins[i];
		if (p.output && j-- == 0) {
		 p.voltSource = vs;
		 return;
		}
	 }
	 System.out.println("setVoltageSource failed for " + this);
	}
	void stamp() {
	 int i;
	 for (i = 0; i != getPostCount(); i++) {
		Pin p = pins[i];
		if (p.output)
		 sim.stampVoltageSource(0, nodes[i], p.voltSource);
	 }
	}
	void execute() {}
	void doStep() {
	 int i;
	 for (i = 0; i != getPostCount(); i++) {
		Pin p = pins[i];
		if (!p.output)
		 p.value = volts[i] > 2.5;
	 }
	 execute();
	 for (i = 0; i != getPostCount(); i++) {
		Pin p = pins[i];
		if (p.output)
		 sim.updateVoltageSource(0, nodes[i], p.voltSource,
					p.value ? 5 : 0);
	 }
	}
	void reset() {
	 int i;
	 for (i = 0; i != getPostCount(); i++) {
		pins[i].value = false;
		pins[i].curcount = 0;
		volts[i] = 0;
	 }
	 lastClock = false;
	}
	
	String dump() {
	 int t = getDumpType();
	 String s = super.dump();
	 if (needsBits())
		s += " " + bits;
	 int i;
	 for (i = 0; i != getPostCount(); i++) {
		if (pins[i].state)
		 s += " " + volts[i];
	 }
	 return s;
	}
	
	void getInfo(String arr[]) {
	 arr[0] = getChipName();
	 int i, a = 1;
	 for (i = 0; i != getPostCount(); i++) {
		Pin p = pins[i];
		if (arr[a] != null)
		 arr[a] += "; ";
		else
		 arr[a] = "";
		String t = p.text;
		if (p.lineOver)
		 t += '\'';
		if (p.clock)
		 t = "Clk";
		arr[a] += t + " = " + getVoltageText(volts[i]);
		if (i % 2 == 1)
		 a++;
	 }
	}
	void setCurrent(int x, double c) {
	 int i;
	 for (i = 0; i != getPostCount(); i++)
		if (pins[i].output && pins[i].voltSource == x)
		 pins[i].current = c;
	}
	String getChipName() { return "chip"; }
	boolean getConnection(int n1, int n2) { return false; }
	boolean hasGroundConnection(int n1) {
	 return pins[n1].output;
	}
	
	public EditInfo getEditInfo(int n) {
	 if (n == 0) {
		EditInfo ei = new EditInfo("", 0, -1, -1);
		ei.checkbox = new Checkbox("Flip X", (flags & FLAG_FLIP_X) != 0);
		return ei;
	 }
	 if (n == 1) {
		EditInfo ei = new EditInfo("", 0, -1, -1);
		ei.checkbox = new Checkbox("Flip Y", (flags & FLAG_FLIP_Y) != 0);
		return ei;
	 }
	 return null;
	}
	public void setEditValue(int
n, EditInfo ei) {
	 if (n == 0) {
		if (ei.checkbox.getState())
		 flags |= FLAG_FLIP_X;
		else
		 flags &= ~FLAG_FLIP_X;
		setPoints();
	 }
	 if (n == 1) {
		if (ei.checkbox.getState())
		 flags |= FLAG_FLIP_Y;
		else
		 flags &= ~FLAG_FLIP_Y;
		setPoints();
	 }
	}
	final int SIDE_N = 0;
	final int SIDE_S = 1;
	final int SIDE_W = 2;
	final int SIDE_E = 3;
	class Pin {
	 Pin(int p, int s, String t) {
		pos = p; side = s; text = t;
	 }
	 Point post, stub;
	 Point textloc;
	 int pos, side, voltSource, bubbleX, bubbleY;
	 String text;
	 boolean lineOver, bubble, clock, output, value, state;
	 double curcount, current;
	 void setPoint(int px, int py, int dx, int dy, int dax, int day,
			 int sx, int sy) {
		if ((flags & FLAG_FLIP_X) != 0) {
		 dx = -dx;
		 dax = -dax;
		 px += cspc2*(sizeX-1);
		 sx = -sx;
		}
		if ((flags & FLAG_FLIP_Y) != 0) {
		 dy = -dy;
		 day = -day;
		 py += cspc2*(sizeY-1);
		 sy = -sy;
		}
		int xa = px+cspc2*dx*pos+sx;
		int ya = py+cspc2*dy*pos+sy;
		post = new Point(xa+dax*cspc2, ya+day*cspc2);
		stub = new Point(xa+dax*cspc , ya+day*cspc );
		textloc = new Point(xa , ya );
		if (bubble) {
		 bubbleX = xa+dax*10*csize;
		 bubbleY = ya+day*10*csize;
		}
		if (clock) {
		 clockPointsX = new int[3];
		 clockPointsY = new int[3];
		 clockPointsX[0] = xa+dax*cspc-dx*cspc/2;
		 clockPointsY[0] = ya+day*cspc-dy*cspc/2;
		 clockPointsX[1] = xa;
		 clockPointsY[1] = ya;
		 clockPointsX[2] = xa+dax*cspc+dx*cspc/2;
		 clockPointsY[2] = ya+day*cspc+dy*cspc/2;
		}
	 }
	}
 }
CirSim.java
// CirSim.java (c) 2010 by Paul Falstad
// For information about the theory behind this, see Electronic Circuit & System Simulation Methods by Pillage
import java.io.InputStream;
import java.awt.*;
import java.awt.image.*;
import java.applet.Applet;
import java.util.Vector;
import java.io.File;
import java.util.Random;
import java.util.Arrays;
import java.lang.Math;
import java.net.URL;
import java.awt.event.*;
import java.io.FilterInputStream;
import java.io.ByteArrayOutputStream;
import java.util.StringTokenizer;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.net.URLEncoder;
public class CirSim extends Frame
 implements ComponentListener, ActionListener, AdjustmentListener,
 MouseMotionListener, MouseListener, ItemListener, KeyListener {
 
 Thread engine = null;
 Dimension winSize;
 Image dbimage;
 
 Random random;
 public static final int sourceRadius = 7;
 public static final double freqMult = 3.14159265*2*4;
 
 public String getAppletInfo() {
	return "Circuit by Paul Falstad";
 }
 static Container main;
 Label titleLabel;
 Button resetButton;
 Button dumpMatrixButton;
 MenuItem exportItem, exportLinkItem, importItem, exitItem, undoItem, redoItem,
	cutItem, copyItem, pasteItem, selectAllItem, optionsItem;
 Menu optionsMenu;
 Checkbox stoppedCheck;
 CheckboxMenuItem dotsCheckItem;
 CheckboxMenuItem voltsCheckItem;
 CheckboxMenuItem powerCheckItem;
 CheckboxMenuItem smallGridCheckItem;
 CheckboxMenuItem showValuesCheckItem;
 CheckboxMenuItem conductanceCheckItem;
 CheckboxMenuItem euroResistorCheckItem;
 CheckboxMenuItem printableCheckItem;
 CheckboxMenuItem conventionCheckItem;
 Scrollbar speedBar;
 Scrollbar currentBar;
 Label powerLabel;
 Scrollbar powerBar;
 PopupMenu elmMenu;
 MenuItem elmEditMenuItem;
 MenuItem elmCutMenuItem;
 MenuItem elmCopyMenuItem;
 MenuItem elmDeleteMenuItem;
 MenuItem elmScopeMenuItem;
 PopupMenu scopeMenu;
 PopupMenu transScopeMenu;
 PopupMenu mainMenu;
 CheckboxMenuItem scopeVMenuItem;
 CheckboxMenuItem scopeIMenuItem;
 CheckboxMenuItem scopeMaxMenuItem;
 CheckboxMenuItem scopeMinMenuItem;
 CheckboxMenuItem scopeFreqMenuItem;
 CheckboxMenuItem scopePowerMenuItem;
 CheckboxMenuItem scopeIbMenuItem;
 CheckboxMenuItem scopeIcMenuItem;
 CheckboxMenuItem scopeIeMenuItem;
 CheckboxMenuItem scopeVbeMenuItem;
 CheckboxMenuItem scopeVbcMenuItem;
 CheckboxMenuItem scopeVceMenuItem;
 CheckboxMenuItem scopeVIMenuItem;
 CheckboxMenuItem scopeXYMenuItem;
 CheckboxMenuItem scopeResistMenuItem;
 CheckboxMenuItem scopeVceIcMenuItem;
 MenuItem scopeSelectYMenuItem;
 Class addingClass;
 int mouseMode = MODE_SELECT;
 int tempMouseMode = MODE_SELECT;
 String mouseModeStr = "Select";
 static final double pi = 3.14159265358979323846;
 static final int MODE_ADD_ELM = 0;
 static final int MODE_DRAG_ALL = 1;
 static final int MODE_DRAG_ROW = 2;
 static final int MODE_DRAG_COLUMN = 3;
 static final int MODE_DRAG_SELECTED = 4;
 static final int MODE_DRAG_POST = 5;
 static final int MODE_SELECT = 6;
 static final int infoWidth = 120;
 int dragX, dragY, initDragX, initDragY;
 int selectedSource;
 Rectangle selectedArea;
 int gridSize, gridMask, gridRound;
 boolean dragging;
 boolean analyzeFlag;
 boolean dumpMatrix;
 boolean useBufferedImage;
 boolean isMac;
 String ctrlMetaKey;
 double t;
 int pause = 10;
 int scopeSelected = -1;
 int menuScope = -1;
 int hintType = -1, hintItem1, hintItem2;
 String stopMessage;
 double timeStep;
 static final int HINT_LC = 1;
 static final int HINT_RC = 2;
 static final int HINT_3DB_C = 3;
 static final int HINT_TWINT = 4;
 static final int HINT_3DB_L = 5;
 Vector<CircuitElm> elmList;
// Vector setupList;
 CircuitElm dragElm, menuElm, mouseElm, stopElm;
 boolean didSwitch = false;
 int mousePost = -1;
 CircuitElm plotXElm, plotYElm;
 int draggingPost;
 SwitchElm heldSwitchElm;
 double circuitMatrix[][], circuitRightSide[],
	origRightSide[], origMatrix[][];
 RowInfo circuitRowInfo[];
 int circuitPermute[];
 boolean circuitNonLinear;
 int voltageSourceCount;
 int circuitMatrixSize, circuitMatrixFullSize;
 boolean circuitNeedsMap;
 public boolean useFrame;
 int scopeCount;
 Scope scopes[];
 int scopeColCount[];
 static EditDialog editDialog;
 static ImportExportDialog impDialog, expDialog;
 Class dumpTypes[], shortcuts[];
 static String muString = "u";
 static String ohmString = "ohm";
 String clipboard;
 Rectangle circuitArea;
 int circuitBottom;
 Vector<String> undoStack, redoStack;
 int getrand(int x) {
	int q = random.nextInt();
	if (q < 0) q = -q;
	return q % x;
 }
 CircuitCanvas cv;
 Circuit applet;
 CirSim(Circuit a) {
	super("Circuit Simulator v1.6i");
	applet = a;
	useFrame = false;
 }
 String startCircuit = null;
 String startLabel = null;
 String startCircuitText = null;
 String baseURL = "http://www.falstad.com/circuit/";
 
 public void init() {
	String euroResistor = null;
	String useFrameStr = null;
	boolean printable = false;
	boolean convention = true;
	CircuitElm.initClass(this);
	try {
	 baseURL = applet.getDocumentBase().getFile();
	 // look for circuit embedded in URL
	 String doc = applet.getDocumentBase().toString();
	 int in = doc.indexOf('#');
	 if (in > 0) {
		String x = null;
		try {
		 x = doc.substring(in+1);
		 x = URLDecoder.decode(x);
		 startCircuitText = x;
		} catch (Exception e) {
		 System.out.println("can't decode " + x);
		 e.printStackTrace();
		}
	 }
	 in = doc.lastIndexOf('/');
	 if (in > 0)
		baseURL = doc.substring(0, in+1);
	 
	 String param = applet.getParameter("PAUSE");
	 if (param != null)
		pause = Integer.parseInt(param);
	 startCircuit
= applet.getParameter("startCircuit");
	 startLabel = applet.getParameter("startLabel");
	 euroResistor = applet.getParameter("euroResistors");
	 useFrameStr = applet.getParameter("useFrame");
	 String x = applet.getParameter("whiteBackground");
	 if (x != null && x.equalsIgnoreCase("true"))
		printable = true;
	 x = applet.getParameter("conventionalCurrent");
	 if (x != null && x.equalsIgnoreCase("true"))
		convention = false;
	} catch (Exception e) { }
	
	boolean euro = (euroResistor != null && euroResistor.equalsIgnoreCase("true"));
	useFrame = (useFrameStr == null || !useFrameStr.equalsIgnoreCase("false"));
	if (useFrame)
	 main = this;
	else
	 main = applet;
	
	String os = System.getProperty("os.name");
	isMac = (os.indexOf("Mac ") == 0);
	ctrlMetaKey = (isMac) ? "\u2318" : "Ctrl";
	String jv = System.getProperty("java.class.version");
	double jvf = new Double(jv).doubleValue();
	if (jvf >= 48) {
	 muString = "\u03bc";
	 ohmString = "\u03a9";
	 useBufferedImage = true;
	}
	
	dumpTypes = new Class[300];
	shortcuts = new Class[127];
	// these characters are reserved
	dumpTypes[(int)'o'] = Scope.class;
	dumpTypes[(int)'h'] = Scope.class;
	dumpTypes[(int)'$'] = Scope.class;
	dumpTypes[(int)'%'] = Scope.class;
	dumpTypes[(int)'?'] = Scope.class;
	dumpTypes[(int)'B'] = Scope.class;
	main.setLayout(new CircuitLayout());
	cv = new CircuitCanvas(this);
	cv.addComponentListener(this);
	cv.addMouseMotionListener(this);
	cv.addMouseListener(this);
	cv.addKeyListener(this);
	main.add(cv);
	mainMenu = new PopupMenu();
	MenuBar mb = null;
	if (useFrame)
	 mb = new MenuBar();
	Menu m = new Menu("File");
	if (useFrame)
	 mb.add(m);
	else
	 mainMenu.add(m);
	m.add(importItem = getMenuItem("Import"));
	m.add(exportItem = getMenuItem("Export"));
	m.add(exportLinkItem = getMenuItem("Export Link"));
	m.addSeparator();
	m.add(exitItem = getMenuItem("Exit"));
	m = new Menu("Edit");
	m.add(undoItem = getMenuItem("Undo"));
	undoItem.setShortcut(new MenuShortcut(KeyEvent.VK_Z));
	m.add(redoItem = getMenuItem("Redo"));
	redoItem.setShortcut(new MenuShortcut(KeyEvent.VK_Z, true));
	m.addSeparator();
	m.add(cutItem = getMenuItem("Cut"));
	cutItem.setShortcut(new MenuShortcut(KeyEvent.VK_X));
	m.add(copyItem = getMenuItem("Copy"));
	copyItem.setShortcut(new MenuShortcut(KeyEvent.VK_C));
	m.add(pasteItem = getMenuItem("Paste"));
	pasteItem.setShortcut(new MenuShortcut(KeyEvent.VK_V));
	pasteItem.setEnabled(false);
	m.add(selectAllItem = getMenuItem("Select All"));
	selectAllItem.setShortcut(new MenuShortcut(KeyEvent.VK_A));
	if (useFrame)
	 mb.add(m);
	else
	 mainMenu.add(m);
	m = new Menu("Scope");
	if (useFrame)
	 mb.add(m);
	else
	 mainMenu.add(m);
	m.add(getMenuItem("Stack All", "stackAll"));
	m.add(getMenuItem("Unstack All", "unstackAll"));
	optionsMenu = m = new Menu("Options");
	if (useFrame)
	 mb.add(m);
	else
	 mainMenu.add(m);
	m.add(dotsCheckItem = getCheckItem("Show Current"));
	dotsCheckItem.setState(true);
	m.add(voltsCheckItem = getCheckItem("Show Voltage"));
	voltsCheckItem.setState(true);
	m.add(powerCheckItem = getCheckItem("Show Power"));
	m.add(showValuesCheckItem = getCheckItem("Show Values"));
	showValuesCheckItem.setState(true);
	//m.add(conductanceCheckItem = getCheckItem("Show Conductance"));
	m.add(smallGridCheckItem = getCheckItem("Small Grid"));
	m.add(euroResistorCheckItem = getCheckItem("European Resistors"));
	euroResistorCheckItem.setState(euro);
	m.add(printableCheckItem = getCheckItem("White Background"));
	printableCheckItem.setState(printable);
	m.add(conventionCheckItem = getCheckItem("Conventional Current Motion"));
	conventionCheckItem.setState(convention);
	m.add(optionsItem = getMenuItem("Other Options..."));
	
	Menu circuitsMenu = new Menu("Circuits");
	if (useFrame)
	 mb.add(circuitsMenu);
	else
	 mainMenu.add(circuitsMenu);
	mainMenu.add(getClassCheckItem("Add Wire", "WireElm"));
	mainMenu.add(getClassCheckItem("Add Resistor", "ResistorElm"));
	
	Menu passMenu = new Menu("Passive Components");
	mainMenu.add(passMenu);
	passMenu.add(getClassCheckItem("Add Capacitor", "CapacitorElm"));
	passMenu.add(getClassCheckItem("Add Inductor", "InductorElm"));
	passMenu.add(getClassCheckItem("Add Switch", "SwitchElm"));
	passMenu.add(getClassCheckItem("Add Push Switch", "PushSwitchElm"));
	passMenu.add(getClassCheckItem("Add SPDT Switch", "Switch2Elm"));
	passMenu.add(getClassCheckItem("Add Potentiometer", "PotElm"));
	passMenu.add(getClassCheckItem("Add Transformer", "TransformerElm"));
	passMenu.add(getClassCheckItem("Add Tapped Transformer",
				 "TappedTransformerElm"));
	passMenu.add(getClassCheckItem("Add Transmission Line", "TransLineElm"));
	passMenu.add(getClassCheckItem("Add Relay", "RelayElm"));
	passMenu.add(getClassCheckItem("Add Memristor", "MemristorElm"));
	passMenu.add(getClassCheckItem("Add Spark Gap", "SparkGapElm"));
	
	Menu inputMenu = new Menu("Inputs/Outputs");
	mainMenu.add(inputMenu);
	inputMenu.add(getClassCheckItem("Add Ground", "GroundElm"));
	inputMenu.add(getClassCheckItem("Add Voltage Source (2-terminal)", "DCVoltageElm"));
	inputMenu.add(getClassCheckItem("Add A/C Source (2-terminal)", "ACVoltageElm"));
	inputMenu.add(getClassCheckItem("Add Voltage Source (1-terminal)", "RailElm"));
	inputMenu.add(getClassCheckItem("Add A/C Source (1-terminal)", "ACRailElm"));
	inputMenu.add(getClassCheckItem("Add Square Wave (1-terminal)", "SquareRailElm"));
	inputMenu.add(getClassCheckItem("Add Analog Output", "OutputElm"));
	inputMenu.add(getClassCheckItem("Add Logic Input", "LogicInputElm"));
	inputMenu.add(getClassCheckItem("Add Logic Output", "LogicOutputElm"));
	inputMenu.add(getClassCheckItem("Add Clock", "ClockElm"));
	inputMenu.add(getClassCheckItem("Add A/C Sweep", "SweepElm"));
	inputMenu.add(getClassCheckItem("Add Var. Voltage", "VarRailElm"));
	inputMenu.add(getClassCheckItem("Add Antenna", "AntennaElm"));
	inputMenu.add(getClassCheckItem("Add AM source", "AMElm"));
	inputMenu.add(getClassCheckItem("Add FM source", "FMElm"));
	inputMenu.add(getClassCheckItem("Add Current Source", "CurrentElm"));
	inputMenu.add(getClassCheckItem("Add LED", "LEDElm"));
	inputMenu.add(getClassCheckItem("Add Lamp (beta)", "LampElm"));
	inputMenu.add(getClassCheckItem("Add LED Matrix", "LEDMatrixElm"));
	//inputMenu.add(getClassCheckItem("Add Microphone Input", "SignalInElm"));
	//inputMenu.add(getClassCheckItem("Add Speaker Output", "SignalOutElm"));
	
	Menu activeMenu = new Menu("Active Components");
	mainMenu.add(activeMenu);
	activeMenu.add(getClassCheckItem("Add Diode", "DiodeElm"));
	activeMenu.add(getClassCheckItem("Add Zener Diode", "ZenerElm"));
	activeMenu.add(getClassCheckItem("Add Transistor (bipolar, NPN)",
				 "NTransistorElm"));
	activeMenu.add(getClassCheckItem("Add Transistor (bipolar, PNP)",
				 "PTransistorElm"));
	activeMenu.add(getClassCheckItem("Add Op Amp (- on top)", "OpAmpElm"));
	activeMenu.add(getClassCheckItem("Add Op Amp (+ on top)",
				 "OpAmpSwapElm"));
	activeMenu.add(getClassCheckItem("Add MOSFET (n-channel)",
				 "NMosfetElm"));
	activeMenu.add(getClassCheckItem("Add MOSFET (p-channel)",
				 "PMosfetElm"));
	activeMenu.add(getClassCheckItem("Add JFET (n-channel)",
					 "NJfetElm"));
	activeMenu.add(getClassCheckItem("Add JFET (p-channel)",
					 "PJfetElm"));
	activeMenu.add(getClassCheckItem("Add Analog Switch (SPST)", "AnalogSwitchElm"));
	activeMenu.add(getClassCheckItem("Add Analog Switch (SPDT)", "AnalogSwitch2Elm"));
	activeMenu.add(getClassCheckItem("Add Tristate buffer", "TriStateElm"));
	activeMenu.add(getClassCheckItem("Add Schmitt Trigger", "SchmittElm"));
	activeMenu.add(getClassCheckItem("Add Schmitt Trigger (Inverting)", "InvertingSchmittElm"));
	activeMenu.add(getClassCheckItem("Add SCR", "SCRElm"));
	//activeMenu.add(getClassCheckItem("Add Varactor/Varicap",
"VaractorElm"));
	activeMenu.add(getClassCheckItem("Add Tunnel Diode", "TunnelDiodeElm"));
	activeMenu.add(getClassCheckItem("Add Triode", "TriodeElm"));
	//activeMenu.add(getClassCheckItem("Add Diac", "DiacElm"));
	//activeMenu.add(getClassCheckItem("Add Triac", "TriacElm"));
	//activeMenu.add(getClassCheckItem("Add Photoresistor", "PhotoResistorElm"));
	//activeMenu.add(getClassCheckItem("Add Thermistor", "ThermistorElm"));
	activeMenu.add(getClassCheckItem("Add CCII+", "CC2Elm"));
	activeMenu.add(getClassCheckItem("Add CCII-", "CC2NegElm"));
	Menu gateMenu = new Menu("Logic Gates");
	mainMenu.add(gateMenu);
	gateMenu.add(getClassCheckItem("Add Inverter", "InverterElm"));
	gateMenu.add(getClassCheckItem("Add NAND Gate", "NandGateElm"));
	gateMenu.add(getClassCheckItem("Add NOR Gate", "NorGateElm"));
	gateMenu.add(getClassCheckItem("Add AND Gate", "AndGateElm"));
	gateMenu.add(getClassCheckItem("Add OR Gate", "OrGateElm"));
	gateMenu.add(getClassCheckItem("Add XOR Gate", "XorGateElm"));
	Menu chipMenu = new Menu("Chips");
	mainMenu.add(chipMenu);
	chipMenu.add(getClassCheckItem("Add D Flip-Flop", "DFlipFlopElm"));
	chipMenu.add(getClassCheckItem("Add JK Flip-Flop", "JKFlipFlopElm"));
	chipMenu.add(getClassCheckItem("Add T Flip-Flop", "TFlipFlopElm"));
	chipMenu.add(getClassCheckItem("Add 7 Segment LED", "SevenSegElm"));
	chipMenu.add(getClassCheckItem("Add 7 Segment Decoder", "SevenSegDecoderElm"));
	chipMenu.add(getClassCheckItem("Add Multiplexer", "MultiplexerElm"));
	chipMenu.add(getClassCheckItem("Add Demultiplexer", "DeMultiplexerElm"));
	chipMenu.add(getClassCheckItem("Add SIPO shift register", "SipoShiftElm"));
	chipMenu.add(getClassCheckItem("Add PISO shift register", "PisoShiftElm"));
	chipMenu.add(getClassCheckItem("Add Phase Comparator", "PhaseCompElm"));
	chipMenu.add(getClassCheckItem("Add Counter", "CounterElm"));
	chipMenu.add(getClassCheckItem("Add Decade Counter", "DecadeElm"));
	chipMenu.add(getClassCheckItem("Add 555 Timer", "TimerElm"));
	chipMenu.add(getClassCheckItem("Add DAC", "DACElm"));
	chipMenu.add(getClassCheckItem("Add ADC", "ADCElm"));
	chipMenu.add(getClassCheckItem("Add Latch", "LatchElm"));
	//chipMenu.add(getClassCheckItem("Add Static RAM", "SRAMElm"));
	chipMenu.add(getClassCheckItem("Add Sequence generator", "SeqGenElm"));
	chipMenu.add(getClassCheckItem("Add VCO", "VCOElm"));
	chipMenu.add(getClassCheckItem("Add Full Adder", "FullAdderElm"));
	chipMenu.add(getClassCheckItem("Add Half Adder", "HalfAdderElm"));
	chipMenu.add(getClassCheckItem("Add Monostable", "MonostableElm"));
	Menu otherMenu = new Menu("Other");
	mainMenu.add(otherMenu);
	otherMenu.add(getClassCheckItem("Add Text", "TextElm"));
	otherMenu.add(getClassCheckItem("Add Box", "BoxElm"));
	otherMenu.add(getClassCheckItem("Add Scope Probe", "ProbeElm"));
	otherMenu.add(getCheckItem("Drag All (Alt-drag)", "DragAll"));
	otherMenu.add(getCheckItem(
			 isMac ? "Drag Row (Alt-S-drag, S-right)" :
			 "Drag Row (S-right)",
			 "DragRow"));
	otherMenu.add(getCheckItem(
			 isMac ? "Drag Column (Alt-\u2318-drag, \u2318-right)" :
			 "Drag Column (C-right)",
			 "DragColumn"));
	otherMenu.add(getCheckItem("Drag Selected", "DragSelected"));
	otherMenu.add(getCheckItem("Drag Post (" + ctrlMetaKey + "-drag)",
				 "DragPost"));
	mainMenu.add(getCheckItem("Select/Drag Selected (space or Shift-drag)", "Select"));
	main.add(mainMenu);
	main.add(resetButton = new Button("Reset"));
	resetButton.addActionListener(this);
	dumpMatrixButton = new Button("Dump Matrix");
	//main.add(dumpMatrixButton);
	dumpMatrixButton.addActionListener(this);
	stoppedCheck = new Checkbox("Stopped");
	stoppedCheck.addItemListener(this);
	main.add(stoppedCheck);
	
	main.add(new Label("Simulation Speed", Label.CENTER));
	// was max of 140
	main.add(speedBar = new Scrollbar(Scrollbar.HORIZONTAL, 3, 1, 0, 260));
	speedBar.addAdjustmentListener(this);
	main.add(new Label("Current Speed", Label.CENTER));
	currentBar = new Scrollbar(Scrollbar.HORIZONTAL,
				 50, 1, 1, 100);
	currentBar.addAdjustmentListener(this);
	main.add(currentBar);
	main.add(powerLabel = new Label("Power Brightness", Label.CENTER));
	main.add(powerBar = new Scrollbar(Scrollbar.HORIZONTAL,
				 50, 1, 1, 100));
	powerBar.addAdjustmentListener(this);
	powerBar.disable();
	powerLabel.disable();
	main.add(new Label("www.falstad.com"));
	if (useFrame)
	 main.add(new Label(""));
	Font f = new Font("SansSerif", 0, 10);
	Label l;
	l = new Label("Current Circuit:");
	l.setFont(f);
	titleLabel = new Label("Label");
	titleLabel.setFont(f);
	if (useFrame) {
	 main.add(l);
	 main.add(titleLabel);
	}
	setGrid();
	elmList = new Vector<CircuitElm>();
//	setupList = new Vector();
	undoStack = new Vector<String>();
	redoStack = new Vector<String>();
	scopes = new Scope[20];
	scopeColCount = new int[20];
	scopeCount = 0;
	
	random = new Random();
	cv.setBackground(Color.black);
	cv.setForeground(Color.lightGray);
	
	elmMenu = new PopupMenu();
	elmMenu.add(elmEditMenuItem = getMenuItem("Edit"));
	elmMenu.add(elmScopeMenuItem = getMenuItem("View in Scope"));
	elmMenu.add(elmCutMenuItem = getMenuItem("Cut"));
	elmMenu.add(elmCopyMenuItem = getMenuItem("Copy"));
	elmMenu.add(elmDeleteMenuItem = getMenuItem("Delete"));
	main.add(elmMenu);
	
	scopeMenu = buildScopeMenu(false);
	transScopeMenu = buildScopeMenu(true);
	getSetupList(circuitsMenu, false);
	if (useFrame)
	 setMenuBar(mb);
	if (startCircuitText != null)
	 readSetup(startCircuitText);
	else if (stopMessage == null && startCircuit != null)
	 readSetupFile(startCircuit, startLabel);
	else
	 readSetup(null, 0, false);
	if (useFrame) {
	 Dimension screen = getToolkit().getScreenSize();
	 resize(860, 640);
	 handleResize();
	 Dimension x = getSize();
	 setLocation((screen.width - x.width)/2,
			(screen.height - x.height)/2);
	 show();
	} else {
	 if (!powerCheckItem.getState()) {
		main.remove(powerBar);
		main.remove(powerLabel);
		main.validate();
	 }
	 hide();
	 handleResize();
	 applet.validate();
	}
	requestFocus();
	addWindowListener(new WindowAdapter()
		{
			public void windowClosing(WindowEvent we)
			{
				destroyFrame();
			}
		}
	);
 }
 boolean shown = false;
 
 public void triggerShow() {
	if (!shown)
	 show();
	shown = true;
 }
 public void requestFocus()
 {
	super.requestFocus();
	cv.requestFocus();
 }
 
 PopupMenu buildScopeMenu(boolean t) {
	PopupMenu m = new PopupMenu();
	m.add(getMenuItem("Remove", "remove"));
	m.add(getMenuItem("Speed 2x", "speed2"));
	m.add(getMenuItem("Speed 1/2x", "speed1/2"));
	m.add(getMenuItem("Scale 2x", "scale"));
	m.add(getMenuItem("Max Scale", "maxscale"));
	m.add(getMenuItem("Stack", "stack"));
	m.add(getMenuItem("Unstack", "unstack"));
	m.add(getMenuItem("Reset", "reset"));
	if (t) {
	 m.add(scopeIbMenuItem = getCheckItem("Show Ib"));
	 m.add(scopeIcMenuItem = getCheckItem("Show Ic"));
	 m.add(scopeIeMenuItem = getCheckItem("Show Ie"));
	 m.add(scopeVbeMenuItem = getCheckItem("Show Vbe"));
	 m.add(scopeVbcMenuItem = getCheckItem("Show Vbc"));
	 m.add(scopeVceMenuItem = getCheckItem("Show Vce"));
	 m.add(scopeVceIcMenuItem = getCheckItem("Show Vce vs Ic"));
	} else {
	 m.add(scopeVMenuItem = getCheckItem("Show Voltage"));
	 m.add(scopeIMenuItem = getCheckItem("Show Current"));
	 m.add(scopePowerMenuItem = getCheckItem("Show Power Consumed"));
	 m.add(scopeMaxMenuItem = getCheckItem("Show Peak Value"));
	 m.add(scopeMinMenuItem = getCheckItem("Show Negative Peak Value"));
	 m.add(scopeFreqMenuItem = getCheckItem("Show Frequency"));
	 m.add(scopeVIMenuItem = getCheckItem("Show V vs I"));
	 m.add(scopeXYMenuItem = getCheckItem("Plot X/Y"));
	 m.add(scopeSelectYMenuItem = getMenuItem("Select Y", "selecty"));
	 m.add(scopeResistMenuItem
= getCheckItem("Show Resistance"));
	}
	main.add(m);
	return m;
 }
 
 MenuItem getMenuItem(String s) {
	MenuItem mi = new MenuItem(s);
	mi.addActionListener(this);
	return mi;
 }
 MenuItem getMenuItem(String s, String ac) {
	MenuItem mi = new MenuItem(s);
	mi.setActionCommand(ac);
	mi.addActionListener(this);
	return mi;
 }
 CheckboxMenuItem getCheckItem(String s) {
	CheckboxMenuItem mi = new CheckboxMenuItem(s);
	mi.addItemListener(this);
	mi.setActionCommand("");
	return mi;
 }
 CheckboxMenuItem getClassCheckItem(String s, String t) {
	try {
	 Class c = Class.forName(t);
	 CircuitElm elm = constructElement(c, 0, 0);
	 register(c, elm);
	 if ( elm.needsShortcut() ) {
		s += " (" + (char)elm.getShortcut() + ")";
	 }
	 elm.delete();
	} catch (Exception ee) {
	 ee.printStackTrace();
	}
	return getCheckItem(s, t);
 }
 
 CheckboxMenuItem getCheckItem(String s, String t) {
	CheckboxMenuItem mi = new CheckboxMenuItem(s);
	mi.addItemListener(this);
	mi.setActionCommand(t);
	return mi;
 }
 void register(Class c, CircuitElm elm) {
	int t = elm.getDumpType();
	if (t == 0) {
	 System.out.println("no dump type: " + c);
	 return;
	}
	int s = elm.getShortcut();
	if ( elm.needsShortcut() && s == 0 )
	{
	 if ( s == 0 )
	 {
		System.err.println("no shortcut " + c + " for " + c);
		return;
	 }
	 else if ( s <= ' ' || s >= 127 )
	 {
		System.err.println("invalid shortcut " + c + " for " + c);
		return;
	 }
	}
	Class dclass = elm.getDumpClass();
	if ( dumpTypes[t] != null && dumpTypes[t] != dclass ) {
	 System.out.println("dump type conflict: " + c + " " +
			 dumpTypes[t]);
	 return;
	}
	dumpTypes[t] = dclass;
	Class sclass = elm.getClass();
	if ( elm.needsShortcut() && shortcuts[s] != null &&
	 shortcuts[s] != sclass )
	{
	 System.err.println("shortcut conflict: " + c +
	 		 " (previously assigned to " +
			 shortcuts[s] + ")");
	}
	else
	{
	 shortcuts[s] = sclass;
	}
 }
 
 void handleResize() {
 winSize = cv.getSize();
	if (winSize.width == 0)
	 return;
	dbimage = main.createImage(winSize.width, winSize.height);
	int h = winSize.height / 5;
	/*if (h < 128 && winSize.height > 300)
	 h = 128;*/
	circuitArea = new Rectangle(0, 0, winSize.width, winSize.height-h);
	int i;
	int minx = 1000, maxx = 0, miny = 1000, maxy = 0;
	for (i = 0; i != elmList.size(); i++) {
	 CircuitElm ce = getElm(i);
	 // centered text causes problems when trying to center the circuit,
	 // so we special-case it here
	 if (!ce.isCenteredText()) {
		minx = min(ce.x, min(ce.x2, minx));
		maxx = max(ce.x, max(ce.x2, maxx));
	 }
	 miny = min(ce.y, min(ce.y2, miny));
	 maxy = max(ce.y, max(ce.y2, maxy));
	}
	// center circuit; we don't use snapGrid() because that rounds
	int dx = gridMask & ((circuitArea.width -(maxx-minx))/2-minx);
	int dy = gridMask & ((circuitArea.height-(maxy-miny))/2-miny);
	if (dx+minx < 0)
	 dx = gridMask & (-minx);
	if (dy+miny < 0)
	 dy = gridMask & (-miny);
	for (i = 0; i != elmList.size(); i++) {
	 CircuitElm ce = getElm(i);
	 ce.move(dx, dy);
	}
	// after moving elements, need this to avoid singular matrix probs
	needAnalyze();
	circuitBottom = 0;
 }
 void destroyFrame() {
	if (applet == null)
	{
	 dispose();
	 System.exit(0);
	}
	else
	{
	 applet.destroyFrame();
	}
 }
 
 public boolean handleEvent(Event ev) {
 if (ev.id == Event.WINDOW_DESTROY) {
	 destroyFrame();
 return true;
 }
 return super.handleEvent(ev);
 }
 
 public void paint(Graphics g) {
	cv.repaint();
 }
 static final int resct = 6;
 long lastTime = 0, lastFrameTime, lastIterTime, secTime = 0;
 int frames = 0;
 int steps = 0;
 int framerate = 0, steprate = 0;
 public void updateCircuit(Graphics realg) {
	CircuitElm realMouseElm;
	if (winSize == null || winSize.width == 0)
	 return;
	if (analyzeFlag) {
	 analyzeCircuit();
	 analyzeFlag = false;
	}
	if (editDialog != null && editDialog.elm instanceof CircuitElm)
	 mouseElm = (CircuitElm) (editDialog.elm);
	realMouseElm = mouseElm;
	if (mouseElm == null)
	 mouseElm = stopElm;
	setupScopes();
 Graphics2D g = null; // hausen: changed to Graphics2D
	g = (Graphics2D)dbimage.getGraphics();
	g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
		RenderingHints.VALUE_ANTIALIAS_ON);
	CircuitElm.selectColor = Color.cyan;
	if (printableCheckItem.getState()) {
 	 CircuitElm.whiteColor = Color.black;
 	 CircuitElm.lightGrayColor = Color.black;
 	 g.setColor(Color.white);
	} else {
	 CircuitElm.whiteColor = Color.white;
	 CircuitElm.lightGrayColor = Color.lightGray;
	 g.setColor(Color.black);
	}
	g.fillRect(0, 0, winSize.width, winSize.height);
	if (!stoppedCheck.getState()) {
	 try {
		runCircuit();
	 } catch (Exception e) {
		e.printStackTrace();
		analyzeFlag = true;
		cv.repaint();
		return;
	 }
	}
	if (!stoppedCheck.getState()) {
	 long sysTime = System.currentTimeMillis();
	 if (lastTime != 0) {
		int inc = (int) (sysTime-lastTime);
		double c = currentBar.getValue();
		c = java.lang.Math.exp(c/3.5-14.2);
		CircuitElm.currentMult = 1.7 * inc * c;
		if (!conventionCheckItem.getState())
		 CircuitElm.currentMult = -CircuitElm.currentMult;
	 }
	 if (sysTime-secTime >= 1000) {
		framerate = frames; steprate = steps;
		frames = 0; steps = 0;
		secTime = sysTime;
	 }
	 lastTime = sysTime;
	} else
	 lastTime = 0;
	CircuitElm.powerMult = Math.exp(powerBar.getValue()/4.762-7);
	
	int i;
	Font oldfont = g.getFont();
	for (i = 0; i != elmList.size(); i++) {
	 if (powerCheckItem.getState())
		g.setColor(Color.gray);
	 /*else if (conductanceCheckItem.getState())
	 g.setColor(Color.white);*/
	 getElm(i).draw(g);
	}
	if (tempMouseMode == MODE_DRAG_ROW || tempMouseMode == MODE_DRAG_COLUMN ||
	 tempMouseMode == MODE_DRAG_POST || tempMouseMode == MODE_DRAG_SELECTED)
	 for (i = 0; i != elmList.size(); i++) {
		CircuitElm ce = getElm(i);
		ce.drawPost(g, ce.x , ce.y );
		ce.drawPost(g, ce.x2, ce.y2);
	 }
	int badnodes = 0;
	// find bad connections, nodes not connected to other elements which
	// intersect other elements' bounding boxes
	// debugged by hausen: nullPointerException
	if ( nodeList != null )
	for (i = 0; i != nodeList.size(); i++) {
	 CircuitNode cn = getCircuitNode(i);
	 if (!cn.internal && cn.links.size() == 1) {
		int bb = 0, j;
		CircuitNodeLink cnl = cn.links.elementAt(0);
		for (j = 0; j != elmList.size(); j++)
		{ // TODO: (hausen) see if this change does not break stuff
		 CircuitElm ce = getElm(j);
		 if ( ce instanceof GraphicElm )
			continue;
		 if (cnl.elm != ce &&
			getElm(j).boundingBox.contains(cn.x, cn.y))
			bb++;
		}
		if (bb > 0) {
		 g.setColor(Color.red);
		 g.fillOval(cn.x-3, cn.y-3, 7, 7);
		 badnodes++;
		}
	 }
	}
	/*if (mouseElm != null) {
	 g.setFont(oldfont);
	 g.drawString("+", mouseElm.x+10, mouseElm.y);
	 }*/
	if (dragElm != null &&
	 (dragElm.x != dragElm.x2 || dragElm.y != dragElm.y2))
	 dragElm.draw(g);
	g.setFont(oldfont);
	int ct = scopeCount;
	if (stopMessage != null)
	 ct = 0;
	for (i = 0; i != ct; i++)
	 scopes[i].draw(g);
	g.setColor(CircuitElm.whiteColor);
	if (stopMessage != null) {
	 g.drawString(stopMessage, 10, circuitArea.height);
	} else {
	 if (circuitBottom == 0)
		calcCircuitBottom();
	 String info[] = new String[10];
	 if (mouseElm != null) {
		if (mousePost == -1)
		 mouseElm.getInfo(info);
		else
		 info[0] = "V = " +
			CircuitElm.getUnitText(mouseElm.getPostVoltage(mousePost), "V");
		/* //shownodes
		for (i = 0; i != mouseElm.getPostCount(); i++)
info[0] += " " + mouseElm.nodes[i];
		if (mouseElm.getVoltageSourceCount() > 0)
		 info[0] += ";" + (mouseElm.getVoltageSource()+nodeList.size());
		*/
		
	 } else {
		CircuitElm.showFormat.setMinimumFractionDigits(2);
		info[0] = "t = " + CircuitElm.getUnitText(t, "s");
		CircuitElm.showFormat.setMinimumFractionDigits(0);
	 }
	 if (hintType != -1) {
		for (i = 0; info[i] != null; i++)
		 ;
		String s = getHint();
		if (s == null)
		 hintType = -1;
		else
		 info[i] = s;
	 }
	 int x = 0;
	 if (ct != 0)
		x = scopes[ct-1].rightEdge() + 20;
	 x = max(x, winSize.width*2/3);
	 
	 // count lines of data
	 for (i = 0; info[i] != null; i++)
		;
	 if (badnodes > 0)
		info[i++] = badnodes + ((badnodes == 1) ?
					" bad connection" : " bad connections");
	 
	 // find where to show data; below circuit, not too high unless we need it
	 int ybase = winSize.height-15*i-5;
	 ybase = min(ybase, circuitArea.height);
	 ybase = max(ybase, circuitBottom);
	 for (i = 0; info[i] != null; i++)
		g.drawString(info[i], x,
			 ybase+15*(i+1));
	}
	if (selectedArea != null) {
	 g.setColor(CircuitElm.selectColor);
	 g.drawRect(selectedArea.x, selectedArea.y, selectedArea.width, selectedArea.height);
	}
	mouseElm = realMouseElm;
	frames++;
	/*
	g.setColor(Color.white);
	g.drawString("Framerate: " + framerate, 10, 10);
	g.drawString("Steprate: " + steprate, 10, 30);
	g.drawString("Steprate/iter: " + (steprate/getIterCount()), 10, 50);
	g.drawString("iterc: " + (getIterCount()), 10, 70);
	*/
	realg.drawImage(dbimage, 0, 0, this);
	if (!stoppedCheck.getState() && circuitMatrix != null) {
	 // Limit to 50 fps (thanks to Jurgen Klotzer for this)
	 long delay = 1000/50 - (System.currentTimeMillis() - lastFrameTime);
	 //realg.drawString("delay: " + delay, 10, 90);
	 if (delay > 0) {
		try {
		 Thread.sleep(delay);
		} catch (InterruptedException e) {
		}
	 }
	 
	 cv.repaint(0);
	}
	lastFrameTime = lastTime;
 }
 void setupScopes() {
	int i;
	
	// check scopes to make sure the elements still exist, and remove
	// unused scopes/columns
	int pos = -1;
	for (i = 0; i < scopeCount; i++) {
	 if (locateElm(scopes[i].elm) < 0)
		scopes[i].setElm(null);
	 if (scopes[i].elm == null) {
		int j;
		for (j = i; j != scopeCount; j++)
		 scopes[j] = scopes[j+1];
		scopeCount--;
		i--;
		continue;
	 }
	 if (scopes[i].position > pos+1)
		scopes[i].position = pos+1;
	 pos = scopes[i].position;
	}
	while (scopeCount > 0 && scopes[scopeCount-1].elm == null)
	 scopeCount--;
	int h = winSize.height - circuitArea.height;
	pos = 0;
	for (i = 0; i != scopeCount; i++)
	 scopeColCount[i] = 0;
	for (i = 0; i != scopeCount; i++) {
	 pos = max(scopes[i].position, pos);
	 scopeColCount[scopes[i].position]++;
	}
	int colct = pos+1;
	int iw = infoWidth;
	if (colct <= 2)
	 iw = iw*3/2;
	int w = (winSize.width-iw) / colct;
	int marg = 10;
	if (w < marg*2)
	 w = marg*2;
	pos = -1;
	int colh = 0;
	int row = 0;
	int speed = 0;
	for (i = 0; i != scopeCount; i++) {
	 Scope s = scopes[i];
	 if (s.position > pos) {
		pos = s.position;
		colh = h / scopeColCount[pos];
		row = 0;
		speed = s.speed;
	 }
	 if (s.speed != speed) {
		s.speed = speed;
		s.resetGraph();
	 }
	 Rectangle r = new Rectangle(pos*w, winSize.height-h+colh*row,
					w-marg, colh);
	 row++;
	 if (!r.equals(s.rect))
		s.setRect(r);
	}
 }
 
 String getHint() {
	CircuitElm c1 = getElm(hintItem1);
	CircuitElm c2 = getElm(hintItem2);
	if (c1 == null || c2 == null)
	 return null;
	if (hintType == HINT_LC) {
	 if (!(c1 instanceof InductorElm))
		return null;
	 if (!(c2 instanceof CapacitorElm))
		return null;
	 InductorElm ie = (InductorElm) c1;
	 CapacitorElm ce = (CapacitorElm) c2;
	 return "res.f = " + CircuitElm.getUnitText(1/(2*pi*Math.sqrt(ie.inductance*
						 ce.capacitance)), "Hz");
	}
	if (hintType == HINT_RC) {
	 if (!(c1 instanceof ResistorElm))
		return null;
	 if (!(c2 instanceof CapacitorElm))
		return null;
	 ResistorElm re = (ResistorElm) c1;
	 CapacitorElm ce = (CapacitorElm) c2;
	 return "RC = " + CircuitElm.getUnitText(re.resistance*ce.capacitance,
					 "s");
	}
	if (hintType == HINT_3DB_C) {
	 if (!(c1 instanceof ResistorElm))
		return null;
	 if (!(c2 instanceof CapacitorElm))
		return null;
	 ResistorElm re = (ResistorElm) c1;
	 CapacitorElm ce = (CapacitorElm) c2;
	 return "f.3db = " +
		CircuitElm.getUnitText(1/(2*pi*re.resistance*ce.capacitance), "Hz");
	}
	if (hintType == HINT_3DB_L) {
	 if (!(c1 instanceof ResistorElm))
		return null;
	 if (!(c2 instanceof InductorElm))
		return null;
	 ResistorElm re = (ResistorElm) c1;
	 InductorElm ie = (InductorElm) c2;
	 return "f.3db = " +
		CircuitElm.getUnitText(re.resistance/(2*pi*ie.inductance), "Hz");
	}
	if (hintType == HINT_TWINT) {
	 if (!(c1 instanceof ResistorElm))
		return null;
	 if (!(c2 instanceof CapacitorElm))
		return null;
	 ResistorElm re = (ResistorElm) c1;
	 CapacitorElm ce = (CapacitorElm) c2;
	 return "fc = " +
		CircuitElm.getUnitText(1/(2*pi*re.resistance*ce.capacitance), "Hz");
	}
	return null;
 }
 public void toggleSwitch(int n) {
	int i;
	for (i = 0; i != elmList.size(); i++) {
	 CircuitElm ce = getElm(i);
	 if (ce instanceof SwitchElm) {
		n--;
		if (n == 0) {
		 ((SwitchElm) ce).toggle();
		 analyzeFlag = true;
		 cv.repaint();
		 return;
		}
	 }
	}
 }
 
 void needAnalyze() {
	analyzeFlag = true;
	cv.repaint();
 }
 
 Vector<CircuitNode> nodeList;
 CircuitElm voltageSources[];
 public CircuitNode getCircuitNode(int n) {
	if (n >= nodeList.size())
	 return null;
	return nodeList.elementAt(n);
 }
 public CircuitElm getElm(int n) {
	if (n >= elmList.size())
	 return null;
	return elmList.elementAt(n);
 }
 
 void analyzeCircuit() {
	calcCircuitBottom();
	if (elmList.isEmpty())
	 return;
	stopMessage = null;
	stopElm = null;
	int i, j;
	int vscount = 0;
	nodeList = new Vector<CircuitNode>();
	boolean gotGround = false;
	boolean gotRail = false;
	CircuitElm volt = null;
	//System.out.println("ac1");
	// look for voltage or ground element
	for (i = 0; i != elmList.size(); i++) {
	 CircuitElm ce = getElm(i);
	 if (ce instanceof GroundElm) {
		gotGround = true;
		break;
	 }
	 if (ce instanceof RailElm)
		gotRail = true;
	 if (volt == null && ce instanceof VoltageElm)
		volt = ce;
	}
	// if no ground, and no rails, then the voltage elm's first terminal
	// is ground
	if (!gotGround && volt != null && !gotRail) {
	 CircuitNode cn = new CircuitNode();
	 Point pt = volt.getPost(0);
	 cn.x = pt.x;
	 cn.y = pt.y;
	 nodeList.addElement(cn);
	} else {
	 // otherwise allocate extra node for ground
	 CircuitNode cn = new CircuitNode();
	 cn.x = cn.y = -1;
	 nodeList.addElement(cn);
	}
	//System.out.println("ac2");
	// allocate nodes and voltage sources
	for (i = 0; i != elmList.size(); i++) {
	 CircuitElm ce = getElm(i);
	 int inodes = ce.getInternalNodeCount();
	 int ivs = ce.getVoltageSourceCount();
	 int posts = ce.getPostCount();
	 
	 // allocate a node for each post and match posts to nodes
	 for (j = 0; j != posts; j++) {
		Point pt = ce.getPost(j);
		int k;
		for (k = 0; k != nodeList.size(); k++) {
		 CircuitNode cn = getCircuitNode(k);
		 if (pt.x == cn.x && pt.y == cn.y)
			break;
		}
		if (k == nodeList.size()) {
		 CircuitNode cn = new CircuitNode();
		 cn.x = pt.x;
		 cn.y = pt.y;
		 CircuitNodeLink cnl = new CircuitNodeLink();
		 cnl.num = j;
		 cnl.elm = ce;
		 cn.links.addElement(cnl);
		 ce.setNode(j, nodeList.size());
nodeList.addElement(cn);
		} else {
		 CircuitNodeLink cnl = new CircuitNodeLink();
		 cnl.num = j;
		 cnl.elm = ce;
		 getCircuitNode(k).links.addElement(cnl);
		 ce.setNode(j, k);
		 // if it's the ground node, make sure the node voltage is 0,
		 // cause it may not get set later
		 if (k == 0)
			ce.setNodeVoltage(j, 0);
		}
	 }
	 for (j = 0; j != inodes; j++) {
		CircuitNode cn = new CircuitNode();
		cn.x = cn.y = -1;
		cn.internal = true;
		CircuitNodeLink cnl = new CircuitNodeLink();
		cnl.num = j+posts;
		cnl.elm = ce;
		cn.links.addElement(cnl);
		ce.setNode(cnl.num, nodeList.size());
		nodeList.addElement(cn);
	 }
	 vscount += ivs;
	}
	voltageSources = new CircuitElm[vscount];
	vscount = 0;
	circuitNonLinear = false;
	//System.out.println("ac3");
	// determine if circuit is nonlinear
	for (i = 0; i != elmList.size(); i++) {
	 CircuitElm ce = getElm(i);
	 if (ce.nonLinear())
		circuitNonLinear = true;
	 int ivs = ce.getVoltageSourceCount();
	 for (j = 0; j != ivs; j++) {
		voltageSources[vscount] = ce;
		ce.setVoltageSource(j, vscount++);
	 }
	}
	voltageSourceCount = vscount;
	int matrixSize = nodeList.size()-1 + vscount;
	circuitMatrix = new double[matrixSize][matrixSize];
	circuitRightSide = new double[matrixSize];
	origMatrix = new double[matrixSize][matrixSize];
	origRightSide = new double[matrixSize];
	circuitMatrixSize = circuitMatrixFullSize = matrixSize;
	circuitRowInfo = new RowInfo[matrixSize];
	circuitPermute = new int[matrixSize];
	int vs = 0;
	for (i = 0; i != matrixSize; i++)
	 circuitRowInfo[i] = new RowInfo();
	circuitNeedsMap = false;
	
	// stamp linear circuit elements
	for (i = 0; i != elmList.size(); i++) {
	 CircuitElm ce = getElm(i);
	 ce.stamp();
	}
	//System.out.println("ac4");
	// determine nodes that are unconnected
	boolean closure[] = new boolean[nodeList.size()];
	boolean tempclosure[] = new boolean[nodeList.size()];
	boolean changed = true;
	closure[0] = true;
	while (changed) {
	 changed = false;
	 for (i = 0; i != elmList.size(); i++) {
		CircuitElm ce = getElm(i);
		// loop through all ce's nodes to see if they are connected
		// to other nodes not in closure
		for (j = 0; j < ce.getPostCount(); j++) {
		 if (!closure[ce.getNode(j)]) {
			if (ce.hasGroundConnection(j))
			 closure[ce.getNode(j)] = changed = true;
			continue;
		 }
		 int k;
		 for (k = 0; k != ce.getPostCount(); k++) {
			if (j == k)
			 continue;
			int kn = ce.getNode(k);
			if (ce.getConnection(j, k) && !closure[kn]) {
			 closure[kn] = true;
			 changed = true;
			}
		 }
		}
	 }
	 if (changed)
		continue;
	 // connect unconnected nodes
	 for (i = 0; i != nodeList.size(); i++)
		if (!closure[i] && !getCircuitNode(i).internal) {
		 System.out.println("node " + i + " unconnected");
		 stampResistor(0, i, 1e8);
		 closure[i] = true;
		 changed = true;
		 break;
		}
	}
	//System.out.println("ac5");
	for (i = 0; i != elmList.size(); i++) {
	 CircuitElm ce = getElm(i);
	 // look for inductors with no current path
	 if (ce instanceof InductorElm) {
		FindPathInfo fpi = new FindPathInfo(FindPathInfo.INDUCT, ce,
						 ce.getNode(1));
		// first try findPath with maximum depth of 5, to avoid slowdowns
		if (!fpi.findPath(ce.getNode(0), 5) &&
		 !fpi.findPath(ce.getNode(0))) {
		 System.out.println(ce + " no path");
		 ce.reset();
		}
	 }
	 // look for current sources with no current path
	 if (ce instanceof CurrentElm) {
		FindPathInfo fpi = new FindPathInfo(FindPathInfo.INDUCT, ce,
						 ce.getNode(1));
		if (!fpi.findPath(ce.getNode(0))) {
		 stop("No path for current source!", ce);
		 return;
		}
	 }
	 // look for voltage source loops
	 if ((ce instanceof VoltageElm && ce.getPostCount() == 2) ||
		ce instanceof WireElm) {
		FindPathInfo fpi = new FindPathInfo(FindPathInfo.VOLTAGE, ce,
						 ce.getNode(1));
		if (fpi.findPath(ce.getNode(0))) {
		 stop("Voltage source/wire loop with no resistance!", ce);
		 return;
		}
	 }
	 // look for shorted caps, or caps w/ voltage but no R
	 if (ce instanceof CapacitorElm) {
		FindPathInfo fpi = new FindPathInfo(FindPathInfo.SHORT, ce,
						 ce.getNode(1));
		if (fpi.findPath(ce.getNode(0))) {
		 System.out.println(ce + " shorted");
		 ce.reset();
		} else {
		 fpi = new FindPathInfo(FindPathInfo.CAP_V, ce, ce.getNode(1));
		 if (fpi.findPath(ce.getNode(0))) {
			stop("Capacitor loop with no resistance!", ce);
			return;
		 }
		}
	 }
	}
	//System.out.println("ac6");
	// simplify the matrix; this speeds things up quite a bit
	for (i = 0; i != matrixSize; i++) {
	 int qm = -1, qp = -1;
	 double qv = 0;
	 RowInfo re = circuitRowInfo[i];
	 /*System.out.println("row " + i + " " + re.lsChanges + " " + re.rsChanges + " " +
			 re.dropRow);*/
	 if (re.lsChanges || re.dropRow || re.rsChanges)
		continue;
	 double rsadd = 0;
	 // look for rows that can be removed
	 for (j = 0; j != matrixSize; j++) {
		double q = circuitMatrix[i][j];
		if (circuitRowInfo[j].type == RowInfo.ROW_CONST) {
		 // keep a running total of const values that have been
		 // removed already
		 rsadd -= circuitRowInfo[j].value*q;
		 continue;
		}
		if (q == 0)
		 continue;
		if (qp == -1) {
		 qp = j;
		 qv = q;
		 continue;
		}
		if (qm == -1 && q == -qv) {
		 qm = j;
		 continue;
		}
		break;
	 }
	 //System.out.println("line " + i + " " + qp + " " + qm + " " + j);
	 /*if (qp != -1 && circuitRowInfo[qp].lsChanges) {
		System.out.println("lschanges");
		continue;
	 }
	 if (qm != -1 && circuitRowInfo[qm].lsChanges) {
		System.out.println("lschanges");
		continue;
		}*/
	 if (j == matrixSize) {
		if (qp == -1) {
		 stop("Matrix error", null);
		 return;
		}
		RowInfo elt = circuitRowInfo[qp];
		if (qm == -1) {
		 // we found a row with only one nonzero entry; that value
		 // is a constant
		 int k;
		 for (k = 0; elt.type == RowInfo.ROW_EQUAL && k < 100; k++) {
			// follow the chain
			/*System.out.println("following equal chain from " +
					 i + " " + qp + " to " + elt.nodeEq);*/
			qp = elt.nodeEq;
			elt = circuitRowInfo[qp];
		 }
		 if (elt.type == RowInfo.ROW_EQUAL) {
			// break equal chains
			//System.out.println("Break equal chain");
			elt.type = RowInfo.ROW_NORMAL;
			continue;
		 }
		 if (elt.type != RowInfo.ROW_NORMAL) {
			System.out.println("type already " + elt.type + " for " + qp + "!");
			continue;
		 }
		 elt.type = RowInfo.ROW_CONST;
		 elt.value = (circuitRightSide[i]+rsadd)/qv;
		 circuitRowInfo[i].dropRow = true;
		 //System.out.println(qp + " * " + qv + " = const " + elt.value);
		 i = -1; // start over from scratch
		} else if (circuitRightSide[i]+rsadd == 0) {
		 // we found a row with only two nonzero entries, and one
		 // is the negative of the other; the values are equal
		 if (elt.type != RowInfo.ROW_NORMAL) {
			//System.out.println("swapping");
			int qq = qm;
			qm = qp; qp = qq;
			elt = circuitRowInfo[qp];
			if (elt.type != RowInfo.ROW_NORMAL) {
			 // we should follow the chain here, but this
			 // hardly ever happens so it's not worth worrying
			 // about
			 System.out.println("swap failed");
			 continue;
			}
		 }
		 elt.type = RowInfo.ROW_EQUAL;
		 elt.nodeEq = qm;
		 circuitRowInfo[i].dropRow = true;
		 //System.out.println(qp + " = " + qm);
		}
	 }
	}
	//System.out.println("ac7");
	// find size of new matrix
	int nn = 0;
	for (i = 0; i != matrixSize; i++) {
	 RowInfo elt = circuitRowInfo[i];
	 if (elt.type == RowInfo.ROW_NORMAL) {
		elt.mapCol = nn++;
		//System.out.println("col " + i + " maps to "
+ elt.mapCol);
		continue;
	 }
	 if (elt.type == RowInfo.ROW_EQUAL) {
		RowInfo e2 = null;
		// resolve chains of equality; 100 max steps to avoid loops
		for (j = 0; j != 100; j++) {
		 e2 = circuitRowInfo[elt.nodeEq];
		 if (e2.type != RowInfo.ROW_EQUAL)
			break;
		 if (i == e2.nodeEq)
			break;
		 elt.nodeEq = e2.nodeEq;
		}
	 }
	 if (elt.type == RowInfo.ROW_CONST)
		elt.mapCol = -1;
	}
	for (i = 0; i != matrixSize; i++) {
	 RowInfo elt = circuitRowInfo[i];
	 if (elt.type == RowInfo.ROW_EQUAL) {
		RowInfo e2 = circuitRowInfo[elt.nodeEq];
		if (e2.type == RowInfo.ROW_CONST) {
		 // if something is equal to a const, it's a const
		 elt.type = e2.type;
		 elt.value = e2.value;
		 elt.mapCol = -1;
		 //System.out.println(i + " = [late]const " + elt.value);
		} else {
		 elt.mapCol = e2.mapCol;
		 //System.out.println(i + " maps to: " + e2.mapCol);
		}
	 }
	}
	//System.out.println("ac8");
	/*System.out.println("matrixSize = " + matrixSize);
	
	for (j = 0; j != circuitMatrixSize; j++) {
	 System.out.println(j + ": ");
	 for (i = 0; i != circuitMatrixSize; i++)
		System.out.print(circuitMatrix[j][i] + " ");
	 System.out.print(" " + circuitRightSide[j] + "\n");
	}
	System.out.print("\n");*/
	
	// make the new, simplified matrix
	int newsize = nn;
	double newmatx[][] = new double[newsize][newsize];
	double newrs [] = new double[newsize];
	int ii = 0;
	for (i = 0; i != matrixSize; i++) {
	 RowInfo rri = circuitRowInfo[i];
	 if (rri.dropRow) {
		rri.mapRow = -1;
		continue;
	 }
	 newrs[ii] = circuitRightSide[i];
	 rri.mapRow = ii;
	 //System.out.println("Row " + i + " maps to " + ii);
	 for (j = 0; j != matrixSize; j++) {
		RowInfo ri = circuitRowInfo[j];
		if (ri.type == RowInfo.ROW_CONST)
		 newrs[ii] -= ri.value*circuitMatrix[i][j];
		else
		 newmatx[ii][ri.mapCol] += circuitMatrix[i][j];
	 }
	 ii++;
	}
	circuitMatrix = newmatx;
	circuitRightSide = newrs;
	matrixSize = circuitMatrixSize = newsize;
	for (i = 0; i != matrixSize; i++)
	 origRightSide[i] = circuitRightSide[i];
	for (i = 0; i != matrixSize; i++)
	 for (j = 0; j != matrixSize; j++)
		origMatrix[i][j] = circuitMatrix[i][j];
	circuitNeedsMap = true;
	/*
	System.out.println("matrixSize = " + matrixSize + " " + circuitNonLinear);
	for (j = 0; j != circuitMatrixSize; j++) {
	 for (i = 0; i != circuitMatrixSize; i++)
		System.out.print(circuitMatrix[j][i] + " ");
	 System.out.print(" " + circuitRightSide[j] + "\n");
	}
	System.out.print("\n");*/
	// if a matrix is linear, we can do the lu_factor here instead of
	// needing to do it every frame
	if (!circuitNonLinear) {
	 if (!lu_factor(circuitMatrix, circuitMatrixSize, circuitPermute)) {
		stop("Singular matrix!", null);
		return;
	 }
	}
 }
 void calcCircuitBottom() {
	int i;
	circuitBottom = 0;
	for (i = 0; i != elmList.size(); i++) {
	 Rectangle rect = getElm(i).boundingBox;
	 int bottom = rect.height + rect.y;
	 if (bottom > circuitBottom)
		circuitBottom = bottom;
	}
 }
 
 class FindPathInfo {
	static final int INDUCT = 1;
	static final int VOLTAGE = 2;
	static final int SHORT = 3;
	static final int CAP_V = 4;
	boolean used[];
	int dest;
	CircuitElm firstElm;
	int type;
	FindPathInfo(int t, CircuitElm e, int d) {
	 dest = d;
	 type = t;
	 firstElm = e;
	 used = new boolean[nodeList.size()];
	}
	boolean findPath(int n1) { return findPath(n1, -1); }
	boolean findPath(int n1, int depth) {
	 if (n1 == dest)
		return true;
	 if (depth-- == 0)
		return false;
	 if (used[n1]) {
		//System.out.println("used " + n1);
		return false;
	 }
	 used[n1] = true;
	 int i;
	 for (i = 0; i != elmList.size(); i++) {
		CircuitElm ce = getElm(i);
		if (ce == firstElm)
		 continue;
		if (type == INDUCT) {
		 if (ce instanceof CurrentElm)
			continue;
		}
		if (type == VOLTAGE) {
		 if (!(ce.isWire() || ce instanceof VoltageElm))
			continue;
		}
		if (type == SHORT && !ce.isWire())
		 continue;
		if (type == CAP_V) {
		 if (!(ce.isWire() || ce instanceof CapacitorElm ||
			 ce instanceof VoltageElm))
			continue;
		}
		if (n1 == 0) {
		 // look for posts which have a ground connection;
		 // our path can go through ground
		 int j;
		 for (j = 0; j != ce.getPostCount(); j++)
			if (ce.hasGroundConnection(j) &&
			 findPath(ce.getNode(j), depth)) {
			 used[n1] = false;
			 return true;
			}
		}
		int j;
		for (j = 0; j != ce.getPostCount(); j++) {
		 //System.out.println(ce + " " + ce.getNode(j));
		 if (ce.getNode(j) == n1)
			break;
		}
		if (j == ce.getPostCount())
		 continue;
		if (ce.hasGroundConnection(j) && findPath(0, depth)) {
		 //System.out.println(ce + " has ground");
		 used[n1] = false;
		 return true;
		}
		if (type == INDUCT && ce instanceof InductorElm) {
		 double c = ce.getCurrent();
		 if (j == 0)
			c = -c;
		 //System.out.println("matching " + c + " to " + firstElm.getCurrent());
		 //System.out.println(ce + " " + firstElm);
		 if (Math.abs(c-firstElm.getCurrent()) > 1e-10)
			continue;
		}
		int k;
		for (k = 0; k != ce.getPostCount(); k++) {
		 if (j == k)
			continue;
		 //System.out.println(ce + " " + ce.getNode(j) + "-" + ce.getNode(k));
		 if (ce.getConnection(j, k) && findPath(ce.getNode(k), depth)) {
			//System.out.println("got findpath " + n1);
			used[n1] = false;
			return true;
		 }
		 //System.out.println("back on findpath " + n1);
		}
	 }
	 used[n1] = false;
	 //System.out.println(n1 + " failed");
	 return false;
	}
 }
 void stop(String s, CircuitElm ce) {
	stopMessage = s;
	circuitMatrix = null;
	stopElm = ce;
	stoppedCheck.setState(true);
	analyzeFlag = false;
	cv.repaint();
 }
 
 // control voltage source vs with voltage from n1 to n2 (must
 // also call stampVoltageSource())
 void stampVCVS(int n1, int n2, double coef, int vs) {
	int vn = nodeList.size()+vs;
	stampMatrix(vn, n1, coef);
	stampMatrix(vn, n2, -coef);
 }
 
 // stamp independent voltage source #vs, from n1 to n2, amount v
 void stampVoltageSource(int n1, int n2, int vs, double v) {
	int vn = nodeList.size()+vs;
	stampMatrix(vn, n1, -1);
	stampMatrix(vn, n2, 1);
	stampRightSide(vn, v);
	stampMatrix(n1, vn, 1);
	stampMatrix(n2, vn, -1);
 }
 // use this if the amount of voltage is going to be updated in doStep()
 void stampVoltageSource(int n1, int n2, int vs) {
	int vn = nodeList.size()+vs;
	stampMatrix(vn, n1, -1);
	stampMatrix(vn, n2, 1);
	stampRightSide(vn);
	stampMatrix(n1, vn, 1);
	stampMatrix(n2, vn, -1);
 }
 
 void updateVoltageSource(int n1, int n2, int vs, double v) {
	int vn = nodeList.size()+vs;
	stampRightSide(vn, v);
 }
 
 void stampResistor(int n1, int n2, double r) {
	double r0 = 1/r;
	if (Double.isNaN(r0) || Double.isInfinite(r0)) {
	 System.out.print("bad resistance " + r + " " + r0 + "\n");
	 int a = 0;
	 a /= a;
	}
	stampMatrix(n1, n1, r0);
	stampMatrix(n2, n2, r0);
	stampMatrix(n1, n2, -r0);
	stampMatrix(n2, n1, -r0);
 }
 void stampConductance(int n1, int n2, double r0) {
	stampMatrix(n1, n1, r0);
	stampMatrix(n2, n2, r0);
	stampMatrix(n1, n2, -r0);
	stampMatrix(n2, n1, -r0);
 }
 // current from cn1 to cn2 is equal to voltage from vn1 to 2, divided by g
 void stampVCCurrentSource(int cn1, int cn2, int vn1, int vn2, double g) {
	stampMatrix(cn1, vn1, g);
	stampMatrix(cn2, vn2, g);
	stampMatrix(cn1, vn2, -g);
	stampMatrix(cn2, vn1, -g);
 }
 void stampCurrentSource(int n1, int n2, double i) {
	stampRightSide(n1, -i);
	stampRightSide(n2, i);
 }
 // stamp a current source from n1 to n2 depending on current through vs
 void stampCCCS(int

Teste o Premium para desbloquear

Aproveite todos os benefícios por 3 dias sem pagar! 😉
Já tem cadastro?

Outros materiais