From e9d4bf0066f9e3a51a2759d49d82428463f6976f Mon Sep 17 00:00:00 2001 From: lizhenhai <248786960@qq.com> Date: Sun, 17 Dec 2023 16:47:39 +0800 Subject: [PATCH 1/5] Using Sparse Matrix LU to Decompose and Solve Equations --- .../circuitjs1/client/CirSim.java | 16 +- .../circuitjs1/client/matrix/DGrowArray.java | 80 +++ .../client/matrix/DMatrixSparseCSC.java | 461 ++++++++++++++++++ .../circuitjs1/client/matrix/IGrowArray.java | 75 +++ .../circuitjs1/client/matrix/SparseLU.java | 332 +++++++++++++ 5 files changed, 961 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/lushprojects/circuitjs1/client/matrix/DGrowArray.java create mode 100644 src/main/java/com/lushprojects/circuitjs1/client/matrix/DMatrixSparseCSC.java create mode 100644 src/main/java/com/lushprojects/circuitjs1/client/matrix/IGrowArray.java create mode 100644 src/main/java/com/lushprojects/circuitjs1/client/matrix/SparseLU.java diff --git a/src/main/java/com/lushprojects/circuitjs1/client/CirSim.java b/src/main/java/com/lushprojects/circuitjs1/client/CirSim.java index 315fa32..17b700c 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/CirSim.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/CirSim.java @@ -95,6 +95,8 @@ import static com.google.gwt.event.dom.client.KeyCodes.*; import com.google.gwt.user.client.ui.Frame; import com.google.gwt.user.client.ui.Widget; +import com.lushprojects.circuitjs1.client.matrix.DMatrixSparseCSC; +import com.lushprojects.circuitjs1.client.matrix.SparseLU; import com.lushprojects.circuitjs1.client.util.Locale; import com.lushprojects.circuitjs1.client.util.PerfMonitor; import com.google.gwt.user.client.Window.ClosingEvent; @@ -5625,7 +5627,12 @@ public void onPreviewNativeEvent(NativePreviewEvent e) { // indices, used in the lu_solve() routine. static boolean lu_factor(double a[][], int n, int ipvt[]) { int i,j,k; - + + if(n>100){ + DMatrixSparseCSC dMatrixRMaj = DMatrixSparseCSC.convert(a,DMatrixSparseCSC.EPS); + return sparseLU.setA(dMatrixRMaj); + } + // check for a possible singular matrix by scanning for rows that // are all zeroes for (i = 0; i != n; i++) { @@ -5702,13 +5709,16 @@ static boolean lu_factor(double a[][], int n, int ipvt[]) { } return true; } - + final static SparseLU sparseLU = new SparseLU(); // Solves the set of n linear equations using a LU factorization // previously performed by lu_factor. On input, b[0..n-1] is the right // hand side of the equations, and on output, contains the solution. static void lu_solve(double a[][], int n, int ipvt[], double b[]) { int i; - + if(n>100){ + sparseLU.solve(b,b); + return; + } // find first nonzero b element for (i = 0; i != n; i++) { int row = ipvt[i]; diff --git a/src/main/java/com/lushprojects/circuitjs1/client/matrix/DGrowArray.java b/src/main/java/com/lushprojects/circuitjs1/client/matrix/DGrowArray.java new file mode 100644 index 0000000..2cad0d5 --- /dev/null +++ b/src/main/java/com/lushprojects/circuitjs1/client/matrix/DGrowArray.java @@ -0,0 +1,80 @@ +package com.lushprojects.circuitjs1.client.matrix; + +public class DGrowArray { + public double[] data; + public int length; + + public DGrowArray( int length ) { + this.data = new double[length]; + this.length = length; + } + + public DGrowArray() { + this(0); + } + + public int length() { + return length; + } + + public void reset() {reshape(0);} + + /** + * Changes the array's length and doesn't attempt to preserve previous values if a new array is required + * + * @param length New array length + */ + public DGrowArray reshape( int length ) { + if (data.length < length) { + data = new double[length]; + } + this.length = length; + return this; + } + + /** + * Increases the internal array's length by the specified amount. Previous values are preserved. + * The length value is not modified since this does not change the 'meaning' of the array, just + * increases the amount of data which can be stored in it. + * + * this.data = new data_type[ data.length + amount ] + * + * @param amount Number of elements added to the internal array's length + */ + public void growInternal( int amount ) { + double[] tmp = new double[data.length + amount]; + + System.arraycopy(data, 0, tmp, 0, data.length); + this.data = tmp; + } + + public void setTo( DGrowArray original ) { + reshape(original.length); + System.arraycopy(original.data, 0, data, 0, original.length); + } + + public void add( double value ) { + if (length >= data.length) { + growInternal(Math.min(500_000, data.length + 10)); + } + + data[length++] = value; + } + + public double get( int index ) { + if (index < 0 || index >= length) + throw new IllegalArgumentException("Out of bounds"); + return data[index]; + } + + public void set( int index, double value ) { + if (index < 0 || index >= length) + throw new IllegalArgumentException("Out of bounds"); + data[index] = value; + } + + public void free() { + data = new double[0]; + length = 0; + } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/matrix/DMatrixSparseCSC.java b/src/main/java/com/lushprojects/circuitjs1/client/matrix/DMatrixSparseCSC.java new file mode 100644 index 0000000..4f91af1 --- /dev/null +++ b/src/main/java/com/lushprojects/circuitjs1/client/matrix/DMatrixSparseCSC.java @@ -0,0 +1,461 @@ +package com.lushprojects.circuitjs1.client.matrix; + +import java.util.Arrays; +import java.util.Iterator; +public class DMatrixSparseCSC { + /** + * Storage for non-zero values. Only valid up to length-1. + */ + public double[] nz_values = new double[0]; + /** + * Length of data. Number of non-zero values in the matrix + */ + public int nz_length; + /** + * Specifies which row a specific non-zero value corresponds to. If they are sorted or not with in each column + * is specified by the {@link #indicesSorted} flag. + */ + public int[] nz_rows = new int[0]; + /** + * Stores the range of indexes in the non-zero lists that belong to each column. Column 'i' corresponds to + * indexes col_idx[i] to col_idx[i+1]-1, inclusive. + */ + public int[] col_idx; + + /** + * Number of rows in the matrix + */ + public int numRows; + /** + * Number of columns in the matrix + */ + public int numCols; + + /** + * Flag that's used to indicate of the row indices are sorted or not. + */ + public boolean indicesSorted = false; + + public static double EPS = Math.pow(2.0, -52.0); + /** + * Constructor with a default arrayLength of zero. + * + * @param numRows Number of rows + * @param numCols Number of columns + */ + public DMatrixSparseCSC(int numRows, int numCols) { + this(numRows, numCols, 0); + } + + /** + * Specifies shape and number of non-zero elements that can be stored. + * + * @param numRows Number of rows + * @param numCols Number of columns + * @param arrayLength Initial maximum number of non-zero elements that can be in the matrix + */ + public DMatrixSparseCSC(int numRows, int numCols, int arrayLength) { + if (numRows < 0 || numCols < 0 || arrayLength < 0) + throw new IllegalArgumentException("Rows, columns, and arrayLength must be not be negative"); + this.numRows = numRows; + this.numCols = numCols; + this.nz_length = 0; + col_idx = new int[numCols + 1]; + growMaxLength(arrayLength, false); + } + + public DMatrixSparseCSC(DMatrixSparseCSC original) { + this(original.numRows, original.numCols, original.nz_length); + + setTo(original); + } + + public int getNumRows() { + return numRows; + } + + public int getNumCols() { + return numCols; + } + + public DMatrixSparseCSC copy() { + return new DMatrixSparseCSC(this); + } + + public DMatrixSparseCSC createLike() { + return new DMatrixSparseCSC(numRows, numCols); + } + + public void setTo(DMatrixSparseCSC original) { + DMatrixSparseCSC o = original; + reshape(o.numRows, o.numCols, o.nz_length); + this.nz_length = o.nz_length; + + System.arraycopy(o.nz_values, 0, nz_values, 0, nz_length); + System.arraycopy(o.nz_rows, 0, nz_rows, 0, nz_length); + System.arraycopy(o.col_idx, 0, col_idx, 0, numCols + 1); + this.indicesSorted = o.indicesSorted; + } + +// public void print() { +// MatrixIO.printFancy(System.out, this, MatrixIO.DEFAULT_LENGTH); +// } + +// public void print(String format) { +// MatrixIO.print(System.out, this, format); +// } + +// public void printNonZero() { +// String format = "%d %d " + MatrixIO.DEFAULT_FLOAT_FORMAT + "\n"; +// System.out.println("Type = " + getType().name() + " , rows = " + numRows + " , cols = " + numCols +// + " , nz_length = " + nz_length); +// +// for (int col = 0; col < numCols; col++) { +// int idx0 = col_idx[col]; +// int idx1 = col_idx[col + 1]; +// +// for (int i = idx0; i < idx1; i++) { +// int row = nz_rows[i]; +// double value = nz_values[i]; +// +// System.out.printf(format, row, col, value); +// } +// } +// } + + public boolean isAssigned(int row, int col) { + return nz_index(row, col) >= 0; + } + + public double get(int row, int col) { + if (row < 0 || row >= numRows || col < 0 || col >= numCols) + throw new IllegalArgumentException("Outside of matrix bounds"); + + return unsafe_get(row, col); + } + + public double get(int row, int col, double fallBackValue) { + if (row < 0 || row >= numRows || col < 0 || col >= numCols) + throw new IllegalArgumentException("Outside of matrix bounds"); + + return unsafe_get(row, col, fallBackValue); + } + + public double unsafe_get(int row, int col) { + int index = nz_index(row, col); + if (index >= 0) + return nz_values[index]; + return 0; + } + + public double unsafe_get(int row, int col, double fallBackValue) { + int index = nz_index(row, col); + if (index >= 0) + return nz_values[index]; + return fallBackValue; + } + + /** + * Returns the index in nz_rows for the element at (row,col) if it already exists in the matrix. If not then -1 + * is returned. + * + * @param row row coordinate + * @param col column coordinate + * @return nz_row index or -1 if the element does not exist + */ + public int nz_index(int row, int col) { + int col0 = col_idx[col]; + int col1 = col_idx[col + 1]; + + if (this.indicesSorted) { + return Arrays.binarySearch(nz_rows, col0, col1, row); + } else { + for (int i = col0; i < col1; i++) { + if (nz_rows[i] == row) { + return i; + } + } + return -1; + } + } + + public void set(int row, int col, double val) { + if (row < 0 || row >= numRows || col < 0 || col >= numCols) + throw new IllegalArgumentException("Outside of matrix bounds"); + + unsafe_set(row, col, val); + } + + public void unsafe_set(int row, int col, double val) { + int index = nz_index(row, col); + if (index >= 0) { + nz_values[index] = val; + } else { + + int idx0 = col_idx[col]; + int idx1 = col_idx[col + 1]; + + // determine the index the new element should be inserted at. This is done to keep it sorted if + // it was already sorted + for (index = idx0; index < idx1; index++) { + if (row < nz_rows[index]) { + break; + } + } + + // shift all the col_idx after this point by 1 + for (int i = col + 1; i <= numCols; i++) { + col_idx[i]++; + } + + // if it's already at the maximum array length grow the arrays + if (nz_length >= nz_values.length) + growMaxLength(nz_length * 2 + 1, true); + + // shift everything by one + for (int i = nz_length; i > index; i--) { + nz_rows[i] = nz_rows[i - 1]; + nz_values[i] = nz_values[i - 1]; + } + nz_rows[index] = row; + nz_values[index] = val; + nz_length++; + } + } + + public void remove(int row, int col) { + int index = nz_index(row, col); + + if (index < 0) // it's not in the nz structure + return; + + // shift all the col_idx after this point by -1 + for (int i = col + 1; i <= numCols; i++) { + col_idx[i]--; + } + + nz_length--; + for (int i = index; i < nz_length; i++) { + nz_rows[i] = nz_rows[i + 1]; + nz_values[i] = nz_values[i + 1]; + } + } + + public void zero() { + Arrays.fill(col_idx, 0, numCols + 1, 0); + nz_length = 0; + indicesSorted = false; // see justification in reshape + } + + public DMatrixSparseCSC create(int numRows, int numCols) { + return new DMatrixSparseCSC(numRows, numCols); + } + + public int getNonZeroLength() { + return nz_length; + } + + public void reshape(int numRows, int numCols, int arrayLength) { + if (numRows < 0 || numCols < 0 || arrayLength < 0) + throw new IllegalArgumentException("Rows, columns, and arrayLength must be not be negative"); + + // OK so technically it is sorted, but forgetting to correctly set this flag is a common mistake so + // decided to be conservative and mark it as unsorted so that stuff doesn't blow up + this.indicesSorted = false; + this.numRows = numRows; + this.numCols = numCols; + growMaxLength(arrayLength, false); + this.nz_length = 0; + + if (numCols + 1 > col_idx.length) { + col_idx = new int[numCols + 1]; + } else { + Arrays.fill(col_idx, 0, numCols + 1, 0); + } + } + + + public void reshape(int numRows, int numCols) { + reshape(numRows, numCols, 0); + } + + + public void shrinkArrays() { + if (nz_length < nz_values.length) { + double[] tmp_values = new double[nz_length]; + int[] tmp_rows = new int[nz_length]; + + System.arraycopy(this.nz_values, 0, tmp_values, 0, nz_length); + System.arraycopy(this.nz_rows, 0, tmp_rows, 0, nz_length); + + this.nz_values = tmp_values; + this.nz_rows = tmp_rows; + } + } + + /** + * Increases the maximum size of the data array so that it can store sparse data up to 'length'. The class + * parameter nz_length is not modified by this function call. + * + * @param arrayLength Desired maximum length of sparse data + * @param preserveValue If true the old values will be copied into the new arrays. If false that step will be skipped. + */ + public void growMaxLength(int arrayLength, boolean preserveValue) { + if (arrayLength < 0) + throw new IllegalArgumentException("Negative array length. Overflow?"); + + // NOTE: The code below has been (experimentally) commented out. A situation arose where we wanted to exceed + // the max physical size, which would then be corrected later on. + + // see if multiplying numRows*numCols will cause an overflow. If it won't then pick the smaller of the two +// if( numRows != 0 && numCols <= Integer.MAX_VALUE / numRows ) { +// // save the user from themselves +// arrayLength = Math.min(numRows*numCols, arrayLength); +// } + if (arrayLength > this.nz_values.length) { + double[] data = new double[arrayLength]; + int[] row_idx = new int[arrayLength]; + + if (preserveValue) { + System.arraycopy(this.nz_values, 0, data, 0, this.nz_length); + System.arraycopy(this.nz_rows, 0, row_idx, 0, this.nz_length); + } + + this.nz_values = data; + this.nz_rows = row_idx; + } + } + + /** + * Increases the maximum number of columns in the matrix. + * + * @param desiredColumns Desired number of columns. + * @param preserveValue If the array needs to be expanded should it copy the previous values? + */ + public void growMaxColumns(int desiredColumns, boolean preserveValue) { + if (col_idx.length < desiredColumns + 1) { + int[] c = new int[desiredColumns + 1]; + if (preserveValue) + System.arraycopy(col_idx, 0, c, 0, col_idx.length); + col_idx = c; + } + } + + /** + * Given the histogram of columns compute the col_idx for the matrix. nz_length is automatically set and + * nz_values will grow if needed. + * + * @param histogram histogram of column values in the sparse matrix. modified, see above. + */ + public void histogramToStructure(int[] histogram) { + col_idx[0] = 0; + int index = 0; + for (int i = 1; i <= numCols; i++) { + col_idx[i] = index += histogram[i - 1]; + } + nz_length = index; + growMaxLength(nz_length, false); + if (col_idx[numCols] != nz_length) + throw new RuntimeException("Egads"); + } + + /** + * Copies the non-zero structure of orig into "this" + * + * @param orig Matrix who's structure is to be copied + */ + public void copyStructure(DMatrixSparseCSC orig) { + reshape(orig.numRows, orig.numCols, orig.nz_length); + this.nz_length = orig.nz_length; + System.arraycopy(orig.col_idx, 0, col_idx, 0, orig.numCols + 1); + System.arraycopy(orig.nz_rows, 0, nz_rows, 0, orig.nz_length); + } + + /** + * If the indices has been sorted or not + * + * @return true if sorted or false if not sorted + */ + public boolean isIndicesSorted() { + return indicesSorted; + } + + /** + * Returns true if number of non-zero elements is the maximum size + * + * @return true if no more non-zero elements can be added + */ + public boolean isFull() { + return nz_length == numRows * numCols; + } + + public static DMatrixSparseCSC convert(double circuitMatrix[][], double tol) { + int nonzero =0; + for (int i = 0; i != circuitMatrix.length; i++) + for (int j = 0; j != circuitMatrix.length; j++){ + if( circuitMatrix[i][j]!=0){ + nonzero++; + } + } + DMatrixSparseCSC dst = new DMatrixSparseCSC(circuitMatrix.length, circuitMatrix.length, nonzero); + dst.nz_length = 0; + dst.col_idx[0] = 0; + int i, j; + for (i = 0; i != circuitMatrix.length; i++) { + for (j = 0; j != circuitMatrix.length; j++) { + double value = circuitMatrix[j][i]; + if (!(Math.abs(value) <= tol)) { + dst.nz_rows[dst.nz_length] = j; + dst.nz_values[dst.nz_length] = value; + ++dst.nz_length; + } + } + dst.col_idx[i + 1] = dst.nz_length; + } + + return dst; + } + + /** + * Value of an element in a sparse matrix + */ + class CoordinateRealValue { + /** The coordinate */ + public int row,col; + /** The value of the coordinate */ + public double value; + } + + public Iterator createCoordinateIterator() { + return new Iterator() { + final CoordinateRealValue coordinate = new CoordinateRealValue(); + int nz_index = 0; // the index of the non-zero value and row + int column = 0; // which column it's in + + { + incrementColumn(); + } + + + public boolean hasNext() { + return nz_index < nz_length; + } + + + public CoordinateRealValue next() { + coordinate.row = nz_rows[nz_index]; + coordinate.col = column; + coordinate.value = nz_values[nz_index]; + nz_index++; + incrementColumn(); + return coordinate; + } + + private void incrementColumn() { + while (column + 1 <= numCols && nz_index >= col_idx[column + 1]) { + column++; + } + } + }; + } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/matrix/IGrowArray.java b/src/main/java/com/lushprojects/circuitjs1/client/matrix/IGrowArray.java new file mode 100644 index 0000000..57deb06 --- /dev/null +++ b/src/main/java/com/lushprojects/circuitjs1/client/matrix/IGrowArray.java @@ -0,0 +1,75 @@ +package com.lushprojects.circuitjs1.client.matrix; + +public class IGrowArray { + public int[] data; + public int length; + + public IGrowArray( int length ) { + this.data = new int[length]; + this.length = length; + } + + public IGrowArray() { + this(0); + } + + public int length() { + return length; + } + + public void reshape( int length ) { + if (data.length < length) { + data = new int[length]; + } + this.length = length; + } + + /** + * Increases the internal array's length by the specified amount. Previous values are preserved. + * The length value is not modified since this does not change the 'meaning' of the array, just + * increases the amount of data which can be stored in it. + * + * this.data = new data_type[ data.length + amount ] + * + * @param amount Number of elements added to the internal array's length + */ + public void growInternal( int amount ) { + int[] tmp = new int[data.length + amount]; + + System.arraycopy(data, 0, tmp, 0, data.length); + this.data = tmp; + } + + public void setTo( IGrowArray original ) { + reshape(original.length); + System.arraycopy(original.data, 0, data, 0, original.length); + } + + public int get( int index ) { + if (index < 0 || index >= length) + throw new IllegalArgumentException("Out of bounds"); + return data[index]; + } + + public void set( int index, int value ) { + if (index < 0 || index >= length) + throw new IllegalArgumentException("Out of bounds"); + data[index] = value; + } + + public void add( int value ) { + if (length == data.length) { + growInternal(Math.min(10_000, 1 + data.length)); + } + data[length++] = value; + } + + public void clear() { + length = 0; + } + + public void free() { + data = new int[0]; + length = 0; + } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/matrix/SparseLU.java b/src/main/java/com/lushprojects/circuitjs1/client/matrix/SparseLU.java new file mode 100644 index 0000000..fadb513 --- /dev/null +++ b/src/main/java/com/lushprojects/circuitjs1/client/matrix/SparseLU.java @@ -0,0 +1,332 @@ +package com.lushprojects.circuitjs1.client.matrix; + + +import java.util.Arrays; + +public class SparseLU { + + + public SparseLU(){ + + } + + private final IGrowArray gw = new IGrowArray(); + private final DMatrixSparseCSC L = new DMatrixSparseCSC(0, 0, 0); + private final DMatrixSparseCSC U = new DMatrixSparseCSC(0, 0, 0); + private double[] x = new double[0]; + + private void initialize(DMatrixSparseCSC A) { + int m = A.numRows; + int n = A.numCols; + int o = Math.min(m, n); + this.L.reshape(m, m, 4 * A.nz_length + o); + this.L.nz_length = 0; + this.U.reshape(m, n, 4 * A.nz_length + o); + this.U.nz_length = 0; + this.singular = false; + if (this.pinv.length != m) { + this.pinv = new int[m]; + this.x = new double[m]; + } + + for(int i = 0; i < m; ++i) { + this.pinv[i] = -1; + this.L.col_idx[i] = 0; + } + + } + + private final IGrowArray gxi = new IGrowArray(); + private int[] pinv = new int[0]; + + private boolean singular; + private boolean performLU(DMatrixSparseCSC A) { + int m = A.numRows; + int n = A.numCols; + int[] q = null; + int[] w = adjust(this.gw, m * 2, m); + + int k; + for(k = 0; k < n; ++k) { + this.L.col_idx[k] = this.L.nz_length; + this.U.col_idx[k] = this.U.nz_length; + if (this.L.nz_length + n > this.L.nz_values.length) { + this.L.growMaxLength(2 * this.L.nz_values.length + n, true); + } + + if (this.U.nz_length + n > this.U.nz_values.length) { + this.U.growMaxLength(2 * this.U.nz_values.length + n, true); + } + + int col = q != null ? q[k] : k; + int top = solveColB(this.L, true, A, col, this.x, this.pinv, this.gxi, w); + int[] xi = this.gxi.data; + int ipiv = -1; + double a = -1.7976931348623157E308; + + for(int p = top; p < n; ++p) { + int i = xi[p]; + if (this.pinv[i] < 0) { + double t; + if ((t = Math.abs(this.x[i])) > a) { + a = t; + ipiv = i; + } + } else { + this.U.nz_rows[this.U.nz_length] = this.pinv[i]; + this.U.nz_values[this.U.nz_length++] = this.x[i]; + } + } + + if (ipiv == -1 || a <= 0.0) { + this.singular = true; + return false; + } + + double pivot = this.x[ipiv]; + this.U.nz_rows[this.U.nz_length] = k; + this.U.nz_values[this.U.nz_length++] = pivot; + this.pinv[ipiv] = k; + this.L.nz_rows[this.L.nz_length] = ipiv; + this.L.nz_values[this.L.nz_length++] = 1.0; + + for(int p = top; p < n; ++p) { + int i = xi[p]; + if (this.pinv[i] < 0) { + this.L.nz_rows[this.L.nz_length] = i; + this.L.nz_values[this.L.nz_length++] = this.x[i] / pivot; + } + + this.x[i] = 0.0; + } + } + + this.L.col_idx[n] = this.L.nz_length; + this.U.col_idx[n] = this.U.nz_length; + + for(k = 0; k < this.L.nz_length; ++k) { + this.L.nz_rows[k] = this.pinv[this.L.nz_rows[k]]; + } + + return true; + } + + int AnumRows; + int AnumCols; + + public boolean setA(DMatrixSparseCSC A) { + this.AnumRows = A.numRows; + this.AnumCols = A.numCols; + return this.decompose(A); + } + + public boolean decompose(DMatrixSparseCSC A) { + this.initialize(A); + return this.performLU(A); + } + + private final DGrowArray gx = new DGrowArray(); + private final DGrowArray gb = new DGrowArray(); + + public void solve(double[] B, double[] X) { + + if (B.length != this.AnumRows) { + int var10002 = B.length; + throw new IllegalArgumentException("Unexpected number of rows in B based on shape of A. Found=" + var10002 + " Expected=" + this.AnumRows); + } + + double[] x = adjust(this.gx, X.length); + double[] b = adjust(this.gb, B.length); + DMatrixSparseCSC L = this.L; + DMatrixSparseCSC U = this.U; + + for(int i = 0; i < B.length; i++) { + b[i] = B[i]; + } + + permuteInv(pinv, b, x, X.length); + solveL(L, x); + solveU(U, x); + + for(int i = 0; i < X.length; i++) { + X[i] = x[i]; + } + } + public static int solveColB(DMatrixSparseCSC G, boolean lower, DMatrixSparseCSC B, int colB, double[] x, int[] pinv, IGrowArray g_xi, int[] w) { + int X_rows = G.numCols; + int[] xi = adjust(g_xi, X_rows); + int top = searchNzRowsInX(G, B, colB, pinv, xi, w); + + int idxB0; + for(idxB0 = top; idxB0 < X_rows; ++idxB0) { + x[xi[idxB0]] = 0.0; + } + + idxB0 = B.col_idx[colB]; + int idxB1 = B.col_idx[colB + 1]; + + int px; + for(px = idxB0; px < idxB1; ++px) { + x[B.nz_rows[px]] = B.nz_values[px]; + } + + for(px = top; px < X_rows; ++px) { + int j = xi[px]; + int J = pinv != null ? pinv[j] : j; + if (J >= 0) { + int p; + int q; + if (lower) { + x[j] /= G.nz_values[G.col_idx[J]]; + p = G.col_idx[J] + 1; + q = G.col_idx[J + 1]; + } else { + x[j] /= G.nz_values[G.col_idx[J + 1] - 1]; + p = G.col_idx[J]; + q = G.col_idx[J + 1] - 1; + } + + while(p < q) { + int var10001 = G.nz_rows[p]; + x[var10001] -= G.nz_values[p] * x[j]; + ++p; + } + } + } + + return top; + } + + public static int searchNzRowsInX(DMatrixSparseCSC G, DMatrixSparseCSC B, int colB, int[] pinv, int[] xi, int[] w) { + int X_rows = G.numCols; + if (xi.length < X_rows) { + throw new IllegalArgumentException("xi must be at least G.numCols=" + G.numCols); + } else if (w.length < 2 * X_rows) { + throw new IllegalArgumentException("w must be at least 2*G.numCols in length (2*number of rows in X) and first N elements must be zero"); + } else { + int idx0 = B.col_idx[colB]; + int idx1 = B.col_idx[colB + 1]; + int top = X_rows; + + int i; + for(i = idx0; i < idx1; ++i) { + int rowB = B.nz_rows[i]; + if (rowB < X_rows && w[rowB] == 0) { + top = searchNzRowsInX_DFS(rowB, G, top, pinv, xi, w); + } + } + + for(i = top; i < X_rows; ++i) { + w[xi[i]] = 0; + } + + return top; + } + } + private static int searchNzRowsInX_DFS(int rowB, DMatrixSparseCSC G, int top, int[] pinv, int[] xi, int[] w) { + int N = G.numCols; + int head = 0; + xi[head] = rowB; + + while(head >= 0) { + int G_col = xi[head]; + int G_col_new = pinv != null ? pinv[G_col] : G_col; + if (w[G_col] == 0) { + w[G_col] = 1; + w[N + head] = G_col_new >= 0 && G_col_new < N ? G.col_idx[G_col_new] : 0; + } + + boolean done = true; + int idx0 = w[N + head]; + int idx1 = G_col_new >= 0 && G_col_new < N ? G.col_idx[G_col_new + 1] : 0; + + for(int j = idx0; j < idx1; ++j) { + int jrow = G.nz_rows[j]; + if (jrow < N && w[jrow] == 0) { + w[N + head] = j + 1; + ++head; + xi[head] = jrow; + done = false; + break; + } + } + + if (done) { + --head; + --top; + xi[top] = G_col; + } + } + + return top; + } + + public static void solveL(DMatrixSparseCSC L, double[] x) { + int N = L.numCols; + int idx0 = L.col_idx[0]; + + for(int col = 0; col < N; ++col) { + int idx1 = L.col_idx[col + 1]; + double x_j = x[col] /= L.nz_values[idx0]; + + for(int i = idx0 + 1; i < idx1; ++i) { + int row = L.nz_rows[i]; + x[row] -= L.nz_values[i] * x_j; + } + + idx0 = idx1; + } + + } + + public static void solveU(DMatrixSparseCSC U, double[] x) { + int N = U.numCols; + int idx1 = U.col_idx[N]; + + for(int col = N - 1; col >= 0; --col) { + int idx0 = U.col_idx[col]; + double x_j = x[col] /= U.nz_values[idx1 - 1]; + + for(int i = idx0; i < idx1 - 1; ++i) { + int row = U.nz_rows[i]; + x[row] -= U.nz_values[i] * x_j; + } + + idx1 = idx0; + } + + } + + + public static int[] adjust( IGrowArray gwork, int desired, int zeroToM) { + int[] w = adjust(gwork, desired); + Arrays.fill(w, 0, zeroToM, 0); + return w; + } + + public static int[] adjust( IGrowArray gwork, int desired) { + if (gwork == null) { + gwork = new IGrowArray(); + } + + gwork.reshape(desired); + return gwork.data; + } + + + public static double[] adjust(DGrowArray gwork, int desired) { + if (gwork == null) { + gwork = new DGrowArray(); + } + + gwork.reshape(desired); + return gwork.data; + } + + public static void permuteInv(int[] perm, double[] input, double[] output, int N) { + for(int k = 0; k < N; ++k) { + output[perm[k]] = input[k]; + } + + } +} From a5488ed9959291e39cb516afe5637fda2f3c9a17 Mon Sep 17 00:00:00 2001 From: 11 <11> Date: Fri, 29 Dec 2023 22:34:46 +0800 Subject: [PATCH 2/5] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=94=A8IDEA=E6=89=93?= =?UTF-8?q?=E5=BC=80=E9=A1=B9=E7=9B=AE=E5=8F=AF=E8=83=BD=E6=97=A0=E6=B3=95?= =?UTF-8?q?=E6=88=90=E5=8A=9F=E5=8A=A0=E8=BD=BD=E5=86=85=E7=BD=AE=E7=94=B5?= =?UTF-8?q?=E8=B7=AF=E9=97=AE=E9=A2=98=20=E7=AE=80=E5=8C=96LU=E5=88=86?= =?UTF-8?q?=E8=A7=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../circuitjs1/client/CirSim.java | 990 ++++++++---------- .../circuitjs1/client/matrix/SparseLU.java | 10 +- 2 files changed, 439 insertions(+), 561 deletions(-) diff --git a/src/main/java/com/lushprojects/circuitjs1/client/CirSim.java b/src/main/java/com/lushprojects/circuitjs1/client/CirSim.java index 17b700c..4080f4f 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/CirSim.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/CirSim.java @@ -731,7 +731,7 @@ else if (usRes) loadShortcuts(); DOM.appendChild(layoutPanel.getElement(), topPanelCheckbox); - DOM.appendChild(layoutPanel.getElement(), topPanelCheckboxLabel); + DOM.appendChild(layoutPanel.getElement(), topPanelCheckboxLabel); if (!hideMenu) layoutPanel.addNorth(menuBar, MENUBARHEIGHT); @@ -863,7 +863,7 @@ public void onClick(ClickEvent event) { elmMenuBar.setAutoOpen(true); selectScopeMenuBar = new MenuBar(true) { @Override - + // when mousing over scope menu item, select associated scope public void onBrowserEvent(Event event) { int currentItem = -1; @@ -877,16 +877,16 @@ public void onBrowserEvent(Event event) { } switch (DOM.eventGetType(event)) { case Event.ONMOUSEOVER: - scopeMenuSelected = currentItem; - break; + scopeMenuSelected = currentItem; + break; case Event.ONMOUSEOUT: scopeMenuSelected = -1; - break; + break; } super.onBrowserEvent(event); } }; - + elmMenuBar.addItem(elmEditMenuItem = new MenuItem(Locale.LS("Edit..."),new MyCommand("elm","edit"))); elmMenuBar.addItem(elmScopeMenuItem = new MenuItem(Locale.LS("View in New Scope"), new MyCommand("elm","viewInScope"))); elmMenuBar.addItem(elmFloatScopeMenuItem = new MenuItem(Locale.LS("View in New Undocked Scope"), new MyCommand("elm","viewInFloatScope"))); @@ -937,12 +937,12 @@ public void onBrowserEvent(Event event) { cv.addClickHandler(this); cv.addDoubleClickHandler(this); doTouchHandlers(this, cv.getCanvasElement()); - cv.addDomHandler(this, ContextMenuEvent.getType()); + cv.addDomHandler(this, ContextMenuEvent.getType()); menuBar.addDomHandler(new ClickHandler() { public void onClick(ClickEvent event) { doMainMenuChecks(); } - }, ClickEvent.getType()); + }, ClickEvent.getType()); Event.addNativePreviewHandler(this); cv.addMouseWheelHandler(this); @@ -954,7 +954,7 @@ public void onWindowClosing(ClosingEvent event) { } }); setupJSInterface(); - + setSimRunning(running); } @@ -972,12 +972,12 @@ void setColors(String positiveColor, String negativeColor, String neutralColor, if (currentColor == null) currentColor = stor.getItem("currentColor"); } - + if (positiveColor != null) CircuitElm.positiveColor = new Color(URL.decodeQueryString(positiveColor)); else if (getOptionFromStorage("alternativeColor", false)) CircuitElm.positiveColor = Color.blue; - + if (negativeColor != null) CircuitElm.negativeColor = new Color(URL.decodeQueryString(negativeColor)); if (neutralColor != null) @@ -987,15 +987,15 @@ else if (getOptionFromStorage("alternativeColor", false)) CircuitElm.selectColor = new Color(URL.decodeQueryString(selectColor)); else CircuitElm.selectColor = Color.cyan; - + if (currentColor != null) CircuitElm.currentColor = new Color(URL.decodeQueryString(currentColor)); else CircuitElm.currentColor = conventionCheckItem.getState() ? Color.yellow : Color.cyan; - + CircuitElm.setColorScale(); } - + MenuItem menuItemWithShortcut(String icon, String text, String shortcut, MyCommand cmd) { final String edithtml="
" + nbsp + Locale.LS(text) + "
" + shortcut + "
"; return new MenuItem(SafeHtmlUtils.fromTrustedString(sn), cmd); } - + MenuItem iconMenuItem(String icon, String text, Command cmd) { String icoStr = " " + Locale.LS(text); //  return new MenuItem(SafeHtmlUtils.fromTrustedString(icoStr), cmd); } - + boolean getOptionFromStorage(String key, boolean val) { Storage stor = Storage.getLocalStorageIfSupported(); if (stor == null) @@ -1025,7 +1025,7 @@ void setOptionInStorage(String key, boolean val) { return; stor.setItem(key, val ? "true" : "false"); } - + // save shortcuts to local storage void saveShortcuts() { Storage stor = Storage.getLocalStorageIfSupported(); @@ -1042,7 +1042,7 @@ void saveShortcuts() { } stor.setItem("shortcuts", str); } - + // load shortcuts from local storage void loadShortcuts() { Storage stor = Storage.getLocalStorageIfSupported(); @@ -1052,12 +1052,12 @@ void loadShortcuts() { if (str == null) return; String keys[] = str.split(";"); - + // clear existing shortcuts int i; for (i = 0; i != shortcuts.length; i++) shortcuts[i] = null; - + // clear shortcuts from menu for (i = 0; i != mainMenuItems.size(); i++) { CheckboxMenuItem item = mainMenuItems.get(i); @@ -1066,7 +1066,7 @@ void loadShortcuts() { break; item.setShortcut(""); } - + // go through keys (skipping version at start) for (i = 1; i < keys.length; i++) { String arr[] = keys[i].split("="); @@ -1075,7 +1075,7 @@ void loadShortcuts() { int c = Integer.parseInt(arr[0]); String className = arr[1]; shortcuts[c] = className; - + // find menu item and fix it int j; for (j = 0; j != mainMenuItems.size(); j++) { @@ -1087,7 +1087,7 @@ void loadShortcuts() { } } } - + // install touch handlers // don't feel like rewriting this in java. Anyway, java doesn't let us create mouse // events and dispatch them. @@ -1096,16 +1096,16 @@ native static void doTouchHandlers(CirSim sim, CanvasElement cv) /*-{ var lastTap; var tmout; var lastScale; - + cv.addEventListener("touchstart", function (e) { mousePos = getTouchPos(cv, e); var touch = e.touches[0]; - + var etype = "mousedown"; lastScale = 1; clearTimeout(tmout); e.preventDefault(); - + if (e.timeStamp-lastTap < 300) { etype = "dblclick"; } else { @@ -1114,7 +1114,7 @@ native static void doTouchHandlers(CirSim sim, CanvasElement cv) /*-{ }, 500); } lastTap = e.timeStamp; - + var touch1 = e.touches[0]; var touch2 = e.touches[e.touches.length-1]; lastScale = Math.hypot(touch1.clientX-touch2.clientX, touch1.clientY-touch2.clientY); @@ -1157,11 +1157,11 @@ function getTouchPos(canvasDom, touchEvent) { y: touchEvent.touches[0].clientY - rect.top }; } - + }-*/; - + boolean shown = false; - + // this is called twice, once for the Draw menu, once for the right mouse popup menu public void composeMainMenu(MenuBar mainMenuBar, int num) { mainMenuBar.addItem(getClassCheckItem(Locale.LS("Add Wire"), "WireElm")); @@ -1209,7 +1209,7 @@ public void composeMainMenu(MenuBar mainMenuBar, int num) { inputMenuBar.addItem(getClassCheckItem(Locale.LS("Add External Voltage (JavaScript)"), "ExtVoltageElm")); mainMenuBar.addItem(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml+Locale.LS(" Inputs and Sources")), inputMenuBar); - + MenuBar outputMenuBar = new MenuBar(true); outputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Analog Output"), "OutputElm")); outputMenuBar.addItem(getClassCheckItem(Locale.LS("Add LED"), "LEDElm")); @@ -1230,7 +1230,7 @@ public void composeMainMenu(MenuBar mainMenuBar, int num) { outputMenuBar.addItem(getClassCheckItem(Locale.LS("Add DC Motor"), "DCMotorElm")); outputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Wattmeter"), "WattmeterElm")); mainMenuBar.addItem(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml+Locale.LS(" Outputs and Labels")), outputMenuBar); - + MenuBar activeMenuBar = new MenuBar(true); activeMenuBar.addItem(getClassCheckItem(Locale.LS("Add Diode"), "DiodeElm")); activeMenuBar.addItem(getClassCheckItem(Locale.LS("Add Zener Diode"), "ZenerElm")); @@ -1275,7 +1275,7 @@ public void composeMainMenu(MenuBar mainMenuBar, int num) { activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add TL431"), "CustomCompositeElm:~TL431")); activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add Subcircuit Instance"), "CustomCompositeElm")); mainMenuBar.addItem(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml+Locale.LS(" Active Building Blocks")), activeBlocMenuBar); - + MenuBar gateMenuBar = new MenuBar(true); gateMenuBar.addItem(getClassCheckItem(Locale.LS("Add Logic Input"), "LogicInputElm")); gateMenuBar.addItem(getClassCheckItem(Locale.LS("Add Logic Output"), "LogicOutputElm")); @@ -1307,7 +1307,7 @@ public void composeMainMenu(MenuBar mainMenuBar, int num) { chipMenuBar.addItem(getClassCheckItem(Locale.LS("Add Custom Logic"), "UserDefinedLogicElm")); // don't change this, it will break people's saved shortcuts chipMenuBar.addItem(getClassCheckItem(Locale.LS("Add Static RAM"), "SRAMElm")); mainMenuBar.addItem(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml+Locale.LS(" Digital Chips")), chipMenuBar); - + MenuBar achipMenuBar = new MenuBar(true); achipMenuBar.addItem(getClassCheckItem(Locale.LS("Add 555 Timer"), "TimerElm")); achipMenuBar.addItem(getClassCheckItem(Locale.LS("Add Phase Comparator"), "PhaseCompElm")); @@ -1316,12 +1316,12 @@ public void composeMainMenu(MenuBar mainMenuBar, int num) { achipMenuBar.addItem(getClassCheckItem(Locale.LS("Add VCO"), "VCOElm")); achipMenuBar.addItem(getClassCheckItem(Locale.LS("Add Monostable"), "MonostableElm")); mainMenuBar.addItem(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml+Locale.LS(" Analog and Hybrid Chips")), achipMenuBar); - + if (subcircuitMenuBar == null) subcircuitMenuBar = new MenuBar[2]; subcircuitMenuBar[num] = new MenuBar(true); mainMenuBar.addItem(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml+Locale.LS(" Subcircuits")), subcircuitMenuBar[num]); - + MenuBar otherMenuBar = new MenuBar(true); CheckboxMenuItem mi; otherMenuBar.addItem(mi=getClassCheckItem(Locale.LS("Drag All"), "DragAll")); @@ -1339,12 +1339,12 @@ public void composeMainMenu(MenuBar mainMenuBar, int num) { mainMenuBar.addItem(mi=getClassCheckItem(Locale.LS("Select/Drag Sel"), "Select")); mi.setShortcut(Locale.LS("(space or Shift-drag)")); } - + void composeSubcircuitMenu() { if (subcircuitMenuBar == null) return; int mi; - + // there are two menus to update: the one in the Draw menu, and the one in the right mouse menu for (mi = 0; mi != 2; mi++) { MenuBar menu = subcircuitMenuBar[mi]; @@ -1358,7 +1358,7 @@ void composeSubcircuitMenu() { } lastSubcircuitMenuUpdate = CustomCompositeModel.sequenceNumber; } - + public void composeSelectScopeMenu(MenuBar sb) { sb.clearItems(); selectScopeMenuItems = new Vector(); @@ -1382,7 +1382,7 @@ public void composeSelectScopeMenu(MenuBar sb) { for (MenuItem mi : selectScopeMenuItems) sb.addItem(mi); } - + public void setSlidersPanelHeight() { int i; int cumheight=0; @@ -1398,10 +1398,8 @@ public void setSlidersPanelHeight() { ih=0; slidersPanel.setHeight(ih+"px"); } - - CheckboxMenuItem getClassCheckItem(String s, String t) { @@ -1437,20 +1435,18 @@ CheckboxMenuItem getClassCheckItem(String s, String t) { mainMenuItemNames.add(t); return mi; } - - void centreCircuit() { if (elmList == null) // avoid exception if called during initialization return; - + Rectangle bounds = getCircuitBounds(); setCircuitArea(); - + double scale = 1; int cheight = circuitArea.height; - + // if there's no scope, and the window isn't very wide, then don't use all of the circuit area when // centering, because the info in the corner might not get in the way. We still want circuitArea to be the full // height though, to allow the user to put stuff there manually. @@ -1458,7 +1454,7 @@ void centreCircuit() { int h = (int) ((double)cheight * scopeHeightFraction); cheight -= h; } - + if (bounds != null) // add some space on edges because bounds calculation is not perfect scale = Math.min(circuitArea.width /(double)(bounds.width+140), @@ -1501,7 +1497,7 @@ Rectangle getCircuitBounds() { int framerate = 0, steprate = 0; static CirSim theSim; - + public void setSimRunning(boolean s) { if (s) { if (stopMessage != null) @@ -1518,13 +1514,13 @@ public void setSimRunning(boolean s) { repaint(); } } - + public boolean simIsRunning() { return simRunning; } - + boolean needsRepaint; - + void repaint() { if (!needsRepaint) { needsRepaint = true; @@ -1537,16 +1533,16 @@ public boolean execute() { }, FASTTIMER); } } - + // ***************************************************************** // UPDATE CIRCUIT - + public void updateCircuit() { PerfMonitor perfmon = new PerfMonitor(); perfmon.startContext("updateCircuit()"); checkCanvasSize(); - + // Analyze circuit boolean didAnalyze = analyzeFlag; if (analyzeFlag || dcAnalysisFlag) { @@ -1555,7 +1551,7 @@ public void updateCircuit() { analyzeFlag = false; perfmon.stopContext(); } - + // Stamp circuit if (needsStamp && simRunning) { perfmon.startContext("stampCircuit()"); @@ -1566,10 +1562,10 @@ public void updateCircuit() { } perfmon.stopContext(); } - + if (stopElm != null && stopElm != mouseElm) stopElm.setMouseElm(true); - + setupScopes(); Graphics g = new Graphics(cvcontext); @@ -1595,7 +1591,7 @@ public void updateCircuit() { console("needsStamp while simRunning?"); perfmon.startContext("runCircuit()"); - try { + try { runCircuit(didAnalyze); } catch (Exception e) { debugger(); @@ -1638,9 +1634,9 @@ public void updateCircuit() { if (noEditCheckItem.getState()) g.drawLock(20, 30); - + g.setColor(Color.white); - + // Set the graphics transform to deal with zoom and offset double scale = devicePixelRatio(); cvcontext.setTransform(transform[0] * scale, 0, 0, transform[3] * scale, transform[4] * scale, transform[5] * scale); @@ -1650,7 +1646,7 @@ public void updateCircuit() { for (int i = 0; i != elmList.size(); i++) { if (powerCheckItem.getState()) g.setColor(Color.gray); - + getElm(i).draw(g); } perfmon.stopContext(); @@ -1663,9 +1659,9 @@ public void updateCircuit() { // for some mouse modes, what matters is not the posts but the endpoints (which // are only the same for 2-terminal elements). We draw those now if needed - if (tempMouseMode == MODE_DRAG_ROW || - tempMouseMode == MODE_DRAG_COLUMN || - tempMouseMode == MODE_DRAG_POST || + if (tempMouseMode == MODE_DRAG_ROW || + tempMouseMode == MODE_DRAG_COLUMN || + tempMouseMode == MODE_DRAG_POST || tempMouseMode == MODE_DRAG_SELECTED) { for (int i = 0; i != elmList.size(); i++) { @@ -1681,7 +1677,7 @@ public void updateCircuit() { } } } - + // draw handles for elm we're creating if (tempMouseMode == MODE_SELECT && mouseElm != null) { mouseElm.drawHandles(g, CircuitElm.selectColor); @@ -1725,12 +1721,12 @@ public void updateCircuit() { perfmon.stopContext(); g.setColor(Color.white); - + perfmon.stopContext(); // graphics - + if (stopElm != null && stopElm != mouseElm) stopElm.setMouseElm(false); - + frames++; // if we did DC analysis, we need to re-analyze the circuit with that flag @@ -1743,7 +1739,7 @@ public void updateCircuit() { lastFrameTime = lastTime; perfmon.stopContext(); // updateCircuit - + if (developerMode) { int height = 15; int increment = 15; @@ -1752,17 +1748,17 @@ public void updateCircuit() { g.drawString("Steprate/iter: " + CircuitElm.showFormat.format(steprate / getIterCount()), 10, height += increment); g.drawString("iterc: " + CircuitElm.showFormat.format(getIterCount()), 10, height += increment); g.drawString("Frames: " + frames, 10, height += increment); - + height += (increment * 2); - + String perfmonResult = PerfMonitor.buildString(perfmon).toString(); String[] splits = perfmonResult.split("\n"); for (int x = 0; x < splits.length; x++) { g.drawString(splits[x], 10, height + (increment * x)); } } - - // This should always be the last + + // This should always be the last // thing called by updateCircuit(); callUpdateHook(); } @@ -1822,7 +1818,7 @@ void drawBottomArea(Graphics g) { // if (mouseElm.getVoltageSourceCount() > 0) // info[0] += ";" + (mouseElm.getVoltageSource()+nodeList.size()); // */ - + } else { info[0] = "t = " + CircuitElm.getTimeText(t); double timerate = 160*getIterCount()*timeStep; @@ -1844,7 +1840,7 @@ void drawBottomArea(Graphics g) { x = scopes[ct-1].rightEdge() + 20; // x = max(x, canvasWidth*2/3); // x=cv.getCoordinateSpaceWidth()*2/3; - + // count lines of data for (i = 0; info[i] != null; i++) ; @@ -1860,23 +1856,23 @@ void drawBottomArea(Graphics g) { g.drawString(info[i], x, ybase+15*(i+1)); } } - + Color getBackgroundColor() { if (printableCheckItem.getState()) return Color.white; return Color.black; } - + int oldScopeCount = -1; - + boolean scopeMenuIsSelected(Scope s) { if (scopeMenuSelected < 0) return false; if (scopeMenuSelected < scopeCount) return scopes[scopeMenuSelected] == s; - return getNthScopeElm(scopeMenuSelected-scopeCount).elmScope == s; + return getNthScopeElm(scopeMenuSelected-scopeCount).elmScope == s; } - + native boolean isSidePanelCheckboxChecked() /*-{ return $doc.getElementById("trigger").checked; }-*/; @@ -1947,7 +1943,7 @@ void setupScopes() { oldScopeCount = scopeCount; } } - + String getHint() { CircuitElm c1 = getElm(hintItem1); CircuitElm c2 = getElm(hintItem2); @@ -2021,12 +2017,12 @@ String getHint() { // } // } // } - + void needAnalyze() { analyzeFlag = true; repaint(); } - + Vector nodeList; Vector postDrawList = new Vector(); Vector badConnectionList = new Vector(); @@ -2043,7 +2039,7 @@ public CircuitElm getElm(int n) { return null; return elmList.elementAt(n); } - + public Adjustable findAdjustable(CircuitElm elm, int item) { int i; for (i = 0; i != adjustables.size(); i++) { @@ -2053,14 +2049,14 @@ public Adjustable findAdjustable(CircuitElm elm, int item) { } return null; } - + public static native void console(String text) /*-{ console.log(text); }-*/; public static native void debugger() /*-{ debugger; }-*/; - + class NodeMapEntry { int node; NodeMapEntry() { node = -1; } @@ -2069,7 +2065,7 @@ class NodeMapEntry { // map points to node numbers HashMap nodeMap; HashMap postCountMap; - + class WireInfo { CircuitElm wire; Vector neighbors; @@ -2078,10 +2074,10 @@ class WireInfo { wire = w; } } - + // info about each wire and its neighbors, used to calculate wire currents Vector wireInfoList; - + // find groups of nodes connected by wire equivalents and map them to the same node. this speeds things // up considerably by reducing the size of the matrix. We do this for wires, labeled nodes, and ground. // The actual node we map to is not assigned yet. Instead we map to the same NodeMapEntry. @@ -2100,7 +2096,7 @@ void calculateWireClosure() { wireInfoList.add(new WireInfo(ce)); Point p0 = ce.getPost(0); NodeMapEntry cn = nodeMap.get(p0); - + // what post are we connected to Point p1 = ce.getConnectedPost(); if (p1 == null) { @@ -2134,10 +2130,10 @@ void calculateWireClosure() { nodeMap.put(p0, cn); nodeMap.put(p1, cn); } - + // console("got " + (groupCount-mergeCount) + " groups with " + nodeMap.size() + " nodes " + mergeCount); } - + // generate info we need to calculate wire currents. Most other elements calculate currents using // the voltage on their terminal nodes. But wires have the same voltage at both ends, so we need // to use the neighbors' currents instead. We used to treat wires as zero voltage sources to make @@ -2150,7 +2146,7 @@ void calculateWireClosure() { boolean calcWireInfo() { int i; int moved = 0; - + for (i = 0; i != wireInfoList.size(); i++) { WireInfo wi = wireInfoList.get(i); CircuitElm wire = wi.wire; @@ -2159,7 +2155,7 @@ boolean calcWireInfo() { Vector neighbors0 = new Vector(); Vector neighbors1 = new Vector(); - + // assume each end is ready (except ground nodes which have one end) // labeled nodes are treated as having 2 terminals, see below boolean isReady0 = true, isReady1 = !(wire instanceof GroundElm); @@ -2172,18 +2168,18 @@ boolean calcWireInfo() { if (ce == wire) continue; Point pt = ce.getPost(cnl.num); - + // is this a wire that doesn't have wire info yet? If so we can't use it yet. // That would create a circular dependency. So that side isn't ready. boolean notReady = (ce.isRemovableWire() && !ce.hasWireInfo); - + // which post does this element connect to, if any? if (pt.x == wire.x && pt.y == wire.y) { neighbors0.add(ce); if (notReady) isReady0 = false; } else if (wire.getPostCount() > 1) { Point p2 = wire.getConnectedPost(); - if (pt.x == p2.x && pt.y == p2.y) { + if (pt.x == p2.x && pt.y == p2.y) { neighbors1.add(ce); if (notReady) isReady1 = false; } @@ -2216,7 +2212,7 @@ boolean calcWireInfo() { } } } - + return true; } @@ -2226,14 +2222,14 @@ void setGroundNode() { 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; - + // set ground node to 0 NodeMapEntry nme = nodeMap.get(ce.getPost(0)); nme.node = 0; @@ -2274,18 +2270,18 @@ void makeNodeList() { 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); Integer g = postCountMap.get(pt); postCountMap.put(pt, g == null ? 1 : g+1); NodeMapEntry cln = nodeMap.get(pt); - + // is this node not in map yet? or is the node number unallocated? // (we don't allocate nodes before this because changing the allocation order // of nodes changes circuit behavior and breaks backward compatibility; - // the code below to connect unconnected nodes may connect a different node to ground) + // the code below to connect unconnected nodes may connect a different node to ground) if (cln == null || cln.node == -1) { CircuitNode cn = new CircuitNode(); CircuitNodeLink cnl = new CircuitNodeLink(); @@ -2321,21 +2317,21 @@ void makeNodeList() { ce.setNode(cnl.num, nodeList.size()); nodeList.addElement(cn); } - + // also count voltage sources so we can allocate array vscount += ivs; } - + voltageSources = new CircuitElm[vscount]; } - + Vector unconnectedNodes; Vector nodesWithGroundConnection; int nodesWithGroundConnectionCount; - + void findUnconnectedNodes() { int i, j; - + // determine nodes that are not connected indirectly to ground. // all nodes must be connected to ground somehow, or else we // will get a matrix error. @@ -2391,7 +2387,7 @@ void findUnconnectedNodes() { } } } - + // take list of unconnected nodes, which we identified earlier, and connect them to ground // with a big resistor. otherwise we will get matrix errors. The resistor has to be big, // otherwise circuits like 555 Square Wave will break @@ -2402,10 +2398,10 @@ void connectUnconnectedNodes() { stampResistor(0, n, 1e8); } } - + boolean validateCircuit() { int i, j; - + for (i = 0; i != elmList.size(); i++) { CircuitElm ce = getElm(i); // look for inductors with no current path @@ -2433,7 +2429,7 @@ boolean validateCircuit() { } else cur.broken = false; } - + // look for voltage source or wire loops. we do this for voltage sources if (ce.getPostCount() == 2) { if (ce instanceof VoltageElm) { @@ -2454,7 +2450,7 @@ boolean validateCircuit() { return false; } } - + // look for shorted caps, or caps w/ voltage but no R if (ce instanceof CapacitorElm) { FindPathInfo fpi = new FindPathInfo(FindPathInfo.SHORT, ce, @@ -2478,7 +2474,7 @@ boolean validateCircuit() { } return true; } - + // analyze the circuit when something changes, so it can be simulated void analyzeCircuit() { stopMessage = null; @@ -2497,12 +2493,12 @@ void analyzeCircuit() { // allocate nodes and voltage sources makeNodeList(); - + makePostDrawList(); if (!calcWireInfo()) return; nodeMap = null; // done with this - + int vscount = 0; circuitNonLinear = false; @@ -2536,14 +2532,14 @@ void analyzeCircuit() { findUnconnectedNodes(); if (!validateCircuit()) return; - + nodesWithGroundConnectionCount = nodesWithGroundConnection.size(); // only need this for validation nodesWithGroundConnection = null; - + timeStep = maxTimeStep; needsStamp = true; - + callAnalyzeHook(); } @@ -2564,7 +2560,7 @@ void stampCircuit() { for (i = 0; i != matrixSize; i++) circuitRowInfo[i] = new RowInfo(); circuitNeedsMap = false; - + connectUnconnectedNodes(); // stamp linear circuit elements @@ -2576,11 +2572,11 @@ void stampCircuit() { if (!simplifyMatrix(matrixSize)) return; - + // check if we called stop() if (circuitMatrix == null) return; - + // if a matrix is linear, we can do the lu_factor here instead of // needing to do it every frame if (!circuitNonLinear) { @@ -2589,7 +2585,7 @@ void stampCircuit() { return; } } - + // copy elmList to an array to avoid a bunch of calls to canCast() when doing simulation elmArr = new CircuitElm[elmList.size()]; int scopeElmCount = 0; @@ -2598,14 +2594,14 @@ void stampCircuit() { if (elmArr[i] instanceof ScopeElm) scopeElmCount++; } - + // copy ScopeElms to an array to avoid a second pass over entire list of elms during simulation scopeElmArr = new ScopeElm[scopeElmCount]; int j = 0; for (i = 0; i != elmList.size(); i++) { if (elmArr[i] instanceof ScopeElm) scopeElmArr[j++] = (ScopeElm) elmArr[i]; - } + } needsStamp = false; } @@ -2620,9 +2616,9 @@ boolean simplifyMatrix(int matrixSize) { RowInfo re = circuitRowInfo[i]; /*System.out.println("row " + i + " " + re.lsChanges + " " + re.rsChanges + " " + re.dropRow);*/ - + // if (qp != -100) continue; // uncomment this line to disable matrix simplification for debugging purposes - + if (re.lsChanges || re.dropRow || re.rsChanges) continue; double rsadd = 0; @@ -2713,7 +2709,7 @@ boolean simplifyMatrix(int matrixSize) { } // console("old size = " + matrixSize + " new size = " + newsize); - + circuitMatrix = newmatx; circuitRightSide = newrs; matrixSize = circuitMatrixSize = newsize; @@ -2725,7 +2721,7 @@ boolean simplifyMatrix(int matrixSize) { circuitNeedsMap = true; return true; } - + // make list of posts we need to draw. posts shared by 2 elements should be hidden, all // others should be drawn. We can't use the node list for this purpose anymore because wires // have the same node number at both ends. @@ -2735,7 +2731,7 @@ void makePostDrawList() { for (Map.Entry entry : postCountMap.entrySet()) { if (entry.getValue() != 2) postDrawList.add(entry.getKey()); - + // look for bad connections, posts not connected to other elements which intersect // other elements' bounding boxes if (entry.getValue() == 1) { @@ -2812,7 +2808,7 @@ boolean findPath(int n1) { } return false; } - + boolean checkElm(int n1, CircuitElm ce) { if (ce == firstElm) return false; @@ -2878,7 +2874,7 @@ void stop(String s, CircuitElm ce) { 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) { @@ -2886,7 +2882,7 @@ void stampVCVS(int n1, int n2, double coef, int 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; @@ -2906,13 +2902,13 @@ void stampVoltageSource(int n1, int n2, int vs) { stampMatrix(n1, vn, 1); stampMatrix(n2, vn, -1); } - + // update voltage source in doStep() 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)) { @@ -2997,7 +2993,7 @@ void stampRightSide(int i) { if (i > 0) circuitRowInfo[i-1].rsChanges = true; } - + // indicate that the values on the left side of row i change in doStep() void stampNonLinear(int i) { if (i > 0) @@ -3025,10 +3021,10 @@ boolean canDelayWireProcessing() { return false; return true; } - + boolean converged; int subIterations; - + void runCircuit(boolean didAnalyze) { if (circuitMatrix == null || elmList.size() == 0) { circuitMatrix = null; @@ -3045,19 +3041,19 @@ void runCircuit(boolean didAnalyze) { lastIterTime = tm; return; } - + // Check if we don't need to run simulation (for very slow simulation speeds). // If the circuit changed, do at least one iteration to make sure everything is consistent. if (1000 >= steprate*(tm-lastIterTime) && !didAnalyze) return; - + boolean delayWireProcessing = canDelayWireProcessing(); - + int timeStepCountAtFrameStart = timeStepCount; - + // keep track of iterations completed without convergence issues int goodIterations = 100; - + for (iter = 1; ; iter++) { if (goodIterations >= 3 && timeStep < maxTimeStep) { // things are going well, double the time step @@ -3066,7 +3062,7 @@ void runCircuit(boolean didAnalyze) { stampCircuit(); goodIterations = 0; } - + int i, j, subiter; for (i = 0; i != elmArr.length; i++) elmArr[i].startIteration(); @@ -3091,7 +3087,7 @@ void runCircuit(boolean didAnalyze) { boolean printit = debugprint; debugprint = false; if (circuitMatrixSize < 8) { - // we only need this for debugging purposes, so skip it for large matrices + // we only need this for debugging purposes, so skip it for large matrices for (j = 0; j != circuitMatrixSize; j++) { for (i = 0; i != circuitMatrixSize; i++) { double x = circuitMatrix[i][j]; @@ -3171,7 +3167,7 @@ void runCircuit(boolean didAnalyze) { for (i = 0; i != lastNodeVoltages.length; i++) lastNodeVoltages[i] = nodeVoltages[i]; // console("set lastrightside at " + t + " " + lastNodeVoltages); - + tm = System.currentTimeMillis(); lit = tm; // Check whether enough time has elapsed to perform an *additional* iteration after @@ -3209,10 +3205,10 @@ void applySolvedRightSide(double rs[]) { voltageSources[ji].setCurrent(ji, res); } } - + setNodeVoltages(nodeVoltages); } - + // set node voltages in each element given an array of node voltages void setNodeVoltages(double nv[]) { int j, k; @@ -3225,16 +3221,16 @@ void setNodeVoltages(double nv[]) { } } } - + // we removed wires from the matrix to speed things up. in order to display wire currents, // we need to calculate them now. void calcWireCurrents() { int i; - + // for debugging //for (i = 0; i != wireInfoList.size(); i++) // wireInfoList.get(i).wire.setCurrent(-1, 1.23); - + for (i = 0; i != wireInfoList.size(); i++) { WireInfo wi = wireInfoList.get(i); double cur = 0; @@ -3253,10 +3249,10 @@ void calcWireCurrents() { wi.wire.setCurrent(-1, -cur); } } - + int min(int a, int b) { return (a < b) ? a : b; } int max(int a, int b) { return (a > b) ? a : b; } - + public void resetAction(){ int i; analyzeFlag = true; @@ -3270,7 +3266,7 @@ public void resetAction(){ scopes[i].resetGraph(true); repaint(); } - + static void electronSaveAsCallback(String s) { s = s.substring(s.lastIndexOf('/')+1); s = s.substring(s.lastIndexOf('\\')+1); @@ -3284,7 +3280,7 @@ static void electronSaveCallback() { theSim.savedFlag = true; theSim.repaint(); } - + static native void electronSaveAs(String dump) /*-{ $wnd.showSaveDialog().then(function (file) { if (file.canceled) @@ -3298,35 +3294,35 @@ static native void electronSave(String dump) /*-{ $wnd.saveFile(null, dump); @com.lushprojects.circuitjs1.client.CirSim::electronSaveCallback()(); }-*/; - + static void electronOpenFileCallback(String text, String name) { LoadFile.doLoadCallback(text, name); theSim.allowSave(true); } - + static native void electronOpenFile() /*-{ $wnd.openFile(function (text, name) { @com.lushprojects.circuitjs1.client.CirSim::electronOpenFileCallback(Ljava/lang/String;Ljava/lang/String;)(text, name); }); }-*/; - + static native void toggleDevTools() /*-{ $wnd.toggleDevTools(); }-*/; - + static native boolean isElectron() /*-{ return ($wnd.openFile != undefined); - }-*/; + }-*/; static native String getElectronStartCircuitText() /*-{ return $wnd.startCircuitText; - }-*/; - + }-*/; + void allowSave(boolean b) { if (saveFileItem != null) saveFileItem.setEnabled(b); } - + public void menuPerformed(String menu, String item) { if ((menu=="edit" || menu=="main" || menu=="scopes") && noEditCheckItem.getState()) { Window.alert(Locale.LS("Editing disabled. Re-enable from the Options menu.")); @@ -3401,13 +3397,13 @@ public void menuPerformed(String menu, String item) { doUndo(); if (item=="redo") doRedo(); - + // if the mouse is hovering over an element, and a shortcut key is pressed, operate on that element (treat it like a context menu item selection) if (menu == "key" && mouseElm != null) { menuElm = mouseElm; menu = "elm"; } - + if (item == "cut") { if (menu!="elm") menuElm = null; @@ -3435,7 +3431,7 @@ public void menuPerformed(String menu, String item) { // destroyFrame(); // return; // } - + if (item=="centrecircuit") { pushUndo(); centreCircuit(); @@ -3482,16 +3478,16 @@ public void menuPerformed(String menu, String item) { if (i > 0) scopes[i].speed = scopes[i-1].speed; } - + if (item=="viewInFloatScope" && menuElm != null) { ScopeElm newScope = new ScopeElm(snapGrid(menuElm.x+50), snapGrid(menuElm.y+50)); elmList.addElement(newScope); newScope.setScopeElm(menuElm); - + // need to rebuild scopeElmArr needAnalyze(); } - + if (item.startsWith("addToScope") && menuElm != null) { int n; n = Integer.parseInt(item.substring(10)); @@ -3503,7 +3499,7 @@ public void menuPerformed(String menu, String item) { } scopeMenuSelected = -1; } - + if (menu=="scopepop") { pushUndo(); Scope s; @@ -3525,7 +3521,7 @@ public void menuPerformed(String menu, String item) { ScopeElm newScope = new ScopeElm(snapGrid(menuElm.x+50), snapGrid(menuElm.y+50)); elmList.addElement(newScope); newScope.setElmScope(scopes[menuScope]); - + int i; // remove scope from list. setupScopes() will fix the positions for (i = menuScope; i < scopeCount; i++) @@ -3569,7 +3565,7 @@ public void menuPerformed(String menu, String item) { pushUndo(); readSetupFile("blank.txt", "Blank Circuit"); } - + // if (ac.indexOf("setup ") == 0) { // pushUndo(); // readSetupFile(ac.substring(6), @@ -3620,10 +3616,10 @@ else if (s.compareTo("Select") == 0) setSlidersPanelHeight(); } } - + repaint(); } - + int countScopeElms() { int c = 0; for (int i = 0; i != elmList.size(); i++) { @@ -3632,7 +3628,7 @@ int countScopeElms() { } return c; } - + ScopeElm getNthScopeElm(int n) { for (int i = 0; i != elmList.size(); i++) { if ( elmList.get(i) instanceof ScopeElm) { @@ -3643,10 +3639,10 @@ ScopeElm getNthScopeElm(int n) { } return (ScopeElm) null; } - - + + boolean canStackScope(int s) { - if (scopeCount < 2) + if (scopeCount < 2) return false; if (s==0) s=1; @@ -3654,13 +3650,13 @@ boolean canStackScope(int s) { return false; return true; } - + boolean canCombineScope(int s) { return scopeCount >=2; } - + boolean canUnstackScope(int s) { - if (scopeCount < 2) + if (scopeCount < 2) return false; if (s==0) s=1; @@ -3705,7 +3701,7 @@ void combineScope(int s) { scopes[s-1].combine(scopes[s]); scopes[s].setElm(null); } - + void stackAll() { int i; @@ -3730,7 +3726,7 @@ void combineAll() { scopes[i+1].setElm(null); } } - + void separateAll() { int i; Scope newscopes[] = new Scope[20]; @@ -3752,7 +3748,7 @@ void doEdit(Editable eable) { editDialog = new EditDialog(eable, this); editDialog.show(); } - + void doSliders(CircuitElm ce) { clearSelection(); pushUndo(); @@ -3767,7 +3763,7 @@ void doExportAsUrl() dialogShowing = new ExportAsUrlDialog(dump); dialogShowing.show(); } - + void doExportAsText() { String dump = dumpCircuit(); @@ -3780,7 +3776,7 @@ void doExportAsImage() dialogShowing = new ExportAsImageDialog(CAC_IMAGE); dialogShowing.show(); } - + void doCreateSubcircuit() { EditCompositeModelDialog dlg = new EditCompositeModelDialog(); @@ -3790,7 +3786,7 @@ void doCreateSubcircuit() dialogShowing = dlg; dialogShowing.show(); } - + void doExportAsLocalFile() { String dump = dumpCircuit(); dialogShowing = new ExportAsLocalFileDialog(dump); @@ -3819,16 +3815,16 @@ String dumpOptions() { powerBar.getValue() + " " + minTimeStep + "\n"; return dump; } - + String dumpCircuit() { int i; CustomLogicModel.clearDumpedFlags(); CustomCompositeModel.clearDumpedFlags(); DiodeModel.clearDumpedFlags(); TransistorModel.clearDumpedFlags(); - + String dump = dumpOptions(); - + for (i = 0; i != elmList.size(); i++) { CircuitElm ce = getElm(i); String m = ce.dumpModel(); @@ -3854,7 +3850,7 @@ String dumpCircuit() { void getSetupList(final boolean openDefault) { String url; - url = GWT.getModuleBaseURL()+"setuplist.txt"+"?v="+random.nextInt(); + url = GWT.getModuleBaseURL()+"setuplist.txt"+"?v="+random.nextInt(); RequestBuilder requestBuilder = new RequestBuilder(RequestBuilder.GET, url); try { requestBuilder.sendRequest(null, new RequestCallback() { @@ -3867,10 +3863,10 @@ public void onResponseReceived(Request request, Response response) { // processing goes here if (response.getStatusCode()==Response.SC_OK) { String text = response.getText(); - processSetupList(text.getBytes(), openDefault); + processSetupList(text, openDefault); // end or processing } - else { + else { Window.alert(Locale.LS("Can't load circuit list!")); GWT.log("Bad file server response:"+response.getStatusText() ); } @@ -3880,16 +3876,16 @@ public void onResponseReceived(Request request, Response response) { GWT.log("failed file reading", e); } } - - void processSetupList(byte b[], final boolean openDefault) { - int len = b.length; - MenuBar currentMenuBar; - MenuBar stack[] = new MenuBar[6]; - int stackptr = 0; - currentMenuBar=new MenuBar(true); - currentMenuBar.setAutoOpen(true); - menuBar.addItem(Locale.LS("Circuits"), currentMenuBar); - + + void processSetupList(String text, final boolean openDefault) { + + MenuBar currentMenuBar; + MenuBar stack[] = new MenuBar[6]; + int stackptr = 0; + currentMenuBar=new MenuBar(true); + currentMenuBar.setAutoOpen(true); + menuBar.addItem(Locale.LS("Circuits"), currentMenuBar); + MenuBar h = new MenuBar(true); helpItem=iconMenuItem("book-open", "User Guide", (Command)null); h.addItem(helpItem); @@ -3902,71 +3898,68 @@ void processSetupList(byte b[], final boolean openDefault) { aboutItem.setScheduledCommand(new MyCommand("file","about")); h.addSeparator(); h.addItem(aboutCircuitsItem = iconMenuItem("link", "About Circuits", - new Command() { public void execute(){ - ScriptInjector.fromString("nw.Shell.openExternal('https://www.falstad.com/circuit/e-index.html');") - .setRemoveTag(false) - .setWindow(ScriptInjector.TOP_WINDOW) - .inject(); - } - })); + new Command() { public void execute(){ + ScriptInjector.fromString("nw.Shell.openExternal('https://www.falstad.com/circuit/e-index.html');") + .setRemoveTag(false) + .setWindow(ScriptInjector.TOP_WINDOW) + .inject(); + } + })); h.addItem(aboutCircuitsPLItem = iconMenuItem("link", "About Circuits (Polish ver.)", - new Command() { public void execute(){ - ScriptInjector.fromString("nw.Shell.openExternal('https://www.falstad.com/circuit/polish/e-index.html');") - .setRemoveTag(false) - .setWindow(ScriptInjector.TOP_WINDOW) - .inject(); - } - })); + new Command() { public void execute(){ + ScriptInjector.fromString("nw.Shell.openExternal('https://www.falstad.com/circuit/polish/e-index.html');") + .setRemoveTag(false) + .setWindow(ScriptInjector.TOP_WINDOW) + .inject(); + } + })); menuBar.addItem(Locale.LS("Help"), h); - - stack[stackptr++] = currentMenuBar; - int p; - for (p = 0; p < len; ) { - int l; - for (l = 0; l != len-p; l++) - if (b[l+p] == '\n' || b[l+p] == '\r') { - l++; - break; - } - String line = new String(b, p, l-1); - if (line.charAt(0) == '#') - ; - else if (line.charAt(0) == '+') { - // MenuBar n = new Menu(line.substring(1)); - MenuBar n = new MenuBar(true); - n.setAutoOpen(true); - currentMenuBar.addItem(Locale.LS(line.substring(1)),n); - currentMenuBar = stack[stackptr++] = n; - } else if (line.charAt(0) == '-') { - currentMenuBar = stack[--stackptr-1]; - } else { - int i = line.indexOf(' '); - if (i > 0) { - String title = Locale.LS(line.substring(i+1)); - boolean first = false; - if (line.charAt(0) == '>') - first = true; - String file = line.substring(first ? 1 : 0, i); - currentMenuBar.addItem(new MenuItem(title, - new MyCommand("circuits", "setup "+file+" " + title))); - if (file.equals(startCircuit) && startLabel == null) { - startLabel = title; - titleLabel.setText(title); - setSlidersPanelHeight(); - } - if (first && startCircuit == null) { - startCircuit = file; - startLabel = title; - if (openDefault && stopMessage == null) - readSetupFile(startCircuit, startLabel); - } - } - } - p += l; - } -} + stack[stackptr++] = currentMenuBar; + String[] lines = text.split("\\r?\\n"); + for (String line : lines) { + console(line); + if (line.isEmpty()) { + continue; + } + if (line.charAt(0) == '#') { + + } else if (line.charAt(0) == '+') { + // MenuBar n = new Menu(line.substring(1)); + MenuBar n = new MenuBar(true); + n.setAutoOpen(true); + currentMenuBar.addItem(Locale.LS(line.substring(1)), n); + currentMenuBar = stack[stackptr++] = n; + } else if (line.charAt(0) == '-') { + currentMenuBar = stack[--stackptr - 1]; + } else { + int i = line.indexOf(' '); + if (i > 0) { + String title = Locale.LS(line.substring(i + 1)); + boolean first = false; + if (line.charAt(0) == '>') + first = true; + String file = line.substring(first ? 1 : 0, i); + currentMenuBar.addItem(new MenuItem(title, + new MyCommand("circuits", "setup " + file + " " + title))); + if (file.equals(startCircuit) && startLabel == null) { + startLabel = title; + titleLabel.setText(title); + setSlidersPanelHeight(); + } + if (first && startCircuit == null) { + startCircuit = file; + startLabel = title; + if (openDefault && stopMessage == null) + readSetupFile(startCircuit, startLabel); + } + } + } + } + + } + void readCircuit(String text, int flags) { readCircuit(text.getBytes(), flags); if ((flags & RC_KEEP_TITLE) == 0) @@ -3985,21 +3978,21 @@ void setCircuitTitle(String s) { titleLabel.setText(s); setSlidersPanelHeight(); } - + void readSetupFile(String str, String title) { System.out.println(str); // TODO: Maybe think about some better approach to cache management! - String url=GWT.getModuleBaseURL()+"circuits/"+str+"?v="+random.nextInt(); + String url=GWT.getModuleBaseURL()+"circuits/"+str+"?v="+random.nextInt(); loadFileFromURL(url); if (title != null) titleLabel.setText(title); setSlidersPanelHeight(); unsavedChanges = false; } - + void loadFileFromURL(String url) { RequestBuilder requestBuilder = new RequestBuilder(RequestBuilder.GET, url); - + try { requestBuilder.sendRequest(null, new RequestCallback() { public void onError(Request request, Throwable exception) { @@ -4014,7 +4007,7 @@ public void onResponseReceived(Request request, Response response) { allowSave(false); unsavedChanges = false; } - else { + else { Window.alert(Locale.LS("Can't load circuit!")); GWT.log("Bad file server response:"+response.getStatusText() ); } @@ -4103,11 +4096,11 @@ void readCircuit(byte b[], int flags) { break; } // do not add new symbols here without testing export as link - + // if first character is a digit then parse the type as a number if (tint >= '0' && tint <= '9') tint = new Integer(type).intValue(); - + if (tint == 34) { DiodeModel.undumpModel(st); break; @@ -4131,7 +4124,7 @@ void readCircuit(byte b[], int flags) { int x2 = new Integer(st.nextToken()).intValue(); int y2 = new Integer(st.nextToken()).intValue(); int f = new Integer(st.nextToken()).intValue(); - + CircuitElm newce = createCe(tint, x1, y1, x2, y2, f, st); if (newce==null) { System.out.println("unrecognized dump type: " + type); @@ -4155,7 +4148,7 @@ void readCircuit(byte b[], int flags) { break; } p += l; - + } setPowerBarEnable(); enableItems(); @@ -4173,7 +4166,7 @@ void readCircuit(byte b[], int flags) { centreCircuit(); if ((flags & RC_SUBCIRCUITS) != 0) updateModels(); - + AudioInputElm.clearCache(); // to save memory DataInputElm.clearCache(); // to save memory } @@ -4191,7 +4184,7 @@ void deleteSliders(CircuitElm elm) { } } } - + void readHint(StringTokenizer st) { hintType = new Integer(st.nextToken()).intValue(); hintItem1 = new Integer(st.nextToken()).intValue(); @@ -4200,14 +4193,14 @@ void readHint(StringTokenizer st) { void readOptions(StringTokenizer st, int importFlags) { int flags = new Integer(st.nextToken()).intValue(); - + if ((importFlags & RC_RETAIN) != 0) { // need to set small grid if pasted circuit uses it if ((flags & 2) != 0) smallGridCheckItem.setState(true); return; } - + dotsCheckItem.setState((flags & 1) != 0); smallGridCheckItem.setState((flags & 2) != 0); voltsCheckItem.setState((flags & 4) == 0); @@ -4229,7 +4222,7 @@ void readOptions(StringTokenizer st, int importFlags) { } setGrid(); } - + int snapGrid(int x) { return (x+gridRound) & gridMask; } @@ -4255,7 +4248,7 @@ int locateElm(CircuitElm elm) { return i; return -1; } - + public void mouseDragged(MouseMoveEvent e) { // ignore right mouse button with no modifiers (needed on PC) if (e.getNativeButton()==NativeEvent.BUTTON_RIGHT) { @@ -4265,7 +4258,7 @@ public void mouseDragged(MouseMoveEvent e) { e.isAltKeyDown())) return; } - + if (tempMouseMode==MODE_DRAG_SPLITTER) { dragSplitter(e.getX(), e.getY()); return; @@ -4304,7 +4297,7 @@ else if (!noEditCheckItem.getState()) { // dragged when tapped on mobile devices if (System.currentTimeMillis()-mouseDownTime < 150) return; - + tempMouseMode = MODE_DRAG_SELECTED; changed = success = dragSelected(gx, gy); } @@ -4330,7 +4323,7 @@ else if (!noEditCheckItem.getState()) { writeRecoveryToStorage(); repaint(); } - + void dragSplitter(int x, int y) { double h = (double) canvasHeight; if (h<1) @@ -4396,7 +4389,7 @@ boolean onlyGraphicsElmsSelected() { } return true; } - + boolean dragSelected(int x, int y) { boolean me = false; int i; @@ -4453,13 +4446,13 @@ void dragPost(int x, int y, boolean all) { int dy = y-dragGridY; if (dx == 0 && dy == 0) return; - + if (all) { // go through all elms int i; for (i = 0; i != elmList.size(); i++) { CircuitElm e = elmList.get(i); - + // which post do we move? int p = 0; if (e.x == dragGridX && e.y == dragGridY) @@ -4479,7 +4472,7 @@ void doFlip() { menuElm.flipPosts(); needAnalyze(); } - + void doSplit(CircuitElm ce) { int x = snapGrid(inverseTransformX(menuX)); int y = snapGrid(inverseTransformY(menuY)); @@ -4489,18 +4482,18 @@ void doSplit(CircuitElm ce) { x = ce.x; else y = ce.y; - + // don't create zero-length wire if (x == ce.x && y == ce.y || x == ce.x2 && y == ce.y2) return; - + WireElm newWire = new WireElm(x, y); newWire.drag(ce.x2, ce.y2); ce.drag(x, y); elmList.addElement(newWire); needAnalyze(); } - + void selectArea(int x, int y, boolean add) { int x1 = min(x, initDragGridX); int x2 = max(x, initDragGridX); @@ -4522,7 +4515,7 @@ void selectArea(int x, int y, boolean add) { // } // mouseElm = cs; // } - + void setMouseElm(CircuitElm ce) { if (ce!=mouseElm) { if (mouseElm!=null) @@ -4549,12 +4542,12 @@ void removeZeroLengthElements() { } needAnalyze(); } - + boolean mouseIsOverSplitter(int x, int y) { boolean isOverSplitter; if (scopeCount == 0) return false; - isOverSplitter =((x>=0) && (x=0) && (x=circuitArea.height-5) && (y e) { mousePost = -1; plotXElm = plotYElm = null; - + if (mouseIsOverSplitter(sx, sy)) { setMouseElm(null); return; } - + if (circuitArea.contains(sx, sy)) { if (mouseElm!=null && ( mouseElm.getHandleGrabbedClose(gx, gy, POSTGRABSQ, MINPOSTGRABSIZE)>=0)) { newMouseElm=mouseElm; @@ -4725,7 +4718,7 @@ public void onContextMenu(ContextMenuEvent e) { doPopupMenu(); } } - + @SuppressWarnings("deprecation") void doPopupMenu() { if (noEditCheckItem.getState() || dialogIsShowing()) @@ -4738,7 +4731,7 @@ void doPopupMenu() { if (scopes[scopeSelected].canMenu()) { menuScope=scopeSelected; menuPlot=scopes[scopeSelected].selectedPlot; - scopePopupMenu.doScopePopupChecks(false, canStackScope(scopeSelected), canCombineScope(scopeSelected), + scopePopupMenu.doScopePopupChecks(false, canStackScope(scopeSelected), canCombineScope(scopeSelected), canUnstackScope(scopeSelected), scopes[scopeSelected]); contextPanel=new PopupPanel(true); contextPanel.add(scopePopupMenu.getMenuBar()); @@ -4799,15 +4792,15 @@ boolean canSplit(CircuitElm ce) { return true; return false; } - + // check if the user can create sliders for this element boolean sliderItemEnabled(CircuitElm elm) { int i; - + // prevent confusion if (elm instanceof VarRailElm || elm instanceof PotElm) return false; - + for (i = 0; ; i++) { EditInfo ei = elm.getEditInfo(i); if (ei == null) @@ -4820,13 +4813,13 @@ boolean sliderItemEnabled(CircuitElm elm) { void longPress() { doPopupMenu(); } - + void twoFingerTouch(int x, int y) { tempMouseMode = MODE_DRAG_ALL; dragScreenX = x; dragScreenY = y; } - + // public void mouseClicked(MouseEvent e) { public void onClick(ClickEvent e) { e.preventDefault(); @@ -4836,21 +4829,21 @@ public void onClick(ClickEvent e) { // if (e.getNativeButton() == NativeEvent.BUTTON_LEFT) { // if (mouseMode == MODE_SELECT || mouseMode == MODE_DRAG_SELECTED) // clearSelection(); -// } +// } if ((e.getNativeButton() == NativeEvent.BUTTON_MIDDLE)) scrollValues(e.getNativeEvent().getClientX(), e.getNativeEvent().getClientY(), 0); } - + public void onDoubleClick(DoubleClickEvent e){ e.preventDefault(); // if (!didSwitch && mouseElm != null) if (mouseElm != null && !(mouseElm instanceof SwitchElm) && !noEditCheckItem.getState()) doEdit(mouseElm); } - + // public void mouseEntered(MouseEvent e) { // } - + public void onMouseOut(MouseOutEvent e) { mouseCursorX=-1; } @@ -4860,35 +4853,35 @@ void clearMouseElm() { setMouseElm(null); plotXElm = plotYElm = null; } - + int menuClientX, menuClientY; int menuX, menuY; - + public void onMouseDown(MouseDownEvent e) { // public void mousePressed(MouseEvent e) { e.preventDefault(); - + // make sure canvas has focus, not stop button or something else, so all shortcuts work cv.setFocus(true); - - stopElm = null; // if stopped, allow user to select other elements to fix circuit + + stopElm = null; // if stopped, allow user to select other elements to fix circuit menuX = menuClientX = e.getX(); menuY = menuClientY = e.getY(); mouseDownTime = System.currentTimeMillis(); - + // maybe someone did copy in another window? should really do this when // window receives focus enablePaste(); - + if (e.getNativeButton() != NativeEvent.BUTTON_LEFT && e.getNativeButton() != NativeEvent.BUTTON_MIDDLE) return; - + // set mouseElm in case we are on mobile mouseSelect(e); - + mouseDragging=true; didSwitch = false; - + if (mouseWasOverSplitter) { tempMouseMode = MODE_DRAG_SPLITTER; return; @@ -4908,11 +4901,11 @@ else if (e.isControlKeyDown() || e.isMetaKeyDown()) tempMouseMode = MODE_DRAG_POST; } else tempMouseMode = MODE_DRAG_ALL; - + if (noEditCheckItem.getState()) tempMouseMode = MODE_SELECT; - + if (!(dialogIsShowing()) && ((scopeSelected != -1 && scopes[scopeSelected].cursorInSettingsWheel()) || ( scopeSelected == -1 && mouseElm instanceof ScopeElm && ((ScopeElm)mouseElm).elmScope.cursorInSettingsWheel()))){ if (noEditCheckItem.getState()) @@ -4920,7 +4913,7 @@ else if (e.isControlKeyDown() || e.isMetaKeyDown()) Scope s; if (scopeSelected != -1) s=scopes[scopeSelected]; - else + else s=((ScopeElm)mouseElm).elmScope; s.properties(); clearSelection(); @@ -4936,13 +4929,13 @@ else if (e.isControlKeyDown() || e.isMetaKeyDown()) didSwitch = true; return; } - + // IES - Grab resize handles in select mode if they are far enough apart and you are on top of them if (tempMouseMode == MODE_SELECT && mouseElm!=null && !noEditCheckItem.getState() && mouseElm.getHandleGrabbedClose(gx, gy, POSTGRABSQ, MINPOSTGRABSIZE) >=0 && !anySelectedButMouse()) tempMouseMode = MODE_DRAG_POST; - + if (tempMouseMode != MODE_SELECT && tempMouseMode != MODE_DRAG_SELECTED) clearSelection(); @@ -4952,7 +4945,7 @@ else if (e.isControlKeyDown() || e.isMetaKeyDown()) dragging = true; if (tempMouseMode !=MODE_ADD_ELM) return; -// +// int x0 = snapGrid(gx); int y0 = snapGrid(gy); if (!circuitArea.contains(e.getX(), e.getY())) @@ -4966,7 +4959,7 @@ else if (e.isControlKeyDown() || e.isMetaKeyDown()) } static int lastSubcircuitMenuUpdate; - + // check/uncheck/enable/disable menu items as appropriate when menu bar clicked on, or when // right mouse menu accessed. also displays shortcuts as a side effect void doMainMenuChecks() { @@ -4985,17 +4978,17 @@ void doMainMenuChecks() { unstackAllItem.setEnabled(scopeCount > 1 && scopes[scopeCount-1].position != scopeCount -1); combineAllItem.setEnabled(scopeCount > 1); separateAllItem.setEnabled(scopeCount > 0); - + // also update the subcircuit menu if necessary if (lastSubcircuitMenuUpdate != CustomCompositeModel.sequenceNumber) composeSubcircuitMenu(); } - - + + public void onMouseUp(MouseUpEvent e) { e.preventDefault(); mouseDragging=false; - + // click to clear selection if (tempMouseMode == MODE_SELECT && selectedArea == null) clearSelection(); @@ -5003,7 +4996,7 @@ public void onMouseUp(MouseUpEvent e) { // cmd-click = split wire if (tempMouseMode == MODE_DRAG_POST && draggingPost == -1) doSplit(mouseElm); - + tempMouseMode = mouseMode; selectedArea = null; dragging = false; @@ -5039,20 +5032,20 @@ public void onMouseUp(MouseUpEvent e) { dragElm = null; repaint(); } - + public void onMouseWheel(MouseWheelEvent e) { e.preventDefault(); - + // once we start zooming, don't allow other uses of mouse wheel for a while // so we don't accidentally edit a resistor value while zooming boolean zoomOnly = System.currentTimeMillis() < zoomTime+1000; - + if (noEditCheckItem.getState() || !mouseWheelEditCheckItem.getState()) zoomOnly = true; - + if (!zoomOnly) scrollValues(e.getNativeEvent().getClientX(), e.getNativeEvent().getClientY(), e.getDeltaY()); - + if (mouseElm instanceof MouseWheelHandler && !zoomOnly) ((MouseWheelHandler) mouseElm).onMouseWheel(e); else if (scopeSelected != -1 && !zoomOnly) @@ -5076,7 +5069,7 @@ void zoomCircuit(double dy, boolean menu) { newScale = Math.min(newScale, 2.5); setCircuitScale(newScale, menu); } - + void setCircuitScale(double newScale, boolean menu) { int constX = !menu ? mouseCursorX : circuitArea.width/2; int constY = !menu ? mouseCursorY : circuitArea.height/2; @@ -5089,7 +5082,7 @@ void setCircuitScale(double newScale, boolean menu) { transform[4] = constX - cx*newScale; transform[5] = constY - cy*newScale; } - + void setPowerBarEnable() { if (powerCheckItem.getState()) { powerLabel.setStyleName("disabled", false); @@ -5106,10 +5099,10 @@ void scrollValues(int x, int y, int deltay) { scrollValuePopup = new ScrollValuePopup(x, y, deltay, mouseElm, this); } } - + void enableItems() { } - + void setGrid() { gridSize = (smallGridCheckItem.getState()) ? 8 : 16; gridMask = ~(gridSize-1); @@ -5151,14 +5144,14 @@ void loadUndoItem(UndoItem ui) { transform[4] = ui.transform4; transform[5] = ui.transform5; } - + void doRecover() { pushUndo(); readCircuit(recovery); allowSave(false); recoverItem.setEnabled(false); } - + void enableUndoRedo() { redoItem.setEnabled(redoStack.size() > 0); undoItem.setEnabled(undoStack.size() > 0); @@ -5173,14 +5166,14 @@ void setMouseMode(int mode) setCursorStyle("cursorPointer"); } } - + void setCursorStyle(String s) { if (lastCursorStyle!=null) cv.removeStyleName(lastCursorStyle); cv.addStyleName(s); lastCursorStyle=s; } - + void setMenuSelection() { @@ -5217,7 +5210,7 @@ void writeClipboardToStorage() { return; stor.setItem("circuitClipboard", clipboard); } - + void readClipboardFromStorage() { Storage stor = Storage.getLocalStorageIfSupported(); if (stor == null) @@ -5249,14 +5242,14 @@ void deleteUnusedScopeElms() { if (ce instanceof ScopeElm && (((ScopeElm) ce).elmScope.needToRemove() )) { ce.delete(); elmList.removeElementAt(i); - + // need to rebuild scopeElmArr needAnalyze(); } } - + } - + void doDelete(boolean pushUndoFlag) { int i; if (pushUndoFlag) @@ -5277,20 +5270,20 @@ void doDelete(boolean pushUndoFlag) { deleteUnusedScopeElms(); needAnalyze(); writeRecoveryToStorage(); - } + } } - + boolean willDelete( CircuitElm ce ) { // Is this element in the list to be deleted. // This changes the logic from the previous version which would initially only - // delete selected elements (which could include the mouseElm) and then delete the + // delete selected elements (which could include the mouseElm) and then delete the // mouseElm if there were no selected elements. Not really sure this added anything useful // to the user experience. // // BTW, the old logic could also leave mouseElm pointing to a deleted element. return ce.isSelected() || ce.isMouseElm(); } - + String copyOfSelectedElms() { String r = dumpOptions(); CustomLogicModel.clearDumpedFlags(); @@ -5308,17 +5301,17 @@ String copyOfSelectedElms() { } return r; } - + void doCopy() { // clear selection when we're done if we're copying a single element using the context menu boolean clearSel = (menuElm != null && !menuElm.selected); - + setMenuSelection(); clipboard=copyOfSelectedElms(); - + if (clearSel) clearSelection(); - + writeClipboardToStorage(); enablePaste(); } @@ -5335,13 +5328,13 @@ void doDuplicate() { s=copyOfSelectedElms(); doPaste(s); } - + void doPaste(String dump) { pushUndo(); clearSelection(); int i; Rectangle oldbb = null; - + // get old bounding box for (i = 0; i != elmList.size(); i++) { CircuitElm ce = getElm(i); @@ -5351,18 +5344,18 @@ void doPaste(String dump) { else oldbb = bb; } - + // add new items int oldsz = elmList.size(); int flags = RC_RETAIN; - + // don't recenter circuit if we're going to paste in place because that will change the transform // if (mouseCursorX > 0 && circuitArea.contains(mouseCursorX, mouseCursorY)) - + // in fact, don't ever recenter circuit, unless old circuit was empty if (oldsz > 0) flags |= RC_NO_CENTER; - + if (dump != null) readCircuit(dump, flags); else { @@ -5381,25 +5374,25 @@ void doPaste(String dump) { else newbb = bb; } - + if (oldbb != null && newbb != null /*&& oldbb.intersects(newbb)*/) { // find a place on the edge for new items int dx = 0, dy = 0; int spacew = circuitArea.width - oldbb.width - newbb.width; int spaceh = circuitArea.height - oldbb.height - newbb.height; - + if (!oldbb.intersects(newbb)) { // old coordinates may be really far away so move them to same origin as current circuit dx = snapGrid(oldbb.x - newbb.x); dy = snapGrid(oldbb.y - newbb.y); } - + if (spacew > spaceh) { dx = snapGrid(oldbb.x + oldbb.width - newbb.x + gridSize); } else { dy = snapGrid(oldbb.y + oldbb.height - newbb.y + gridSize); } - + // move new items near the mouse if possible if (mouseCursorX > 0 && circuitArea.contains(mouseCursorX, mouseCursorY)) { int gx = inverseTransformX(mouseCursorX); @@ -5415,13 +5408,13 @@ void doPaste(String dump) { dy = mdy; } } - + // move the new items for (i = oldsz; i != elmList.size(); i++) { CircuitElm ce = getElm(i); ce.move(dx, dy); } - + // center circuit // handleResize(); } @@ -5436,7 +5429,7 @@ void clearSelection() { ce.setSelected(false); } } - + void doSelectAll() { int i; for (i = 0; i != elmList.size(); i++) { @@ -5444,7 +5437,7 @@ void doSelectAll() { ce.setSelected(true); } } - + boolean anySelectedButMouse() { for (int i=0; i != elmList.size(); i++) if (getElm(i)!= mouseElm && getElm(i).selected) @@ -5454,7 +5447,7 @@ boolean anySelectedButMouse() { // public void keyPressed(KeyEvent e) {} // public void keyReleased(KeyEvent e) {} - + boolean dialogIsShowing() { if (editDialog!=null && editDialog.isShowing()) return true; @@ -5476,7 +5469,7 @@ boolean dialogIsShowing() { return true; return false; } - + public void onPreviewNativeEvent(NativePreviewEvent e) { int cc=e.getNativeEvent().getCharCode(); int t=e.getTypeInt(); @@ -5489,7 +5482,7 @@ public void onPreviewNativeEvent(NativePreviewEvent e) { if (code==KEY_ENTER) scrollValuePopup.close(true); } - + // process escape/enter for dialogs // multiple edit dialogs could be displayed at once, pick the one in front Dialog dlg = editDialog; @@ -5508,7 +5501,7 @@ public void onPreviewNativeEvent(NativePreviewEvent e) { } return; } - + if ((t&Event.ONKEYPRESS)!=0) { if (cc=='-') { menuPerformed("key", "zoomout"); @@ -5527,7 +5520,7 @@ public void onPreviewNativeEvent(NativePreviewEvent e) { e.cancel(); } } - + // all other shortcuts are ignored when editing disabled if (noEditCheckItem.getState()) return; @@ -5599,7 +5592,7 @@ public void onPreviewNativeEvent(NativePreviewEvent e) { if (code==KEY_O) { menuPerformed("key", "importfromlocalfile"); e.cancel(); - } + } } } if ((t&Event.ONKEYPRESS)!=0) { @@ -5616,144 +5609,29 @@ public void onPreviewNativeEvent(NativePreviewEvent e) { setMouseMode(MODE_SELECT); mouseModeStr = "Select"; tempMouseMode = mouseMode; - e.cancel(); + e.cancel(); } } } - + // factors a matrix into upper and lower triangular matrices by // gaussian elimination. On entry, a[0..n-1][0..n-1] is the // matrix to be factored. ipvt[] returns an integer vector of pivot // indices, used in the lu_solve() routine. static boolean lu_factor(double a[][], int n, int ipvt[]) { - int i,j,k; - - if(n>100){ - DMatrixSparseCSC dMatrixRMaj = DMatrixSparseCSC.convert(a,DMatrixSparseCSC.EPS); - return sparseLU.setA(dMatrixRMaj); - } - - // check for a possible singular matrix by scanning for rows that - // are all zeroes - for (i = 0; i != n; i++) { - boolean row_all_zeros = true; - for (j = 0; j != n; j++) { - if (a[i][j] != 0) { - row_all_zeros = false; - break; - } - } - // if all zeros, it's a singular matrix - if (row_all_zeros) - return false; - } - - // use Crout's method; loop through the columns - for (j = 0; j != n; j++) { - - // calculate upper triangular elements for this column - for (i = 0; i != j; i++) { - double q = a[i][j]; - for (k = 0; k != i; k++) - q -= a[i][k]*a[k][j]; - a[i][j] = q; - } - - // calculate lower triangular elements for this column - double largest = 0; - int largestRow = -1; - for (i = j; i != n; i++) { - double q = a[i][j]; - for (k = 0; k != j; k++) - q -= a[i][k]*a[k][j]; - a[i][j] = q; - double x = Math.abs(q); - if (x >= largest) { - largest = x; - largestRow = i; - } - } - - // pivoting - if (j != largestRow) { - if (largestRow == -1) { - console("largestRow == -1"); - return false; - } - double x; - for (k = 0; k != n; k++) { - x = a[largestRow][k]; - a[largestRow][k] = a[j][k]; - a[j][k] = x; - } - } - - // keep track of row interchanges - ipvt[j] = largestRow; - - // check for zeroes; if we find one, it's a singular matrix. - // we used to avoid them, but that caused weird bugs. For example, - // two inverters with outputs connected together should be flagged - // as a singular matrix, but it was allowed (with weird currents) - if (a[j][j] == 0.0) { - console("didn't avoid zero"); -// a[j][j]=1e-18; - return false; - } - - if (j != n-1) { - double mult = 1.0/a[j][j]; - for (i = j+1; i != n; i++) - a[i][j] *= mult; - } - } - return true; + DMatrixSparseCSC dMatrixRMaj = DMatrixSparseCSC.convert(a,DMatrixSparseCSC.EPS); + return sparseLU.setA(dMatrixRMaj); } - final static SparseLU sparseLU = new SparseLU(); + + final static SparseLU sparseLU = new SparseLU(); // Solves the set of n linear equations using a LU factorization // previously performed by lu_factor. On input, b[0..n-1] is the right // hand side of the equations, and on output, contains the solution. static void lu_solve(double a[][], int n, int ipvt[], double b[]) { - int i; - if(n>100){ - sparseLU.solve(b,b); - return; - } - // find first nonzero b element - for (i = 0; i != n; i++) { - int row = ipvt[i]; - - double swap = b[row]; - b[row] = b[i]; - b[i] = swap; - if (swap != 0) - break; - } - - int bi = i++; - for (; i < n; i++) { - int row = ipvt[i]; - int j; - double tot = b[row]; - - b[row] = b[i]; - // forward substitution using the lower triangular matrix - for (j = bi; j < i; j++) - tot -= a[i][j]*b[j]; - b[i] = tot; - } - for (i = n-1; i >= 0; i--) { - double tot = b[i]; - - // back-substitution using the upper triangular matrix - int j; - for (j = i+1; j != n; j++) - tot -= a[i][j]*b[j]; - b[i] = tot/a[i][i]; - } + sparseLU.solve(b); } - + void createNewLoadFile() { // This is a hack to fix what IMHO is a bug in the 2) { l = l.slice(-2).toUpperCase(); return (l == "US" || (l=="CA" && orCanada)); @@ -6199,7 +6077,7 @@ native boolean weAreInGermany() /*-{ } catch (e) { return 0; } }-*/; - + // For debugging void dumpNodelist() { @@ -6223,7 +6101,7 @@ void dumpNodelist() { cs = e.getDumpClass().toString(); int p = cs.lastIndexOf('.'); cs = cs.substring(p+1); - if (cs=="WireElm") + if (cs=="WireElm") continue; if (cs=="LabeledNodeElm") cs = cs+" "+((LabeledNodeElm)e).text; @@ -6240,7 +6118,7 @@ void dumpNodelist() { console(s); } } - + native void printCanvas(CanvasElement cv) /*-{ var img = cv.toDataURL("image/png"); var win = window.open("", "print", "height=500,width=500,status=yes,location=no"); @@ -6255,7 +6133,7 @@ void doDCAnalysis() { dcAnalysisFlag = true; resetAction(); } - + void doPrint() { Canvas cv = getCircuitAsCanvas(CAC_PRINT); printCanvas(cv.getCanvasElement()); @@ -6303,12 +6181,12 @@ public void doExportAsSVGFromAPI() { static final int CAC_PRINT = 0; static final int CAC_IMAGE = 1; static final int CAC_SVG = 2; - + public Canvas getCircuitAsCanvas(int type) { // create canvas to draw circuit into Canvas cv = Canvas.createIfSupported(); Rectangle bounds = getCircuitBounds(); - + // add some space on edges because bounds calculation is not perfect int wmargin = 140; int hmargin = 100; @@ -6316,21 +6194,21 @@ public Canvas getCircuitAsCanvas(int type) { int h = (bounds.height*2+hmargin) ; cv.setCoordinateSpaceWidth(w); cv.setCoordinateSpaceHeight(h); - + Context2d context = cv.getContext2d(); drawCircuitInContext(context, type, bounds, w, h); return cv; } - + // create SVG context using canvas2svg native static Context2d createSVGContext(int w, int h) /*-{ return new C2S(w, h); }-*/; - + native static String getSerializedSVG(Context2d context) /*-{ return context.getSerializedSvg(); }-*/; - + public String getCircuitAsSVG() { Rectangle bounds = getCircuitBounds(); @@ -6343,14 +6221,14 @@ public String getCircuitAsSVG() { drawCircuitInContext(context, CAC_SVG, bounds, w, h); return getSerializedSVG(context); } - + void drawCircuitInContext(Context2d context, int type, Rectangle bounds, int w, int h) { Graphics g = new Graphics(context); context.setTransform(1, 0, 0, 1, 0, 0); double oldTransform[] = Arrays.copyOf(transform, 6); - + double scale = 1; - + // turn on white background, turn off current display boolean p = printableCheckItem.getState(); boolean c = dotsCheckItem.getState(); @@ -6374,7 +6252,7 @@ void drawCircuitInContext(Context2d context, int type, Rectangle bounds, int w, if (bounds != null) scale = Math.min(w /(double)(bounds.width+wmargin), h/(double)(bounds.height+hmargin)); - + // ScopeElms need the transform array to be updated transform[0] = transform[3] = scale; transform[4] = -(bounds.x-wmargin/2); @@ -6382,7 +6260,7 @@ void drawCircuitInContext(Context2d context, int type, Rectangle bounds, int w, context.scale(scale, scale); context.translate(transform[4], transform[5]); context.setLineCap(Context2d.LineCap.ROUND); - + // draw elements int i; for (i = 0; i != elmList.size(); i++) { @@ -6397,14 +6275,14 @@ void drawCircuitInContext(Context2d context, int type, Rectangle bounds, int w, dotsCheckItem.setState(c); transform = oldTransform; } - + boolean isSelection() { for (int i = 0; i != elmList.size(); i++) if (getElm(i).isSelected()) return true; return false; } - + public CustomCompositeModel getCircuitAsComposite() { int i; String nodeDump = ""; @@ -6419,10 +6297,10 @@ public CustomCompositeModel getCircuitAsComposite() { }; Vector extList = new Vector(); boolean sel = isSelection(); - + boolean used[] = new boolean[nodeList.size()]; boolean extnodes[] = new boolean[nodeList.size()]; - + // find all the labeled nodes, get a list of them, and create a node number map for (i = 0; i != elmList.size(); i++) { CircuitElm ce = getElm(i); @@ -6433,22 +6311,22 @@ public CustomCompositeModel getCircuitAsComposite() { String label = lne.text; if (lne.isInternal()) continue; - + // already added to list? if (extnodes[ce.getNode(0)]) continue; - + int side = ChipElm.SIDE_W; if (Math.abs(ce.dx) >= Math.abs(ce.dy) && ce.dx > 0) side = ChipElm.SIDE_E; if (Math.abs(ce.dx) <= Math.abs(ce.dy) && ce.dy < 0) side = ChipElm.SIDE_N; if (Math.abs(ce.dx) <= Math.abs(ce.dy) && ce.dy > 0) side = ChipElm.SIDE_S; - + // create ext list entry for external nodes sideLabels[side].add(lne); extnodes[ce.getNode(0)] = true; } } - + Collections.sort(sideLabels[ChipElm.SIDE_W], (LabeledNodeElm a, LabeledNodeElm b) -> Integer.signum(a.y - b.y)); Collections.sort(sideLabels[ChipElm.SIDE_E], (LabeledNodeElm a, LabeledNodeElm b) -> Integer.signum(a.y - b.y)); Collections.sort(sideLabels[ChipElm.SIDE_N], (LabeledNodeElm a, LabeledNodeElm b) -> Integer.signum(a.x - b.x)); @@ -6481,24 +6359,24 @@ public CustomCompositeModel getCircuitAsComposite() { used[n] = true; nodeDump += " " + n; } - + // save positions int x1 = ce.x; int y1 = ce.y; int x2 = ce.x2; int y2 = ce.y2; - + // set them to 0 so they're easy to remove ce.x = ce.y = ce.x2 = ce.y2 = 0; String tstring = ce.dump(); tstring = tstring.replaceFirst("[A-Za-z0-9]+ 0 0 0 0 ", ""); // remove unused tint_x1 y1 x2 y2 coords for internal components - + // restore positions ce.x = x1; ce.y = y1; ce.x2 = x2; ce.y2 = y2; if (dump.length() > 0) dump += " "; dump += CustomLogicModel.escape(tstring); } - + for (i = 0; i != extList.size(); i++) { ExtListEntry ent = extList.get(i); if (!used[ent.node]) { @@ -6506,7 +6384,7 @@ public CustomCompositeModel getCircuitAsComposite() { return null; } } - + boolean first = true; for (i = 0; i != unconnectedNodes.size(); i++) { int q = unconnectedNodes.get(i); @@ -6518,7 +6396,7 @@ public CustomCompositeModel getCircuitAsComposite() { Window.alert("Some nodes are unconnected!"); return null; } - } + } CustomCompositeModel ccm = new CustomCompositeModel(); ccm.nodeList = nodeDump; @@ -6526,14 +6404,14 @@ public CustomCompositeModel getCircuitAsComposite() { ccm.extList = extList; return ccm; } - + static void invertMatrix(double a[][], int n) { int ipvt[] = new int[n]; lu_factor(a, n, ipvt); int i, j; double b[] = new double[n]; double inva[][] = new double[n][n]; - + // solve for each column of identity matrix for (i = 0; i != n; i++) { for (j = 0; j != n; j++) @@ -6543,13 +6421,13 @@ static void invertMatrix(double a[][], int n) { for (j = 0; j != n; j++) inva[j][i] = b[j]; } - + // return in original matrix for (i = 0; i != n; i++) for (j = 0; j != n; j++) a[i][j] = inva[i][j]; } - + double getLabeledNodeVoltage(String name) { Integer node = LabeledNodeElm.getByName(name); if (node == null || node == 0) @@ -6557,7 +6435,7 @@ static void invertMatrix(double a[][], int n) { // subtract one because ground is not included in nodeVoltages[] return nodeVoltages[node.intValue()-1]; } - + void setExtVoltage(String name, double v) { int i; for (i = 0; i != elmList.size(); i++) { @@ -6571,7 +6449,7 @@ void setExtVoltage(String name, double v) { } native JsArray getJSArray() /*-{ return []; }-*/; - + JsArray getJSElements() { int i; JsArray arr = getJSArray(); @@ -6582,7 +6460,7 @@ JsArray getJSElements() { } return arr; } - + native void setupJSInterface() /*-{ var that = this; $wnd.CircuitJS1 = { @@ -6591,7 +6469,7 @@ native void setupJSInterface() /*-{ getTimeStep: $entry(function() { return that.@com.lushprojects.circuitjs1.client.CirSim::timeStep; } ), setTimeStep: $entry(function(ts) { that.@com.lushprojects.circuitjs1.client.CirSim::timeStep = ts; } ), // don't use this, see #843 getMaxTimeStep: $entry(function() { return that.@com.lushprojects.circuitjs1.client.CirSim::maxTimeStep; } ), - setMaxTimeStep: $entry(function(ts) { that.@com.lushprojects.circuitjs1.client.CirSim::maxTimeStep = + setMaxTimeStep: $entry(function(ts) { that.@com.lushprojects.circuitjs1.client.CirSim::maxTimeStep = that.@com.lushprojects.circuitjs1.client.CirSim::timeStep = ts; } ), isRunning: $entry(function() { return that.@com.lushprojects.circuitjs1.client.CirSim::simIsRunning()(); } ), getNodeVoltage: $entry(function(n) { return that.@com.lushprojects.circuitjs1.client.CirSim::getLabeledNodeVoltage(Ljava/lang/String;)(n); } ), @@ -6607,26 +6485,26 @@ native void setupJSInterface() /*-{ if (hook) hook($wnd.CircuitJS1); }-*/; - + native void callUpdateHook() /*-{ var hook = $wnd.CircuitJS1.onupdate; if (hook) hook($wnd.CircuitJS1); }-*/; - + native void callAnalyzeHook() /*-{ var hook = $wnd.CircuitJS1.onanalyze; if (hook) hook($wnd.CircuitJS1); }-*/; - + native void callTimeStepHook() /*-{ var hook = $wnd.CircuitJS1.ontimestep; if (hook) hook($wnd.CircuitJS1); }-*/; - + native void callSVGRenderedHook(String svgData) /*-{ var hook = $wnd.CircuitJS1.onsvgrendered; if (hook) diff --git a/src/main/java/com/lushprojects/circuitjs1/client/matrix/SparseLU.java b/src/main/java/com/lushprojects/circuitjs1/client/matrix/SparseLU.java index fadb513..2e4d14e 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/matrix/SparseLU.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/matrix/SparseLU.java @@ -128,14 +128,14 @@ public boolean decompose(DMatrixSparseCSC A) { private final DGrowArray gx = new DGrowArray(); private final DGrowArray gb = new DGrowArray(); - public void solve(double[] B, double[] X) { + public void solve(double[] B) { if (B.length != this.AnumRows) { int var10002 = B.length; throw new IllegalArgumentException("Unexpected number of rows in B based on shape of A. Found=" + var10002 + " Expected=" + this.AnumRows); } - double[] x = adjust(this.gx, X.length); + double[] x = adjust(this.gx, B.length); double[] b = adjust(this.gb, B.length); DMatrixSparseCSC L = this.L; DMatrixSparseCSC U = this.U; @@ -144,12 +144,12 @@ public void solve(double[] B, double[] X) { b[i] = B[i]; } - permuteInv(pinv, b, x, X.length); + permuteInv(pinv, b, x, B.length); solveL(L, x); solveU(U, x); - for(int i = 0; i < X.length; i++) { - X[i] = x[i]; + for(int i = 0; i < B.length; i++) { + B[i] = x[i]; } } public static int solveColB(DMatrixSparseCSC G, boolean lower, DMatrixSparseCSC B, int colB, double[] x, int[] pinv, IGrowArray g_xi, int[] w) { From 0236a6092bd8dee0883364a7eba0a9b3ea052182 Mon Sep 17 00:00:00 2001 From: 11 <11> Date: Sat, 30 Dec 2023 11:58:48 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=E6=9B=B4=E6=96=B0readme=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 16aa86f..c88eed0 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,7 @@ +# 更新内容 2023-12-30 +1、优化矩阵LU分解,更换为稀疏矩阵LU分解,加快大型电路仿真优化速度\ +2、修改读取电路模型,避免IDEA编译为无法正常加载电路 + # CircuitJS1 Desktop Mod **Circuit Simulator is renamed CircuitJS1 Desktop Mod** From 2eab12891861727aa4d129847dbfe61214de9ab9 Mon Sep 17 00:00:00 2001 From: 11 <11> Date: Sat, 30 Dec 2023 13:38:42 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=8F=8C=E5=9C=88?= =?UTF-8?q?=E7=BB=A7=E7=94=B5=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- .../circuitjs1/client/CirSim.java | 6 +- .../circuitjs1/client/Relay2Elm.java | 596 ++++++++++++++++++ .../circuitjs1/public/locale_zh.txt | 1 + 4 files changed, 603 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/lushprojects/circuitjs1/client/Relay2Elm.java diff --git a/README.md b/README.md index c88eed0..25e95e9 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # 更新内容 2023-12-30 1、优化矩阵LU分解,更换为稀疏矩阵LU分解,加快大型电路仿真优化速度\ 2、修改读取电路模型,避免IDEA编译为无法正常加载电路 - +3、增加双圈继电器 # CircuitJS1 Desktop Mod **Circuit Simulator is renamed CircuitJS1 Desktop Mod** diff --git a/src/main/java/com/lushprojects/circuitjs1/client/CirSim.java b/src/main/java/com/lushprojects/circuitjs1/client/CirSim.java index 4080f4f..2f5ec18 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/CirSim.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/CirSim.java @@ -1180,6 +1180,7 @@ public void composeMainMenu(MenuBar mainMenuBar, int num) { passMenuBar.addItem(getClassCheckItem(Locale.LS("Add Tapped Transformer"), "TappedTransformerElm")); passMenuBar.addItem(getClassCheckItem(Locale.LS("Add Transmission Line"), "TransLineElm")); passMenuBar.addItem(getClassCheckItem(Locale.LS("Add Relay"), "RelayElm")); + passMenuBar.addItem(getClassCheckItem(Locale.LS("Add Double Coil Relay"), "Relay2Elm")); passMenuBar.addItem(getClassCheckItem(Locale.LS("Add Memristor"), "MemristorElm")); passMenuBar.addItem(getClassCheckItem(Locale.LS("Add Spark Gap"), "SparkGapElm")); passMenuBar.addItem(getClassCheckItem(Locale.LS("Add Fuse"), "FuseElm")); @@ -3959,7 +3960,7 @@ void processSetupList(String text, final boolean openDefault) { } } - + void readCircuit(String text, int flags) { readCircuit(text.getBytes(), flags); if ((flags & RC_KEEP_TITLE) == 0) @@ -5773,6 +5774,7 @@ public static CircuitElm createCe(int tint, int x1, int y1, int x2, int y2, int case 422: return new DelayBufferElm(x1, y1, x2, y2, f, st); case 423: return new LineElm(x1, y1, x2, y2, f, st); case 424: return new DataInputElm(x1, y1, x2, y2, f, st); + case 425: return new Relay2Elm(x1, y1, x2, y2, f, st); } return null; } @@ -5842,6 +5844,8 @@ public static CircuitElm constructElement(String n, int x1, int y1){ return (CircuitElm) new TransLineElm(x1, y1); if (n=="RelayElm") return (CircuitElm) new RelayElm(x1, y1); + if (n=="Relay2Elm") + return (CircuitElm) new Relay2Elm(x1, y1); if (n=="MemristorElm") return (CircuitElm) new MemristorElm(x1, y1); if (n=="SparkGapElm") diff --git a/src/main/java/com/lushprojects/circuitjs1/client/Relay2Elm.java b/src/main/java/com/lushprojects/circuitjs1/client/Relay2Elm.java new file mode 100644 index 0000000..c6e2883 --- /dev/null +++ b/src/main/java/com/lushprojects/circuitjs1/client/Relay2Elm.java @@ -0,0 +1,596 @@ +/* + Copyright (C) Paul Falstad and Iain Sharp + + This file is part of CircuitJS1. + + CircuitJS1 is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + CircuitJS1 is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with CircuitJS1. If not, see . +*/ + +package com.lushprojects.circuitjs1.client; + +import com.google.gwt.user.client.ui.Button; +import com.lushprojects.circuitjs1.client.util.Locale; + +// 0 = switch +// 1 = switch end 1 +// 2 = switch end 2 +// ... +// 3n = coil +// 3n+1 = coil +// 3n+2 = end of coil resistor + +class Relay2Elm extends CircuitElm { + final int FLAG_SWAP_COIL = 1; + final int FLAG_SHOW_BOX = 2; + final int FLAG_BOTH_SIDES_COIL = 4; + int[] voltSources = new int[2]; + double inductance; + Inductor ind; + Inductor ind2; + double r_on, r_off, onCurrent, offCurrent; + Point coilPosts[], coilLeads[], swposts[][], swpoles[][], ptSwitch[]; + Point lines[]; + Point outline[] = newPointArray(4); + double coilCurrent, switchCurrent[], coilCurCount, switchCurCount[]; + double coilCurrent2; + // fractional position, between 0 and 1 inclusive + double d_position; + double f_position; + + // integer position, can be 0 (off), 1 (on), 2 (in between) + int i_position; + + double coilR; + + // time to switch in seconds, or 0 for old model where switching time was not constant + double switchingTime; + + int poleCount; + int openhs; + boolean onState; + final int nSwitch0 = 0; + final int nSwitch1 = 1; + final int nSwitch2 = 2; + int nCoil1, nCoil2, nCoil3; + int nCoil4, nCoil5, nCoil6; + double currentOffset1, currentOffset2; + + public Relay2Elm(int xx, int yy) { + super(xx, yy); + ind = new Inductor(sim); + ind2 = new Inductor(sim); + inductance = .2; + ind.setup(inductance, 0, Inductor.FLAG_BACK_EULER); + ind2.setup(inductance, 0, Inductor.FLAG_BACK_EULER); + noDiagonal = true; + onCurrent = .02; + offCurrent = .015; + r_on = .05; + r_off = 1e6; + coilR = 20; + switchingTime = 5e-3; + coilCurrent = coilCurCount = 0; + poleCount = 1; + flags |= FLAG_SHOW_BOX | FLAG_BOTH_SIDES_COIL; + setupPoles(); + } + + public Relay2Elm(int xa, int ya, int xb, int yb, int f, + StringTokenizer st) { + super(xa, ya, xb, yb, f); + poleCount = new Integer(st.nextToken()).intValue(); + inductance = new Double(st.nextToken()).doubleValue(); + coilCurrent = new Double(st.nextToken()).doubleValue(); + r_on = new Double(st.nextToken()).doubleValue(); + r_off = new Double(st.nextToken()).doubleValue(); + onCurrent = new Double(st.nextToken()).doubleValue(); + coilR = new Double(st.nextToken()).doubleValue(); + try { + offCurrent = onCurrent; + switchingTime = 0; + offCurrent = new Double(st.nextToken()).doubleValue(); + switchingTime = Double.parseDouble(st.nextToken()); + d_position = i_position = Integer.parseInt(st.nextToken()); + } catch (Exception e) { + } + if (i_position == 1) + onState = true; + // intermediate state? + if (i_position == 2) + d_position = .5; + noDiagonal = true; + ind = new Inductor(sim); + ind.setup(inductance, coilCurrent, Inductor.FLAG_BACK_EULER); + + ind2 = new Inductor(sim); + ind2.setup(inductance, coilCurrent, Inductor.FLAG_BACK_EULER); + setupPoles(); + allocNodes(); + } + + void setupPoles() { + nCoil1 = 3 * poleCount; + nCoil2 = nCoil1 + 1; + nCoil3 = nCoil1 + 2; + + nCoil4 = nCoil1 + 3; + nCoil5 = nCoil1 + 4; + nCoil6 = nCoil1 + 5; + + + if (switchCurrent == null || switchCurrent.length != poleCount) { + switchCurrent = new double[poleCount]; + switchCurCount = new double[poleCount]; + } + } + + int getDumpType() { + return 425; + } + + String dump() { + return super.dump() + " " + poleCount + " " + + inductance + " " + coilCurrent + " " + + r_on + " " + r_off + " " + onCurrent + " " + coilR + " " + offCurrent + " " + switchingTime + " " + i_position; + } + + void draw(Graphics g) { + int i, p; + for (i = 0; i != 4; i++) { + setVoltageColor(g, volts[nCoil1 + i]); + drawThickLine(g, coilLeads[i], coilPosts[i]); +// Point point1 =new Point(); +// interpPoint(coilLeads[i], coilPosts[i], point1, .5, +// -10); + } + int x = ((flags & FLAG_SWAP_COIL) != 0) ? 1 : 0; + int x1 = ((flags & FLAG_SWAP_COIL) != 0) ? 3 : 2; + setPowerColor(g, coilCurrent * (volts[nCoil1] - volts[nCoil2])); + drawCoil(g, dsign * 6, coilLeads[x], coilLeads[1 - x], + volts[nCoil1 + x], volts[nCoil2 - x]); + + drawCoil(g, dsign * 6, coilLeads[x1], coilLeads[5 - x1], + volts[nCoil1 + x1], volts[nCoil2 - x1]); + // draw rectangle + if ((flags & FLAG_SHOW_BOX) != 0) { + g.setColor(needsHighlight() ? selectColor : lightGrayColor); + drawThickLine(g, outline[0], outline[1]); + drawThickLine(g, outline[1], outline[2]); + drawThickLine(g, outline[2], outline[3]); + drawThickLine(g, outline[3], outline[0]); + } + + // draw lines + g.setColor(Color.darkGray); + for (i = 0; i != poleCount; i++) { + if (i == 0) { + int off = ((flags & FLAG_BOTH_SIDES_COIL) == 0) ? 0 : 4; + interpPoint(point1, point2, lines[i * 2], .5, + openhs * 2 + 5 * dsign - i * openhs * 3 + off); + } else + interpPoint(point1, point2, lines[i * 2], .5, + (int) (openhs * (-i * 3 + 3 - .5 + d_position)) + 5 * dsign); + interpPoint(point1, point2, lines[i * 2 + 1], .5, + (int) (openhs * (-i * 3 - .5 + d_position)) - 5 * dsign); + g.setLineDash(4, 4); + g.drawLine(lines[i * 2].x, lines[i * 2].y, lines[i * 2 + 1].x, lines[i * 2 + 1].y); + g.setLineDash(0, 0); + } + + for (p = 0; p != poleCount; p++) { + int po = p * 3; + for (i = 0; i != 3; i++) { + // draw lead + setVoltageColor(g, volts[nSwitch0 + po + i]); + drawThickLine(g, swposts[p][i], swpoles[p][i]); + } + + interpPoint(swpoles[p][1], swpoles[p][2], ptSwitch[p], d_position); + //setVoltageColor(g, volts[nSwitch0]); + g.setColor(Color.lightGray); + drawThickLine(g, swpoles[p][0], ptSwitch[p]); + switchCurCount[p] = updateDotCount(switchCurrent[p], + switchCurCount[p]); + drawDots(g, swposts[p][0], swpoles[p][0], switchCurCount[p]); + + if (i_position != 2) + drawDots(g, swpoles[p][i_position + 1], swposts[p][i_position + 1], + switchCurCount[p]); + } + + coilCurCount = updateDotCount(coilCurrent, coilCurCount); + + if (coilCurCount != 0) { + drawDots(g, coilPosts[0], coilLeads[0], coilCurCount); + + drawDots(g, coilLeads[0], coilLeads[1], addCurCount(coilCurCount, currentOffset1)); + drawDots(g, coilLeads[1], coilPosts[1], addCurCount(coilCurCount, currentOffset2)); + +// drawDots(g, coilLeads[2], coilPosts[2], coilCurCount); +// drawDots(g, coilLeads[2], coilPosts[3], addCurCount(coilCurCount, currentOffset1)); +// drawDots(g, coilLeads[3], coilPosts[3], addCurCount(coilCurCount, currentOffset1)); + } + + drawPosts(g); + setBbox(outline[0], outline[2], 0); + adjustBbox(coilPosts[0], coilPosts[1]); + adjustBbox(coilPosts[2], coilPosts[3]); + adjustBbox(swposts[0][0], swposts[0][1]); + } + + double getCurrentIntoNode(int n) { + if (n < 3 * poleCount) { + int p = n / 3; + int k = n % 3; + if (k == 0) + return -switchCurrent[p]; + if (k == 1 + i_position) + return switchCurrent[p]; + return 0; + } + if (n == 3 * poleCount) + return -coilCurrent; + if (n < 3 * poleCount + 2) + return -coilCurrent; + if (n == 3 * poleCount + 2) + return -coilCurrent2; + return coilCurrent2; + } + + void setPoints() { + super.setPoints(); + setupPoles(); + allocNodes(); + openhs = -dsign * 16; + + // switch + calcLeads(32); + swposts = new Point[poleCount][3]; + swpoles = new Point[poleCount][3]; + int i, j; + for (i = 0; i != poleCount; i++) { + for (j = 0; j != 3; j++) { + swposts[i][j] = new Point(); + swpoles[i][j] = new Point(); + } + interpPoint(lead1, lead2, swpoles[i][0], 0, -openhs * 3 * i); + interpPoint(lead1, lead2, swpoles[i][1], 1, -openhs * 3 * i - openhs); + interpPoint(lead1, lead2, swpoles[i][2], 1, -openhs * 3 * i + openhs); + interpPoint(point1, point2, swposts[i][0], 0, -openhs * 3 * i); + interpPoint(point1, point2, swposts[i][1], 1, -openhs * 3 * i - openhs); + interpPoint(point1, point2, swposts[i][2], 1, -openhs * 3 * i + openhs); + } + + // coil + coilPosts = newPointArray(4); + coilLeads = newPointArray(4); + ptSwitch = newPointArray(poleCount); + + int x = ((flags & FLAG_SWAP_COIL) != 0) ? 1 : 0; + int boxSize; + if ((flags & FLAG_BOTH_SIDES_COIL) == 0) { + interpPoint(point1, point2, coilPosts[0], x, openhs * 2); + interpPoint(point1, point2, coilPosts[1], x, openhs * 3); +// interpPoint(point1, point2, coilLeads[0], .5, openhs*2); +// interpPoint(point1, point2, coilLeads[1], .5, openhs*3); + interpPoint(point1, point2, coilLeads[2], .5, openhs * 4); + interpPoint(point1, point2, coilLeads[3], .5, openhs * 5); + boxSize = 56; + } else { + interpPoint(point1, point2, coilPosts[0], 0, openhs * 2); + interpPoint(point1, point2, coilPosts[1], 1, openhs * 2); + interpPoint(point1, point2, coilLeads[0], .5 - 16 / dn, openhs * 2); + interpPoint(point1, point2, coilLeads[1], .5 + 16 / dn, openhs * 2); + + interpPoint(point1, point2, coilPosts[2], 0, openhs * 3); + interpPoint(point1, point2, coilPosts[3], 1, openhs * 3); + interpPoint(point1, point2, coilLeads[2], .5 - 16 / dn, openhs * 3); + interpPoint(point1, point2, coilLeads[3], .5 + 16 / dn, openhs * 3); + boxSize = 60; + } + + // lines + lines = newPointArray(poleCount * 2); + + // outline + double boxWScale = Math.min(0.4, 25.0 / dn); + interpPoint(point1, point2, outline[0], 0.5 - boxWScale, -boxSize * dsign); + interpPoint(point1, point2, outline[1], 0.5 + boxWScale, -boxSize * dsign); + interpPoint(point1, point2, outline[2], 0.5 + boxWScale, -(openhs * 3 * poleCount) - (24.0 * dsign)); + interpPoint(point1, point2, outline[3], 0.5 - boxWScale, -(openhs * 3 * poleCount) - (24.0 * dsign)); + + currentOffset1 = distance(coilPosts[0], coilLeads[0]); + currentOffset2 = currentOffset1 + distance(coilLeads[0], coilLeads[1]); + } + + Point getPost(int n) { + if (n < 3 * poleCount) + return swposts[n / 3][n % 3]; + return coilPosts[n - 3 * poleCount]; + } + + int getPostCount() { + return 4 + poleCount * 3; + } + + int getInternalNodeCount() { + return 2; + } + + void reset() { + super.reset(); + ind.reset(); + ind2.reset(); + coilCurrent = coilCurrent2 = coilCurCount = 0; + int i; + for (i = 0; i != poleCount; i++) + switchCurrent[i] = switchCurCount[i] = 0; + f_position= d_position = i_position = 0; + + // preserve onState because if we don't, Relay Flip-Flop gets left in a weird state on reset. + // onState = false; + } + + double a1, a2, a3, a4; + + void stamp() { +// // inductor from coil post 1 to internal node +// ind.stamp(nodes[nCoil1], nodes[nCoil3]); +// // resistor from internal node to coil post 2 +// sim.stampResistor(nodes[nCoil3], nodes[nCoil2], coilR); + + // inductor from coil post 1 to internal node + ind.stamp(nodes[nCoil1], nodes[nCoil6]); + sim.stampResistor(nodes[nCoil6], nodes[nCoil2], coilR); + + ind2.stamp(nodes[nCoil3], nodes[nCoil5]); + sim.stampResistor(nodes[nCoil5], nodes[nCoil4], coilR); + // resistor from internal node to coil post 2 +// sim.stampResistor(nodes[nCoil3], nodes[nCoil2], coilR); + + + int i; + for (i = 0; i != poleCount * 3; i++) + sim.stampNonLinear(nodes[nSwitch0 + i]); + } + + void startIteration() { + // using old model? + if (switchingTime == 0) { + startIterationOld(); + return; + } + + ind.startIteration(volts[nCoil1] - volts[nCoil6]); + ind2.startIteration(volts[nCoil3] - volts[nCoil5]); + double absCurrent = Math.abs(coilCurrent); + double absCurrent2 = Math.abs(coilCurrent2); + + if (onState) { + // on or turning on. check if we need to turn off + if (absCurrent < offCurrent && absCurrent2 < offCurrent) { + // turning off, set switch to intermediate position + onState = false; + i_position = 1; + } else { +// d_position += sim.timeStep / switchingTime; +// if (d_position >= 1) +// d_position = i_position = 1; + d_position = f_position = i_position = 1; + } + } else { + // off or turning off. check if we need to turn on + if (absCurrent > onCurrent || absCurrent2 > onCurrent) { + // turning on, set switch to intermediate position + onState = true; + i_position = 1; + } else { + f_position -= sim.timeStep / switchingTime; + if (f_position <= 0) + f_position= d_position = i_position = 0; + } + + } + } + + void startIterationOld() { + ind.startIteration(volts[nCoil1] - volts[nCoil3]); + + // magic value to balance operate speed with reset speed not at all realistically + double magic = 1.3; + double pmult = Math.sqrt(magic + 1); + double c = onCurrent; + double p = coilCurrent * pmult / c; + d_position = Math.abs(p * p) - 1.3; + if (d_position < 0) + d_position = 0; + if (d_position > 1) + d_position = 1; + if (d_position < .1) + i_position = 0; + else if (d_position > .9) + i_position = 1; + else + i_position = 2; + //System.out.println("ind " + this + " " + current + " " + voltdiff); + } + + // we need this to be able to change the matrix for each step + boolean nonLinear() { + return true; + } + + void doStep() { +// double voltdiff = volts[nCoil1]-volts[nCoil3]; +// ind.doStep(voltdiff); + + double voltdiff = volts[nCoil1] - volts[nCoil6]; + double voltdiff2 = volts[nCoil3] - volts[nCoil5]; + + ind.doStep(voltdiff); + ind2.doStep(voltdiff2); + int p; + for (p = 0; p != poleCount * 3; p += 3) { + sim.stampResistor(nodes[nSwitch0 + p], nodes[nSwitch1 + p], + i_position == 0 ? r_on : r_off); + sim.stampResistor(nodes[nSwitch0 + p], nodes[nSwitch2 + p], + i_position == 1 ? r_on : r_off); + } + } + + void calculateCurrent() { + + double voltdiff = volts[nCoil1] - volts[nCoil6]; + double voltdiff2 = volts[nCoil3] - volts[nCoil5]; + + coilCurrent = ind.calculateCurrent(voltdiff); + coilCurrent2 = ind2.calculateCurrent(voltdiff2); + // actually this isn't correct, since there is a small amount + // of current through the switch when off + int p; + for (p = 0; p != poleCount; p++) { + if (i_position == 2) + switchCurrent[p] = 0; + else + switchCurrent[p] = + (volts[nSwitch0 + p * 3] - volts[nSwitch1 + p * 3 + i_position]) / r_on; + } + } + + void getInfo(String arr[]) { + arr[0] = Locale.LS("relay"); + if (i_position == 0) + arr[0] += " (" + Locale.LS("off") + ")"; + else if (i_position == 1) + arr[0] += " (" + Locale.LS("on") + ")"; + if (switchingTime == 0) + arr[0] += " (" + Locale.LS("old model") + ")"; + int i; + int ln = 1; + for (i = 0; i != poleCount; i++) + arr[ln++] = "I" + (i + 1) + " = " + getCurrentDText(switchCurrent[i]); + arr[ln++] = Locale.LS("coil I") + " = " + getCurrentDText(coilCurrent); + arr[ln++] = Locale.LS("coil Vd") + " = " + + getVoltageDText(volts[nCoil1] - volts[nCoil2]); + } + + public EditInfo getEditInfo(int n) { + if (n == 0) + return new EditInfo("Inductance (H)", inductance, 0, 0); + 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); + if (n == 3) + return new EditInfo("On Current (A)", onCurrent, 0, 0); + if (n == 4) { + if (switchingTime == 0) { + // still using old model, so hide off current which won't work. + // make button to switch to new model + EditInfo ei = new EditInfo("", 0, -1, -1); + ei.button = new Button(Locale.LS("Use New Model")); + return ei; + } + return new EditInfo("Off Current (A)", offCurrent, 0, 0); + } + if (n == 5) + return new EditInfo("Number of Poles", poleCount, 1, 4). + setDimensionless(); + if (n == 6) + return new EditInfo("Coil Resistance (ohms)", coilR, 0, 0); + if (n == 7) { + int style = 1; + if ((flags & FLAG_SWAP_COIL) != 0) + style = 2; + else if ((flags & FLAG_BOTH_SIDES_COIL) != 0) + style = 0; + EditInfo ei = new EditInfo("Coil Style", style, -1, -1); + ei.choice = new Choice(); + ei.choice.add("Both Sides"); + ei.choice.add("Side 1"); + ei.choice.add("Side 2"); + ei.choice.select(style); + return ei; + } + if (n == 8) { + EditInfo ei = new EditInfo("", 0, -1, -1); + ei.checkbox = new Checkbox("Show Box", (flags & FLAG_SHOW_BOX) != 0); + return ei; + } + + // show switching time only for new model, since it is meaningless for old one + if (n == 9 && switchingTime > 0) + return new EditInfo("Switching Time (s)", switchingTime, 0, 0); + return null; + } + + public void setEditValue(int n, EditInfo ei) { + if (n == 0 && ei.value > 0) { + inductance = ei.value; + ind.setup(inductance, coilCurrent, Inductor.FLAG_BACK_EULER); + } + if (n == 1 && ei.value > 0) + r_on = ei.value; + if (n == 2 && ei.value > 0) + r_off = ei.value; + if (n == 3 && ei.value > 0) + onCurrent = ei.value; + if (n == 4) { + // this could be a button or a text box for off current + if (ei.button != null) { + // upgrading to new model + switchingTime = 5e-3; + ei.newDialog = true; + } else if (ei.value > 0) + offCurrent = ei.value; + } + if (n == 5 && ei.value >= 1) { + poleCount = (int) ei.value; + setPoints(); + } + if (n == 6 && ei.value > 0) + coilR = ei.value; + if (n == 7) { + int style = ei.choice.getSelectedIndex(); + final int styles[] = {FLAG_BOTH_SIDES_COIL, 0, FLAG_SWAP_COIL}; + flags &= ~(FLAG_SWAP_COIL | FLAG_BOTH_SIDES_COIL); + flags |= styles[style]; + setPoints(); + } + if (n == 8) + flags = ei.changeFlag(flags, FLAG_SHOW_BOX); + if (n == 9 && ei.value > 0) + switchingTime = ei.value; + } + + boolean comparePairNew(int a1, int a2, int b1, int b2) { + return ((a1 == b1 && a2 == b2) || (a1 == b2 && a2 == b1)); + } + + boolean getConnection(int n1, int n2) { + if (comparePairNew(n1, n2, 3 * poleCount, 3 * poleCount + 1)) + return true; + if (comparePairNew(n1, n2, 3 * poleCount + 2, 3 * poleCount + 3)) + return true; + if (n1 >= 3 * poleCount || n2 >= 3 * poleCount) { + return false; + } + return (n1 / 3 == n2 / 3); + } + +// int getShortcut() { +// return 999; +// } +} + diff --git a/src/main/java/com/lushprojects/circuitjs1/public/locale_zh.txt b/src/main/java/com/lushprojects/circuitjs1/public/locale_zh.txt index d230f27..e9f92f5 100644 --- a/src/main/java/com/lushprojects/circuitjs1/public/locale_zh.txt +++ b/src/main/java/com/lushprojects/circuitjs1/public/locale_zh.txt @@ -141,6 +141,7 @@ "Add Potentiometer"="添加可变电阻" "Add Push Switch"="添加按钮开关" "Add Relay"="添加继电器" +"Add Double Coil Relay"="添加双线圈继电器" "Add Resistor"="添加电阻器" "Add SCR"="添加可控硅" "Add SIPO shift register"="添加SIPO移位寄存器" From 3422802b56833c66e7f1908a1483112b1fff6fc1 Mon Sep 17 00:00:00 2001 From: 11 <11> Date: Tue, 27 Feb 2024 15:21:12 +0800 Subject: [PATCH 5/5] Code Style: Indentation settings --- .../circuitjs1/client/ACRailElm.java | 16 +- .../circuitjs1/client/ACVoltageElm.java | 11 +- .../circuitjs1/client/ADCElm.java | 85 +- .../lushprojects/circuitjs1/client/AMElm.java | 193 +- .../circuitjs1/client/AboutBox.java | 60 +- .../circuitjs1/client/Adjustable.java | 223 +- .../circuitjs1/client/AmmeterElm.java | 200 +- .../circuitjs1/client/AnalogSwitch2Elm.java | 150 +- .../circuitjs1/client/AnalogSwitchElm.java | 222 +- .../circuitjs1/client/AndGateElm.java | 137 +- .../circuitjs1/client/AntennaElm.java | 76 +- .../circuitjs1/client/AudioInputElm.java | 289 +- .../circuitjs1/client/AudioOutputElm.java | 486 +- .../circuitjs1/client/BoxElm.java | 66 +- .../circuitjs1/client/CC2Elm.java | 140 +- .../circuitjs1/client/CCCSElm.java | 396 +- .../circuitjs1/client/CCVSElm.java | 390 +- .../circuitjs1/client/CapacitorElm.java | 402 +- .../circuitjs1/client/Checkbox.java | 36 +- .../client/CheckboxAlignedMenuItem.java | 8 +- .../circuitjs1/client/CheckboxMenuItem.java | 143 +- .../circuitjs1/client/ChipElm.java | 1165 +- .../circuitjs1/client/Choice.java | 24 +- .../circuitjs1/client/CirSim.java | 9661 +++++++++-------- .../circuitjs1/client/CircuitElm.java | 1646 +-- .../circuitjs1/client/CircuitNode.java | 7 +- .../circuitjs1/client/CircuitNodeLink.java | 8 +- .../circuitjs1/client/ClockElm.java | 26 +- .../lushprojects/circuitjs1/client/Color.java | 48 +- .../circuitjs1/client/ComparatorElm.java | 150 +- .../circuitjs1/client/CompositeElm.java | 544 +- .../circuitjs1/client/Counter2Elm.java | 261 +- .../circuitjs1/client/CounterElm.java | 293 +- .../circuitjs1/client/CrystalElm.java | 274 +- .../circuitjs1/client/CurrentElm.java | 186 +- .../client/CustomCompositeChipElm.java | 89 +- .../circuitjs1/client/CustomCompositeElm.java | 266 +- .../client/CustomCompositeModel.java | 271 +- .../circuitjs1/client/CustomLogicElm.java | 410 +- .../circuitjs1/client/CustomLogicModel.java | 358 +- .../client/CustomTransformerElm.java | 882 +- .../circuitjs1/client/DACElm.java | 82 +- .../circuitjs1/client/DCMotorElm.java | 332 +- .../circuitjs1/client/DCVoltageElm.java | 16 +- .../circuitjs1/client/DFlipFlopElm.java | 247 +- .../circuitjs1/client/DarlingtonElm.java | 206 +- .../circuitjs1/client/DataInputElm.java | 320 +- .../circuitjs1/client/DataRecorderElm.java | 213 +- .../circuitjs1/client/DeMultiplexerElm.java | 149 +- .../circuitjs1/client/DecimalDisplayElm.java | 90 +- .../circuitjs1/client/DelayBufferElm.java | 256 +- .../circuitjs1/client/DiacElm.java | 223 +- .../circuitjs1/client/Dialog.java | 49 +- .../lushprojects/circuitjs1/client/Diode.java | 281 +- .../circuitjs1/client/DiodeElm.java | 278 +- .../circuitjs1/client/DiodeModel.java | 469 +- .../client/EditCompositeModelDialog.java | 589 +- .../circuitjs1/client/EditDialog.java | 546 +- .../circuitjs1/client/EditDialogLoadFile.java | 50 +- .../client/EditDiodeModelDialog.java | 34 +- .../circuitjs1/client/EditInfo.java | 129 +- .../circuitjs1/client/EditOptions.java | 306 +- .../client/EditTransistorModelDialog.java | 28 +- .../client/ExportAsImageDialog.java | 71 +- .../client/ExportAsLocalFileDialog.java | 162 +- .../circuitjs1/client/ExportAsTextDialog.java | 126 +- .../circuitjs1/client/ExportAsUrlDialog.java | 200 +- .../lushprojects/circuitjs1/client/Expr.java | 839 +- .../circuitjs1/client/ExtVoltageElm.java | 106 +- .../lushprojects/circuitjs1/client/FFT.java | 96 +- .../lushprojects/circuitjs1/client/FMElm.java | 219 +- .../lushprojects/circuitjs1/client/Font.java | 29 +- .../circuitjs1/client/FullAdderElm.java | 156 +- .../circuitjs1/client/FuseElm.java | 308 +- .../circuitjs1/client/GateElm.java | 512 +- .../circuitjs1/client/GraphicElm.java | 17 +- .../circuitjs1/client/Graphics.java | 321 +- .../circuitjs1/client/GroundElm.java | 270 +- .../circuitjs1/client/HalfAdderElm.java | 82 +- .../circuitjs1/client/HelpDialog.java | 108 +- .../circuitjs1/client/ImportFromDropbox.java | 34 +- .../client/ImportFromDropboxDialog.java | 179 +- .../client/ImportFromTextDialog.java | 84 +- .../circuitjs1/client/Inductor.java | 94 +- .../circuitjs1/client/InductorElm.java | 233 +- .../circuitjs1/client/InverterElm.java | 260 +- .../client/InvertingSchmittElm.java | 365 +- .../circuitjs1/client/JKFlipFlopElm.java | 266 +- .../circuitjs1/client/JfetElm.java | 267 +- .../circuitjs1/client/LDRElm.java | 277 +- .../circuitjs1/client/LEDArrayElm.java | 347 +- .../circuitjs1/client/LEDElm.java | 240 +- .../circuitjs1/client/LabeledNodeElm.java | 221 +- .../circuitjs1/client/LampElm.java | 379 +- .../circuitjs1/client/LatchElm.java | 111 +- .../circuitjs1/client/LicenseDialog.java | 48 +- .../circuitjs1/client/LineElm.java | 48 +- .../circuitjs1/client/LoadFile.java | 69 +- .../circuitjs1/client/LogicInputElm.java | 302 +- .../circuitjs1/client/LogicOutputElm.java | 258 +- .../circuitjs1/client/MBBSwitchElm.java | 363 +- .../circuitjs1/client/MemristorElm.java | 221 +- .../circuitjs1/client/MonostableElm.java | 186 +- .../circuitjs1/client/MosfetElm.java | 1068 +- .../circuitjs1/client/MultiplexerElm.java | 246 +- .../circuitjs1/client/MyCommand.java | 22 +- .../circuitjs1/client/NDarlingtonElm.java | 5 +- .../circuitjs1/client/NJfetElm.java | 22 +- .../circuitjs1/client/NMosfetElm.java | 16 +- .../circuitjs1/client/NTransistorElm.java | 19 +- .../circuitjs1/client/NandGateElm.java | 35 +- .../circuitjs1/client/NoiseElm.java | 25 +- .../circuitjs1/client/NorGateElm.java | 35 +- .../circuitjs1/client/OTAElm.java | 226 +- .../circuitjs1/client/OhmMeterElm.java | 118 +- .../circuitjs1/client/OpAmpElm.java | 421 +- .../circuitjs1/client/OpAmpRealElm.java | 332 +- .../circuitjs1/client/OpAmpSwapElm.java | 20 +- .../circuitjs1/client/OptocouplerElm.java | 143 +- .../circuitjs1/client/OrGateElm.java | 198 +- .../circuitjs1/client/OutputElm.java | 201 +- .../circuitjs1/client/PDarlingtonElm.java | 5 +- .../circuitjs1/client/PMosfetElm.java | 16 +- .../circuitjs1/client/PTransistorElm.java | 19 +- .../circuitjs1/client/PhaseCompElm.java | 100 +- .../circuitjs1/client/PisoShiftElm.java | 267 +- .../lushprojects/circuitjs1/client/Point.java | 78 +- .../circuitjs1/client/PolarCapacitorElm.java | 140 +- .../circuitjs1/client/Polygon.java | 36 +- .../circuitjs1/client/PotElm.java | 526 +- .../circuitjs1/client/ProbeElm.java | 397 +- .../circuitjs1/client/PushSwitchElm.java | 16 +- .../circuitjs1/client/QueryParameters.java | 35 +- .../circuitjs1/client/RailElm.java | 149 +- .../circuitjs1/client/Rectangle.java | 56 +- .../circuitjs1/client/Relay2Elm.java | 8 +- .../circuitjs1/client/RelayElm.java | 828 +- .../circuitjs1/client/ResistorElm.java | 217 +- .../circuitjs1/client/RingCounterElm.java | 243 +- .../circuitjs1/client/RowInfo.java | 21 +- .../circuitjs1/client/SCRElm.java | 374 +- .../circuitjs1/client/SRAMElm.java | 469 +- .../circuitjs1/client/SRAMLoadFile.java | 16 +- .../circuitjs1/client/SchmittElm.java | 145 +- .../lushprojects/circuitjs1/client/Scope.java | 3528 +++--- .../circuitjs1/client/ScopeElm.java | 155 +- .../circuitjs1/client/ScopePopupMenu.java | 54 +- .../client/ScopePropertiesDialog.java | 1243 ++- .../circuitjs1/client/ScrollValuePopup.java | 318 +- .../circuitjs1/client/Scrollbar.java | 521 +- .../circuitjs1/client/SearchDialog.java | 204 +- .../circuitjs1/client/SeqGenElm.java | 363 +- .../circuitjs1/client/SevenSegDecoderElm.java | 239 +- .../circuitjs1/client/SevenSegElm.java | 722 +- .../circuitjs1/client/ShortcutsDialog.java | 224 +- .../circuitjs1/client/SipoShiftElm.java | 175 +- .../circuitjs1/client/SliderDialog.java | 406 +- .../circuitjs1/client/SparkGapElm.java | 173 +- .../circuitjs1/client/SquareRailElm.java | 16 +- .../circuitjs1/client/StopTriggerElm.java | 200 +- .../circuitjs1/client/StringTokenizer.java | 397 +- .../circuitjs1/client/SweepElm.java | 356 +- .../circuitjs1/client/Switch2Elm.java | 347 +- .../circuitjs1/client/SwitchElm.java | 182 +- .../circuitjs1/client/TFlipFlopElm.java | 210 +- .../client/TappedTransformerElm.java | 555 +- .../circuitjs1/client/TestPointElm.java | 342 +- .../circuitjs1/client/TextElm.java | 229 +- .../circuitjs1/client/ThermistorNTCElm.java | 312 +- .../circuitjs1/client/TimeDelayRelayElm.java | 218 +- .../circuitjs1/client/TimerElm.java | 216 +- .../circuitjs1/client/TransLineElm.java | 370 +- .../circuitjs1/client/TransformerElm.java | 579 +- .../circuitjs1/client/TransistorElm.java | 1062 +- .../circuitjs1/client/TransistorModel.java | 322 +- .../circuitjs1/client/TriStateElm.java | 219 +- .../circuitjs1/client/TriacElm.java | 347 +- .../circuitjs1/client/TriodeElm.java | 370 +- .../circuitjs1/client/TunnelDiodeElm.java | 164 +- .../circuitjs1/client/UnijunctionElm.java | 239 +- .../circuitjs1/client/VCCSElm.java | 395 +- .../circuitjs1/client/VCOElm.java | 196 +- .../circuitjs1/client/VCVSElm.java | 186 +- .../circuitjs1/client/VarRailElm.java | 160 +- .../circuitjs1/client/VaractorElm.java | 190 +- .../circuitjs1/client/VoltageElm.java | 643 +- .../circuitjs1/client/WattmeterElm.java | 253 +- .../circuitjs1/client/WireElm.java | 169 +- .../circuitjs1/client/XorGateElm.java | 65 +- .../circuitjs1/client/ZenerElm.java | 106 +- .../circuitjs1/client/circuitjs1.java | 8 +- 191 files changed, 30878 insertions(+), 28048 deletions(-) diff --git a/src/main/java/com/lushprojects/circuitjs1/client/ACRailElm.java b/src/main/java/com/lushprojects/circuitjs1/client/ACRailElm.java index a32c294..024f02f 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/ACRailElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/ACRailElm.java @@ -19,8 +19,16 @@ package com.lushprojects.circuitjs1.client; -class ACRailElm extends RailElm { - public ACRailElm(int xx, int yy) { super(xx, yy, WF_AC); } - Class getDumpClass() { return RailElm.class; } - int getShortcut() { return 0; } +public class ACRailElm extends RailElm { + public ACRailElm(int xx, int yy) { + super(xx, yy, WF_AC); } + + Class getDumpClass() { + return RailElm.class; + } + + int getShortcut() { + return 0; + } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/ACVoltageElm.java b/src/main/java/com/lushprojects/circuitjs1/client/ACVoltageElm.java index aa3ddf0..d0b75cb 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/ACVoltageElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/ACVoltageElm.java @@ -19,7 +19,12 @@ package com.lushprojects.circuitjs1.client; -class ACVoltageElm extends VoltageElm { - public ACVoltageElm(int xx, int yy) { super(xx, yy, WF_AC); } - Class getDumpClass() { return VoltageElm.class; } +public class ACVoltageElm extends VoltageElm { + public ACVoltageElm(int xx, int yy) { + super(xx, yy, WF_AC); } + + Class getDumpClass() { + return VoltageElm.class; + } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/ADCElm.java b/src/main/java/com/lushprojects/circuitjs1/client/ADCElm.java index 8d1d1e4..3e9772f 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/ADCElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/ADCElm.java @@ -19,51 +19,76 @@ package com.lushprojects.circuitjs1.client; -class ADCElm extends ChipElm { - public ADCElm(int xx, int yy) { super(xx, yy); } +public 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); + StringTokenizer st) { + super(xa, ya, xb, yb, f, st); } - String getChipName() { return "ADC"; } - boolean needsBits() { return true; } + + 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(); + 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<= 2) { - bits = (int)ei.value; + bits = (int) ei.value; setupPins(); setPoints(); } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/AMElm.java b/src/main/java/com/lushprojects/circuitjs1/client/AMElm.java index d643f2e..f26876d 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/AMElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/AMElm.java @@ -21,30 +21,36 @@ // contributed by Edward Calver -class AMElm extends CircuitElm { +public class AMElm extends CircuitElm { static final int FLAG_COS = 2; - double carrierfreq,signalfreq, maxVoltage, freqTimeZero; + double carrierfreq, signalfreq, maxVoltage, freqTimeZero; + public AMElm(int xx, int yy) { - super(xx, yy); - maxVoltage = 5; - carrierfreq = 1000; - signalfreq=40; - reset(); + super(xx, yy); + maxVoltage = 5; + carrierfreq = 1000; + signalfreq = 40; + reset(); } + public AMElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f); - carrierfreq = new Double(st.nextToken()).doubleValue(); - signalfreq= new Double(st.nextToken()).doubleValue(); - maxVoltage = new Double(st.nextToken()).doubleValue(); - if ((flags & FLAG_COS) != 0) { - flags &= ~FLAG_COS; - } - reset(); - } - int getDumpType() { return 200; } + StringTokenizer st) { + super(xa, ya, xb, yb, f); + carrierfreq = new Double(st.nextToken()).doubleValue(); + signalfreq = new Double(st.nextToken()).doubleValue(); + maxVoltage = new Double(st.nextToken()).doubleValue(); + if ((flags & FLAG_COS) != 0) { + flags &= ~FLAG_COS; + } + reset(); + } + + int getDumpType() { + return 200; + } + String dump() { - return super.dump() + " " +carrierfreq+" " + signalfreq + " " +maxVoltage; + return super.dump() + " " + carrierfreq + " " + signalfreq + " " + maxVoltage; } /*void setCurrent(double c) { current = c; @@ -52,92 +58,109 @@ String dump() { }*/ void reset() { - freqTimeZero = 0; - curcount = 0; + freqTimeZero = 0; + curcount = 0; + } + + int getPostCount() { + return 1; } - int getPostCount() { return 1; } - - void stamp() { - sim.stampVoltageSource(0, nodes[0], voltSource); + + void stamp() { + sim.stampVoltageSource(0, nodes[0], voltSource); } + void doStep() { - sim.updateVoltageSource(0, nodes[0], voltSource, getVoltage()); + sim.updateVoltageSource(0, nodes[0], voltSource, getVoltage()); } + double getVoltage() { - double w = 2*pi*(sim.t-freqTimeZero); - return ((Math.sin(w*signalfreq)+1)/2)*Math.sin(w*carrierfreq)*maxVoltage; + double w = 2 * pi * (sim.t - freqTimeZero); + return ((Math.sin(w * signalfreq) + 1) / 2) * Math.sin(w * carrierfreq) * maxVoltage; } + final int circleSize = 17; void draw(Graphics g) { - setBbox(point1, point2, circleSize); - setVoltageColor(g, volts[0]); - drawThickLine(g, point1, lead1); - - Font f = new Font("SansSerif", 0, 12); - g.setFont(f); - g.setColor(needsHighlight() ? selectColor : whiteColor); - setPowerColor(g, false); - double v = getVoltage(); - String s = "AM"; - drawCenteredText(g, s, x2, y2, true); - drawWaveform(g, point2); - drawPosts(g); - curcount = updateDotCount(-current, curcount); - if (sim.dragElm != this) - drawDots(g, point1, lead1, curcount); - } - + setBbox(point1, point2, circleSize); + setVoltageColor(g, volts[0]); + drawThickLine(g, point1, lead1); + + Font f = new Font("SansSerif", 0, 12); + g.setFont(f); + g.setColor(needsHighlight() ? selectColor : whiteColor); + setPowerColor(g, false); + double v = getVoltage(); + String s = "AM"; + drawCenteredText(g, s, x2, y2, true); + drawWaveform(g, point2); + drawPosts(g); + curcount = updateDotCount(-current, curcount); + if (sim.dragElm != this) + drawDots(g, point1, lead1, curcount); + } + void drawWaveform(Graphics g, Point center) { - g.setColor(needsHighlight() ? selectColor : Color.gray); - setPowerColor(g, false); - int xc = center.x; int yc = center.y; - drawThickCircle(g, xc, yc, circleSize); - int wl = 8; - adjustBbox(xc-circleSize, yc-circleSize, - xc+circleSize, yc+circleSize); + g.setColor(needsHighlight() ? selectColor : Color.gray); + setPowerColor(g, false); + int xc = center.x; + int yc = center.y; + drawThickCircle(g, xc, yc, circleSize); + int wl = 8; + adjustBbox(xc - circleSize, yc - circleSize, + xc + circleSize, yc + circleSize); } - void setPoints() { - super.setPoints(); - lead1 = interpPoint(point1, point2, 1-circleSize/dn); + void setPoints() { + super.setPoints(); + lead1 = interpPoint(point1, point2, 1 - circleSize / dn); } - - double getVoltageDiff() { return volts[0]; } - - boolean hasGroundConnection(int n1) { return true; } - + + double getVoltageDiff() { + return volts[0]; + } + + boolean hasGroundConnection(int n1) { + return true; + } + int getVoltageSourceCount() { - return 1; + return 1; } - double getPower() { return -getVoltageDiff()*current; } + + double getPower() { + return -getVoltageDiff() * current; + } + void getInfo(String arr[]) { - - arr[0] = "AM Source"; - arr[1] = "I = " + getCurrentText(getCurrent()); - arr[2] = "V = " + - getVoltageText(getVoltageDiff()); - arr[3] = "cf = " + getUnitText(carrierfreq, "Hz"); - arr[4] = "sf = " + getUnitText(signalfreq, "Hz"); - arr[5] = "Vmax = " + getVoltageText(maxVoltage); + + arr[0] = "AM Source"; + arr[1] = "I = " + getCurrentText(getCurrent()); + arr[2] = "V = " + + getVoltageText(getVoltageDiff()); + arr[3] = "cf = " + getUnitText(carrierfreq, "Hz"); + arr[4] = "sf = " + getUnitText(signalfreq, "Hz"); + arr[5] = "Vmax = " + getVoltageText(maxVoltage); } + public EditInfo getEditInfo(int n) { - if (n == 0) - return new EditInfo("Max Voltage", maxVoltage, -20, 20); - if (n == 1) - return new EditInfo("Carrier Frequency (Hz)", carrierfreq, 4, 500); - if (n == 2) - return new EditInfo("Signal Frequency (Hz)", signalfreq, 4, 500); - - return null; + if (n == 0) + return new EditInfo("Max Voltage", maxVoltage, -20, 20); + if (n == 1) + return new EditInfo("Carrier Frequency (Hz)", carrierfreq, 4, 500); + if (n == 2) + return new EditInfo("Signal Frequency (Hz)", signalfreq, 4, 500); + + return null; } + public void setEditValue(int n, EditInfo ei) { - if (n == 0) - maxVoltage = ei.value; - if (n == 1) - carrierfreq = ei.value; - if (n == 2) - signalfreq=ei.value; + if (n == 0) + maxVoltage = ei.value; + if (n == 1) + carrierfreq = ei.value; + if (n == 2) + signalfreq = ei.value; } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/AboutBox.java b/src/main/java/com/lushprojects/circuitjs1/client/AboutBox.java index 39618f1..369b00d 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/AboutBox.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/AboutBox.java @@ -28,34 +28,34 @@ import com.google.gwt.user.client.ui.Button; public class AboutBox extends PopupPanel { - - VerticalPanel vp; - Button okButton; - - AboutBox(String version) { - super(); - - // Add versionString variable to SessionStorage for iFrame in AboutBox - Storage sstor = Storage.getSessionStorageIfSupported(); - sstor.setItem("versionString", version); - - vp = new VerticalPanel(); - setWidget(vp); - vp.setWidth("400px"); - vp.add(new HTML("
")); - - - vp.add(okButton = new Button("OK")); - okButton.addClickHandler(new ClickHandler() { - public void onClick(ClickEvent event) { - close(); - } - }); - center(); - show(); - } - - public void close() { - hide(); - } + + VerticalPanel vp; + Button okButton; + + AboutBox(String version) { + super(); + + // Add versionString variable to SessionStorage for iFrame in AboutBox + Storage sstor = Storage.getSessionStorageIfSupported(); + sstor.setItem("versionString", version); + + vp = new VerticalPanel(); + setWidget(vp); + vp.setWidth("400px"); + vp.add(new HTML("
")); + + + vp.add(okButton = new Button("OK")); + okButton.addClickHandler(new ClickHandler() { + public void onClick(ClickEvent event) { + close(); + } + }); + center(); + show(); + } + + public void close() { + hide(); + } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/Adjustable.java b/src/main/java/com/lushprojects/circuitjs1/client/Adjustable.java index c910de3..e80a782 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/Adjustable.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/Adjustable.java @@ -12,25 +12,25 @@ public class Adjustable implements Command { double minValue, maxValue; int flags; String sliderText; - + // null if this Adjustable has its own slider, non-null if it's sharing another one. Adjustable sharedSlider; - + final int FLAG_SHARED = 1; - + // index of value in getEditInfo() list that this slider controls int editItem; - + Label label; Scrollbar slider; boolean settingValue; - + Adjustable(CircuitElm ce, int item) { - minValue = 1; - maxValue = 1000; - flags = 0; - elm = ce; - editItem = item; + minValue = 1; + maxValue = 1000; + flags = 0; + elm = ce; + editItem = item; EditInfo ei = ce.getEditInfo(editItem); if (ei != null && ei.maxVal > 0) { minValue = ei.minVal; @@ -40,138 +40,141 @@ public class Adjustable implements Command { // undump Adjustable(StringTokenizer st, CirSim sim) { - int e = Integer.parseInt(st.nextToken()); - if (e == -1) - return; - try { - String ei = st.nextToken(); - - // forgot to dump a "flags" field in the initial code, so we have to do this to support backward compatibility - if (ei.startsWith("F")) { - flags = Integer.parseInt(ei.substring(1)); - ei = st.nextToken(); - } - - editItem = Integer.parseInt(ei); - minValue = Double.parseDouble(st.nextToken()); - maxValue = Double.parseDouble(st.nextToken()); - if ((flags & FLAG_SHARED) != 0) { - int ano = Integer.parseInt(st.nextToken()); - sharedSlider = ano == -1 ? null : sim.adjustables.get(ano); - } - sliderText = CustomLogicModel.unescape(st.nextToken()); - } catch (Exception ex) {} - try { - elm = sim.getElm(e); - } catch (Exception ex) {} + int e = Integer.parseInt(st.nextToken()); + if (e == -1) + return; + try { + String ei = st.nextToken(); + + // forgot to dump a "flags" field in the initial code, so we have to do this to support backward compatibility + if (ei.startsWith("F")) { + flags = Integer.parseInt(ei.substring(1)); + ei = st.nextToken(); + } + + editItem = Integer.parseInt(ei); + minValue = Double.parseDouble(st.nextToken()); + maxValue = Double.parseDouble(st.nextToken()); + if ((flags & FLAG_SHARED) != 0) { + int ano = Integer.parseInt(st.nextToken()); + sharedSlider = ano == -1 ? null : sim.adjustables.get(ano); + } + sliderText = CustomLogicModel.unescape(st.nextToken()); + } catch (Exception ex) { + } + try { + elm = sim.getElm(e); + } catch (Exception ex) { + } } - + boolean createSlider(CirSim sim) { - if (elm == null) - return false; - EditInfo ei = elm.getEditInfo(editItem); - if (ei == null) - return false; - if (sharedSlider != null) - return true; - if (sliderText.length() == 0) - return false; - double value = ei.value; - createSlider(sim, value); - return true; + if (elm == null) + return false; + EditInfo ei = elm.getEditInfo(editItem); + if (ei == null) + return false; + if (sharedSlider != null) + return true; + if (sliderText.length() == 0) + return false; + double value = ei.value; + createSlider(sim, value); + return true; } void createSlider(CirSim sim, double value) { sim.addWidgetToVerticalPanel(label = new Label(Locale.LS(sliderText))); label.addStyleName("topSpace"); - int intValue = (int) ((value-minValue)*100/(maxValue-minValue)); + int intValue = (int) ((value - minValue) * 100 / (maxValue - minValue)); sim.addWidgetToVerticalPanel(slider = new Scrollbar(Scrollbar.HORIZONTAL, intValue, 1, 0, 101, this, elm)); } void setSliderValue(double value) { - if (sharedSlider != null) { - sharedSlider.setSliderValue(value); - return; - } - int intValue = (int) ((value-minValue)*100/(maxValue-minValue)); + if (sharedSlider != null) { + sharedSlider.setSliderValue(value); + return; + } + int intValue = (int) ((value - minValue) * 100 / (maxValue - minValue)); settingValue = true; // don't recursively set value again in execute() slider.setValue(intValue); settingValue = false; } - + public void execute() { - if (settingValue) - return; - int i; - CirSim sim = CirSim.theSim; - for (i = 0; i != sim.adjustables.size(); i++) { - Adjustable adj = sim.adjustables.get(i); - if (adj == this || adj.sharedSlider == this) - adj.executeSlider(); - } + if (settingValue) + return; + int i; + CirSim sim = CirSim.theSim; + for (i = 0; i != sim.adjustables.size(); i++) { + Adjustable adj = sim.adjustables.get(i); + if (adj == this || adj.sharedSlider == this) + adj.executeSlider(); + } } - + void executeSlider() { - elm.sim.analyzeFlag = true; - EditInfo ei = elm.getEditInfo(editItem); - ei.value = getSliderValue(); - elm.setEditValue(editItem, ei); - elm.sim.repaint(); + elm.sim.analyzeFlag = true; + EditInfo ei = elm.getEditInfo(editItem); + ei.value = getSliderValue(); + elm.setEditValue(editItem, ei); + elm.sim.repaint(); } - + double getSliderValue() { - double val = sharedSlider == null ? slider.getValue() : sharedSlider.slider.getValue(); - return minValue + (maxValue-minValue)*val/100; + double val = sharedSlider == null ? slider.getValue() : sharedSlider.slider.getValue(); + return minValue + (maxValue - minValue) * val / 100; } - + void deleteSlider(CirSim sim) { - try { - sim.removeWidgetFromVerticalPanel(label); - sim.removeWidgetFromVerticalPanel(slider); - } catch (Exception e) {} + try { + sim.removeWidgetFromVerticalPanel(label); + sim.removeWidgetFromVerticalPanel(slider); + } catch (Exception e) { + } } - + void setMouseElm(CircuitElm e) { - if (slider != null) - slider.draw(); + if (slider != null) + slider.draw(); } - + boolean sliderBeingShared() { - int i; - for (i = 0; i != CirSim.theSim.adjustables.size(); i++) { - Adjustable adj = CirSim.theSim.adjustables.get(i); - if (adj.sharedSlider == this) - return true; - } - return false; + int i; + for (i = 0; i != CirSim.theSim.adjustables.size(); i++) { + Adjustable adj = CirSim.theSim.adjustables.get(i); + if (adj.sharedSlider == this) + return true; + } + return false; } - + String dump() { - int ano = -1; - if (sharedSlider != null) - ano = CirSim.theSim.adjustables.indexOf(sharedSlider); - - return elm.sim.locateElm(elm) + " F1 " + editItem + " " + minValue + " " + maxValue + " " + ano + " " + - CustomLogicModel.escape(sliderText); + int ano = -1; + if (sharedSlider != null) + ano = CirSim.theSim.adjustables.indexOf(sharedSlider); + + return elm.sim.locateElm(elm) + " F1 " + editItem + " " + minValue + " " + maxValue + " " + ano + " " + + CustomLogicModel.escape(sliderText); } - + // reorder adjustables so that items with sliders come first in the list, followed by items that reference them. // this simplifies the UI code, and also makes it much easier to dump/undump the adjustables list, since we will // always be undumping the adjustables with sliders first, then the adjustables that reference them. static void reorderAdjustables() { - Vector newList = new Vector(); - Vector oldList = CirSim.theSim.adjustables; - int i; - for (i = 0; i != oldList.size(); i++) { - Adjustable adj = oldList.get(i); - if (adj.sharedSlider == null) - newList.add(adj); - } - for (i = 0; i != oldList.size(); i++) { - Adjustable adj = oldList.get(i); - if (adj.sharedSlider != null) - newList.add(adj); - } - CirSim.theSim.adjustables = newList; + Vector newList = new Vector(); + Vector oldList = CirSim.theSim.adjustables; + int i; + for (i = 0; i != oldList.size(); i++) { + Adjustable adj = oldList.get(i); + if (adj.sharedSlider == null) + newList.add(adj); + } + for (i = 0; i != oldList.size(); i++) { + Adjustable adj = oldList.get(i); + if (adj.sharedSlider != null) + newList.add(adj); + } + CirSim.theSim.adjustables = newList; } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/AmmeterElm.java b/src/main/java/com/lushprojects/circuitjs1/client/AmmeterElm.java index b3ad9cc..b2b8123 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/AmmeterElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/AmmeterElm.java @@ -24,124 +24,132 @@ import com.lushprojects.circuitjs1.client.util.Locale; -class AmmeterElm extends CircuitElm { - - int meter; - int scale; - final int AM_VOL = 0; - final int AM_RMS = 1; - int zerocount=0; - double rmsI=0, total, count; - double maxI=0, lastMaxI; - double minI=0, lastMinI; - double selectedValue=0; - - double currents[]; - boolean increasingI=true, decreasingI=true; - - public AmmeterElm(int xx, int yy) { - super(xx, yy); +public class AmmeterElm extends CircuitElm { + + int meter; + int scale; + final int AM_VOL = 0; + final int AM_RMS = 1; + int zerocount = 0; + double rmsI = 0, total, count; + double maxI = 0, lastMaxI; + double minI = 0, lastMinI; + double selectedValue = 0; + + double currents[]; + boolean increasingI = true, decreasingI = true; + + public AmmeterElm(int xx, int yy) { + super(xx, yy); flags = FLAG_SHOWCURRENT; scale = SCALE_AUTO; } + public AmmeterElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { + StringTokenizer st) { super(xa, ya, xb, yb, f); scale = SCALE_AUTO; meter = Integer.parseInt(st.nextToken()); try { scale = Integer.parseInt(st.nextToken()); - } catch (Exception e) {} + } catch (Exception e) { + } } + String dump() { - return super.dump() + " " + meter + " " + scale; + return super.dump() + " " + meter + " " + scale; } - String getMeter(){ + + String getMeter() { switch (meter) { - case AM_VOL: - return "I"; - case AM_RMS: - return "Irms"; + case AM_VOL: + return "I"; + case AM_RMS: + return "Irms"; } return ""; } - void setPoints(){ + + void setPoints() { super.setPoints(); - mid = interpPoint(point1,point2,0.6); + mid = interpPoint(point1, point2, 0.6); arrowPoly = calcArrow(point1, mid, 14, 7); } + Point mid; static final int FLAG_SHOWCURRENT = 1; - void stepFinished(){ + + void stepFinished() { count++;//how many counts are in a cycle - total += current*current; //sum of squares - if (current>maxI && increasingI){ + total += current * current; //sum of squares + if (current > maxI && increasingI) { maxI = current; increasingI = true; decreasingI = false; } - if (currentminI && decreasingI){ //change of direction I now going up - lastMinI=minI; //capture last minimum + if (current > minI && decreasingI) { //change of direction I now going up + lastMinI = minI; //capture last minimum maxI = current; increasingI = true; decreasingI = false; - + //rms data - total = total/count; + total = total / count; rmsI = Math.sqrt(total); if (Double.isNaN(rmsI)) - rmsI=0; - count=0; - total=0; + rmsI = 0; + count = 0; + total = 0; + - } //need to zero the rms value if it stays at 0 for a while - if (current==0){ + if (current == 0) { zerocount++; - if (zerocount > 5){ - total=0; - rmsI=0; - maxI=0; - minI=0; + if (zerocount > 5) { + total = 0; + rmsI = 0; + maxI = 0; + minI = 0; } - }else{ - zerocount=0; + } else { + zerocount = 0; } switch (meter) { - case AM_VOL: - selectedValue = current; - break; - case AM_RMS: - selectedValue = rmsI; - break; + case AM_VOL: + selectedValue = current; + break; + case AM_RMS: + selectedValue = rmsI; + break; } } - + Polygon arrowPoly; + void draw(Graphics g) { super.draw(g);//BC required for highlighting setVoltageColor(g, volts[0]); @@ -151,25 +159,34 @@ void draw(Graphics g) { setBbox(point1, point2, 3); String s = "A"; switch (meter) { - case AM_VOL: - s = getUnitTextWithScale(getCurrent(), "A", scale); - break; - case AM_RMS: - s = getUnitTextWithScale(rmsI, "A(rms)", scale); - break; + case AM_VOL: + s = getUnitTextWithScale(getCurrent(), "A", scale); + break; + case AM_RMS: + s = getUnitTextWithScale(rmsI, "A(rms)", scale); + break; } drawValues(g, s, 4); drawPosts(g); } - int getDumpType() { return 370; } + + int getDumpType() { + return 370; + } + void stamp() { sim.stampVoltageSource(nodes[0], nodes[1], voltSource, 0); } + boolean mustShowCurrent() { return (flags & FLAG_SHOWCURRENT) != 0; } - int getVoltageSourceCount() { return 1; } + + int getVoltageSourceCount() { + return 1; + } + void getInfo(String arr[]) { arr[0] = "Ammeter"; switch (meter) { @@ -179,18 +196,26 @@ void getInfo(String arr[]) { case AM_RMS: arr[1] = "Irms = " + getUnitText(rmsI, "A"); break; - } + } } - double getPower() { return 0; } - double getVoltageDiff() { return volts[0]; } - + + double getPower() { + return 0; + } + + double getVoltageDiff() { + return volts[0]; + } + // do not optimize out, even though isWireEquivalent() is true // (because we need current calculated every timestep) - boolean isWireEquivalent() { return true; } - + boolean isWireEquivalent() { + return true; + } + public EditInfo getEditInfo(int n) { - if (n==0){ - EditInfo ei = new EditInfo("Value", selectedValue, -1, -1); + if (n == 0) { + EditInfo ei = new EditInfo("Value", selectedValue, -1, -1); ei.choice = new Choice(); ei.choice.add("Current"); ei.choice.add("RMS Current"); @@ -198,7 +223,7 @@ public EditInfo getEditInfo(int n) { return ei; } if (n == 1) { - EditInfo ei = new EditInfo("Scale", 0); + EditInfo ei = new EditInfo("Scale", 0); ei.choice = new Choice(); ei.choice.add("Auto"); ei.choice.add("A"); @@ -209,10 +234,11 @@ public EditInfo getEditInfo(int n) { } return null; } + public void setEditValue(int n, EditInfo ei) { - if (n==0) + if (n == 0) meter = ei.choice.getSelectedIndex(); - if (n==1) + if (n == 1) scale = ei.choice.getSelectedIndex(); } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/AnalogSwitch2Elm.java b/src/main/java/com/lushprojects/circuitjs1/client/AnalogSwitch2Elm.java index 301ea78..2888198 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/AnalogSwitch2Elm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/AnalogSwitch2Elm.java @@ -19,101 +19,111 @@ package com.lushprojects.circuitjs1.client; -class AnalogSwitch2Elm extends AnalogSwitchElm { +public class AnalogSwitch2Elm extends AnalogSwitchElm { public AnalogSwitch2Elm(int xx, int yy) { - super(xx, 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); + 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); + 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; } - 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); + 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]; + return (n == 0) ? point1 : (n == 3) ? ctlPoint : swposts[n - 1]; + } + + int getDumpType() { + return 160; } - int getDumpType() { return 160; } void calculateCurrent() { - if (open) - current = (volts[0]-volts[2])/r_on; - else - current = (volts[0]-volts[1])/r_on; + 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]); + 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); - } + 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; + if (n1 == 3 || n2 == 3) + return false; + return true; } + void getInfo(String arr[]) { - arr[0] = "analog switch (SPDT)"; - arr[1] = "I = " + getCurrentDText(getCurrent()); + arr[0] = "analog switch (SPDT)"; + arr[1] = "I = " + getCurrentDText(getCurrent()); } - + double getCurrentIntoNode(int n) { - if (n == 0) - return -current; - int position = (open) ? 1 : 0; - if (n == position+1) - return current; - return 0; - } + if (n == 0) + return -current; + int position = (open) ? 1 : 0; + if (n == position + 1) + return current; + return 0; + } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/AnalogSwitchElm.java b/src/main/java/com/lushprojects/circuitjs1/client/AnalogSwitchElm.java index 3653847..901ac08 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/AnalogSwitchElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/AnalogSwitchElm.java @@ -19,140 +19,162 @@ package com.lushprojects.circuitjs1.client; -class AnalogSwitchElm extends CircuitElm { +public 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; + 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) { } - + 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; + return super.dump() + " " + r_on + " " + r_off; } - - int getDumpType() { return 159; } + + 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); - } - + 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); + 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; + current = (volts[0] - volts[1]) / resistance; } - + // we need this to be able to change the matrix for each step - boolean nonLinear() { return true; } + boolean nonLinear() { + return true; + } void stamp() { - sim.stampNonLinear(nodes[0]); - sim.stampNonLinear(nodes[1]); + 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); + 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; } + 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; + 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]); + 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; + 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; + 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; + 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; } - + double getCurrentIntoNode(int n) { - if (n==2) - return 0; - if (n==0) - return -current; - return current; - } + if (n == 2) + return 0; + if (n == 0) + return -current; + return current; + } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/AndGateElm.java b/src/main/java/com/lushprojects/circuitjs1/client/AndGateElm.java index dc19916..ea1710d 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/AndGateElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/AndGateElm.java @@ -21,68 +21,83 @@ import com.google.gwt.canvas.dom.client.Context2d; - 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); - } - - String getGateText() { return "&"; } - - public final native void ellipse(Context2d g, double x, double y, double rx, double ry, double ro, double sa, double ea, boolean ccw) /*-{ +public 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); + } + + String getGateText() { + return "&"; + } + + public final native void ellipse(Context2d g, double x, double y, double rx, double ry, double ro, double sa, double ea, boolean ccw) /*-{ g.ellipse(x, y, rx, ry, ro, sa, ea, ccw); }-*/; - void drawGatePolygon(Graphics g) { - g.setLineWidth(3.0); - g.context.beginPath(); - g.context.moveTo(gatePoly.xpoints[0], gatePoly.ypoints[0]); - double ang1 = -Math.PI/2 * sign(dx); - double ang2 = Math.PI/2 * sign(dx); - boolean ccw = false; - double rx = ww; - double ry = hs2; - if (dx == 0) { - ang1 = (dy > 0) ? 0 : Math.PI; - ang2 = (dy > 0) ? Math.PI : 0; - rx = hs2; - ry = ww; - } - ellipse(g.context, gatePoly.xpoints[2], gatePoly.ypoints[2], rx, ry, 0, ang1, ang2, ccw); - g.context.lineTo(gatePoly.xpoints[4], gatePoly.ypoints[4]); - g.context.closePath(); - g.context.stroke(); - g.setLineWidth(1.0); - } - - void setPoints() { - super.setPoints(); - - if (useEuroGates()) { - createEuroGatePolygon(); - } else { - // 0=topleft, 1 = top of curve, 2 = center, 3=bottom of curve, - // 4 = bottom left - Point triPoints[] = newPointArray(5); - interpPoint2(lead1, lead2, triPoints[0], triPoints[4], 0, hs2); - interpPoint2(lead1, lead2, triPoints[1], triPoints[3], .5, hs2); - interpPoint(lead1, lead2, triPoints[2], .5); - gatePoly = createPolygon(triPoints); - } - if (isInverting()) { - pcircle = interpPoint(point1, point2, .5+(ww+4)/dn); - lead2 = interpPoint(point1, point2, .5+(ww+8)/dn); - } - } - 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'; } + void drawGatePolygon(Graphics g) { + g.setLineWidth(3.0); + g.context.beginPath(); + g.context.moveTo(gatePoly.xpoints[0], gatePoly.ypoints[0]); + double ang1 = -Math.PI / 2 * sign(dx); + double ang2 = Math.PI / 2 * sign(dx); + boolean ccw = false; + double rx = ww; + double ry = hs2; + if (dx == 0) { + ang1 = (dy > 0) ? 0 : Math.PI; + ang2 = (dy > 0) ? Math.PI : 0; + rx = hs2; + ry = ww; + } + ellipse(g.context, gatePoly.xpoints[2], gatePoly.ypoints[2], rx, ry, 0, ang1, ang2, ccw); + g.context.lineTo(gatePoly.xpoints[4], gatePoly.ypoints[4]); + g.context.closePath(); + g.context.stroke(); + g.setLineWidth(1.0); + } + + void setPoints() { + super.setPoints(); + + if (useEuroGates()) { + createEuroGatePolygon(); + } else { + // 0=topleft, 1 = top of curve, 2 = center, 3=bottom of curve, + // 4 = bottom left + Point triPoints[] = newPointArray(5); + interpPoint2(lead1, lead2, triPoints[0], triPoints[4], 0, hs2); + interpPoint2(lead1, lead2, triPoints[1], triPoints[3], .5, hs2); + interpPoint(lead1, lead2, triPoints[2], .5); + gatePoly = createPolygon(triPoints); + } + if (isInverting()) { + pcircle = interpPoint(point1, point2, .5 + (ww + 4) / dn); + lead2 = interpPoint(point1, point2, .5 + (ww + 8) / dn); + } + } + + 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'; } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/AntennaElm.java b/src/main/java/com/lushprojects/circuitjs1/client/AntennaElm.java index 9f29234..11f7ee1 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/AntennaElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/AntennaElm.java @@ -19,39 +19,45 @@ package com.lushprojects.circuitjs1.client; - class AntennaElm extends RailElm { - public AntennaElm(int xx, int yy) { super(xx, yy, WF_AC); } - public AntennaElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f, st); - waveform = WF_AC; - } - double fmphase; - - void drawRail(Graphics g) { - drawRailText(g, "Ant"); - } - - double getVoltage() { - 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; - } - - void stepFinished() { - fmphase += 2*pi*(2200+Math.sin(2*pi*sim.t*13)*100)*sim.timeStep; - } - - int getDumpType() { return 'A'; } - int getShortcut() { return 0; } - - void getInfo(String arr[]) { - super.getInfo(arr); - arr[0] = "Antenna (amplified)"; - } - - public EditInfo getEditInfo(int n) { - return null; - } +public class AntennaElm extends RailElm { + public AntennaElm(int xx, int yy) { + super(xx, yy, WF_AC); } + + public AntennaElm(int xa, int ya, int xb, int yb, int f, StringTokenizer st) { + super(xa, ya, xb, yb, f, st); + waveform = WF_AC; + } + + double fmphase; + + void drawRail(Graphics g) { + drawRailText(g, "Ant"); + } + + double getVoltage() { + 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; + } + + void stepFinished() { + fmphase += 2 * pi * (2200 + Math.sin(2 * pi * sim.t * 13) * 100) * sim.timeStep; + } + + int getDumpType() { + return 'A'; + } + + int getShortcut() { + return 0; + } + + void getInfo(String arr[]) { + super.getInfo(arr); + arr[0] = "Antenna (amplified)"; + } + + public EditInfo getEditInfo(int n) { + return null; + } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/AudioInputElm.java b/src/main/java/com/lushprojects/circuitjs1/client/AudioInputElm.java index c35442b..5dd7374 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/AudioInputElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/AudioInputElm.java @@ -20,135 +20,142 @@ package com.lushprojects.circuitjs1.client; import com.google.gwt.core.client.JsArrayNumber; + import java.util.HashMap; + import com.google.gwt.dom.client.Element; import com.google.gwt.event.dom.client.ChangeEvent; import com.google.gwt.event.dom.client.ChangeHandler; import com.google.gwt.user.client.ui.FileUpload; -class AudioFileEntry { +public class AudioFileEntry { String fileName; JsArrayNumber data; } -class AudioInputElm extends RailElm { - JsArrayNumber data; - double timeOffset; - int samplingRate; - int fileNum; - String fileName; - double maxVoltage; - double startPosition; - - static int lastSamplingRate; - - // cache to preserve audio data when doing cut/paste, or undo/redo - static int fileNumCounter = 1; - static HashMap audioFileMap = new HashMap(); - - public AudioInputElm(int xx, int yy) { - super(xx, yy, WF_AC); - maxVoltage = 5; - } - - public AudioInputElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f, st); - waveform = WF_AC; - maxVoltage = Double.parseDouble(st.nextToken()); - startPosition = Double.parseDouble(st.nextToken()); - fileNum = Integer.parseInt(st.nextToken()); - - AudioFileEntry ent = audioFileMap.get(fileNum); - if (ent != null) { - fileName = ent.fileName; - data = ent.data; - } - samplingRate = lastSamplingRate; - } - - double fmphase; - - String dump() { - // add a file number to the dump so we can preserve the audio file data when doing cut and paste, or undo/redo. - // we don't save the entire file in the dump because it would be huge. - if (data != null) { - if (fileNum == 0) - fileNum = fileNumCounter++; - AudioFileEntry ent = new AudioFileEntry(); - ent.fileName = fileName; - ent.data = data; - audioFileMap.put(fileNum, ent); - } - return super.dump() + " " + maxVoltage + " " + startPosition + " " + fileNum; - } - - void reset() { - timeOffset = startPosition; - } - - void drawRail(Graphics g) { - drawRailText(g, fileName == null ? "No file" : fileName); - } - - String getRailText() { - return fileName == null ? "No file" : fileName; - } - - void setSamplingRate(int sr) { - samplingRate = sr; - } - - double getVoltage() { - if (data == null) - return 0; - if (timeOffset < startPosition) - timeOffset = startPosition; - int ptr = (int) (timeOffset * samplingRate); - if (ptr >= data.length()) { - ptr = 0; - timeOffset = 0; - } - return data.get(ptr) * maxVoltage; - } - - void stepFinished() { - timeOffset += sim.timeStep; - } - - int getDumpType() { return 411; } - int getShortcut() { return 0; } - - public EditInfo getEditInfo(int n) { - if (n == 0) { - EditInfo ei = new EditInfo("", 0, -1, -1); - final AudioInputElm thisElm = this; - final FileUpload file = new FileUpload(); - ei.widget = file; - file.addChangeHandler(new ChangeHandler() { - public void onChange(ChangeEvent event) { - fileName = file.getFilename().replaceAll("^.*\\\\", "").replaceAll("\\.[^.]*$", ""); - AudioInputElm.fetchLoadFileData(thisElm, file.getElement()); - } - }); - return ei; - } - if (n == 1) - return new EditInfo("Max Voltage", maxVoltage); - if (n == 2) - return new EditInfo("Start Position (s)", startPosition); - return null; - } - - public void setEditValue(int n, EditInfo ei) { - if (n == 1) - maxVoltage = ei.value; - if (n == 2) - startPosition = ei.value; - } - - // fetch audio data for a selected file - static native String fetchLoadFileData(AudioInputElm elm, Element uploadElement) /*-{ +public class AudioInputElm extends RailElm { + JsArrayNumber data; + double timeOffset; + int samplingRate; + int fileNum; + String fileName; + double maxVoltage; + double startPosition; + + static int lastSamplingRate; + + // cache to preserve audio data when doing cut/paste, or undo/redo + static int fileNumCounter = 1; + static HashMap audioFileMap = new HashMap(); + + public AudioInputElm(int xx, int yy) { + super(xx, yy, WF_AC); + maxVoltage = 5; + } + + public AudioInputElm(int xa, int ya, int xb, int yb, int f, + StringTokenizer st) { + super(xa, ya, xb, yb, f, st); + waveform = WF_AC; + maxVoltage = Double.parseDouble(st.nextToken()); + startPosition = Double.parseDouble(st.nextToken()); + fileNum = Integer.parseInt(st.nextToken()); + + AudioFileEntry ent = audioFileMap.get(fileNum); + if (ent != null) { + fileName = ent.fileName; + data = ent.data; + } + samplingRate = lastSamplingRate; + } + + double fmphase; + + String dump() { + // add a file number to the dump so we can preserve the audio file data when doing cut and paste, or undo/redo. + // we don't save the entire file in the dump because it would be huge. + if (data != null) { + if (fileNum == 0) + fileNum = fileNumCounter++; + AudioFileEntry ent = new AudioFileEntry(); + ent.fileName = fileName; + ent.data = data; + audioFileMap.put(fileNum, ent); + } + return super.dump() + " " + maxVoltage + " " + startPosition + " " + fileNum; + } + + void reset() { + timeOffset = startPosition; + } + + void drawRail(Graphics g) { + drawRailText(g, fileName == null ? "No file" : fileName); + } + + String getRailText() { + return fileName == null ? "No file" : fileName; + } + + void setSamplingRate(int sr) { + samplingRate = sr; + } + + double getVoltage() { + if (data == null) + return 0; + if (timeOffset < startPosition) + timeOffset = startPosition; + int ptr = (int) (timeOffset * samplingRate); + if (ptr >= data.length()) { + ptr = 0; + timeOffset = 0; + } + return data.get(ptr) * maxVoltage; + } + + void stepFinished() { + timeOffset += sim.timeStep; + } + + int getDumpType() { + return 411; + } + + int getShortcut() { + return 0; + } + + public EditInfo getEditInfo(int n) { + if (n == 0) { + EditInfo ei = new EditInfo("", 0, -1, -1); + final AudioInputElm thisElm = this; + final FileUpload file = new FileUpload(); + ei.widget = file; + file.addChangeHandler(new ChangeHandler() { + public void onChange(ChangeEvent event) { + fileName = file.getFilename().replaceAll("^.*\\\\", "").replaceAll("\\.[^.]*$", ""); + AudioInputElm.fetchLoadFileData(thisElm, file.getElement()); + } + }); + return ei; + } + if (n == 1) + return new EditInfo("Max Voltage", maxVoltage); + if (n == 2) + return new EditInfo("Start Position (s)", startPosition); + return null; + } + + public void setEditValue(int n, EditInfo ei) { + if (n == 1) + maxVoltage = ei.value; + if (n == 2) + startPosition = ei.value; + } + + // fetch audio data for a selected file + static native String fetchLoadFileData(AudioInputElm elm, Element uploadElement) /*-{ var oFiles = uploadElement.files; var context = new (window.AudioContext || window.webkitAudioContext)(); elm.@com.lushprojects.circuitjs1.client.AudioInputElm::setSamplingRate(I)(context.sampleRate); @@ -165,26 +172,26 @@ static native String fetchLoadFileData(AudioInputElm elm, Element uploadElement) reader.readAsArrayBuffer(oFiles[0]); } }-*/; - - void gotAudioData(JsArrayNumber d) { - data = d; - lastSamplingRate = samplingRate; - AudioOutputElm.lastSamplingRate = samplingRate; - } - - void getInfo(String arr[]) { - arr[0] = "audio input"; - if (data == null) { - arr[1] = "no file loaded"; - return; - } - arr[1] = "V = " + getVoltageText(volts[0]); - arr[2] = "pos = " + getUnitText(timeOffset, "s"); - double dur = data.length() / (double)samplingRate; - arr[3] = "dur = " + getUnitText(dur, "s"); - } - - public static void clearCache() { - audioFileMap.clear(); - } + + void gotAudioData(JsArrayNumber d) { + data = d; + lastSamplingRate = samplingRate; + AudioOutputElm.lastSamplingRate = samplingRate; } + + void getInfo(String arr[]) { + arr[0] = "audio input"; + if (data == null) { + arr[1] = "no file loaded"; + return; + } + arr[1] = "V = " + getVoltageText(volts[0]); + arr[2] = "pos = " + getUnitText(timeOffset, "s"); + double dur = data.length() / (double) samplingRate; + arr[3] = "dur = " + getUnitText(dur, "s"); + } + + public static void clearCache() { + audioFileMap.clear(); + } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/AudioOutputElm.java b/src/main/java/com/lushprojects/circuitjs1/client/AudioOutputElm.java index 3a5be31..a025e9e 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/AudioOutputElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/AudioOutputElm.java @@ -23,176 +23,191 @@ public class AudioOutputElm extends CircuitElm { double dataStart; static int lastSamplingRate = 8000; static boolean okToChangeTimeStep; - - public AudioOutputElm(int xx, int yy) { - super(xx, yy); - duration = 1; - samplingRate = lastSamplingRate; - labelNum = getNextLabelNum(); - setDataCount(); - createButton(); - } - public AudioOutputElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f); - duration = Double.parseDouble(st.nextToken()); - samplingRate = Integer.parseInt(st.nextToken()); - labelNum = Integer.parseInt(st.nextToken()); - setDataCount(); - createButton(); - } - String dump() { - return super.dump() + " " + duration + " " + samplingRate + " " + labelNum; - } - - void draggingDone() { - setTimeStep(); - } - - // get next unused labelNum value - int getNextLabelNum() { - int i; - int num = 1; - if (sim.elmList == null) - return 0; - for (i = 0; i != sim.elmList.size(); i++) { - CircuitElm ce = sim.getElm(i); - if (!(ce instanceof AudioOutputElm)) - continue; - int ln = ((AudioOutputElm)ce).labelNum; - if (ln >= num) - num = ln+1; - } - return num; - } - - int getDumpType() { return 211; } - int getPostCount() { return 1; } - void reset() { - dataPtr = 0; - dataFull = false; - dataSampleCount = 0; - nextDataSample = 0; - dataSample = 0; - } - void setPoints() { - super.setPoints(); - lead1 = new Point(); - } - void draw(Graphics g) { - g.save(); - boolean selected = (needsHighlight()); - Font f = new Font("SansSerif", selected ? Font.BOLD : 0, 14); - String s = "Audio Out"; - if (labelNum > 1) - s = "Audio " + labelNum; - g.setFont(f); - int textWidth = (int)g.context.measureText(s).getWidth(); - g.setColor(Color.darkGray); - int pct = (dataFull) ? textWidth : textWidth*dataPtr/dataCount; - g.fillRect(x2-textWidth/2, y2-10, pct, 20); - g.setColor(selected ? selectColor : whiteColor); - interpPoint(point1, point2, lead1, 1-(textWidth/2.+8)/dn); - setBbox(point1, lead1, 0); - drawCenteredText(g, s, x2, y2, true); - setVoltageColor(g, volts[0]); - if (selected) - g.setColor(selectColor); - drawThickLine(g, point1, lead1); - drawPosts(g); - g.restore(); - } - double getVoltageDiff() { return volts[0]; } - void getInfo(String arr[]) { - arr[0] = "audio output"; - arr[1] = "V = " + getVoltageText(volts[0]); - int ct = (dataFull ? dataCount : dataPtr); - double dur = sampleStep * ct; - arr[2] = "start = " + getUnitText(dataFull ? sim.t-duration : dataStart, "s"); - arr[3] = "dur = " + getUnitText(dur, "s"); - arr[4] = "samples = " + ct + (dataFull ? "" : "/" + dataCount); - } - - int dataSampleCount = 0; - double nextDataSample = 0; - double dataSample; - - void stepFinished() { - dataSample += volts[0]; - dataSampleCount++; - if (sim.t >= nextDataSample) { - nextDataSample += sampleStep; - data[dataPtr++] = dataSample/dataSampleCount; - dataSampleCount = 0; - dataSample = 0; - if (dataPtr >= dataCount) { - dataPtr = 0; - dataFull = true; - } - } - } - - void setDataCount() { - dataCount = (int) (samplingRate * duration); - data = new double[dataCount]; - dataStart = sim.t; - dataPtr = 0; - dataFull = false; - sampleStep = 1./samplingRate; - nextDataSample = sim.t+sampleStep; - } - - int samplingRateChoices[] = { 8000, 11025, 16000, 22050, 44100, 48000 }; - - public EditInfo getEditInfo(int n) { - if (n == 0) { - EditInfo ei = new EditInfo("Duration (s)", duration, 0, 5); - return ei; - } - if (n == 1) { - EditInfo ei = new EditInfo("Sampling Rate", 0, -1, -1); - ei.choice = new Choice(); - int i; - for (i = 0; i != samplingRateChoices.length; i++) { - ei.choice.add(samplingRateChoices[i] + ""); - if (samplingRateChoices[i] == samplingRate) - ei.choice.select(i); - } - return ei; - } - if (n == 2) { - EditInfo ei = new EditInfo("", 0, -1, -1); - String url=getLastBlob(); - if (url == null) - return null; - Date date = new Date(); - DateTimeFormat dtf = DateTimeFormat.getFormat("yyyyMMdd-HHmm"); - String fname = "audio-"+ dtf.format(date) + ".circuitjs.wav"; - Anchor a=new Anchor(Locale.LS("Download last played audio"), url); - a.getElement().setAttribute("Download", fname); - ei.widget = a; - return ei; + + public AudioOutputElm(int xx, int yy) { + super(xx, yy); + duration = 1; + samplingRate = lastSamplingRate; + labelNum = getNextLabelNum(); + setDataCount(); + createButton(); + } + + public AudioOutputElm(int xa, int ya, int xb, int yb, int f, + StringTokenizer st) { + super(xa, ya, xb, yb, f); + duration = Double.parseDouble(st.nextToken()); + samplingRate = Integer.parseInt(st.nextToken()); + labelNum = Integer.parseInt(st.nextToken()); + setDataCount(); + createButton(); + } + + String dump() { + return super.dump() + " " + duration + " " + samplingRate + " " + labelNum; + } + + void draggingDone() { + setTimeStep(); + } + + // get next unused labelNum value + int getNextLabelNum() { + int i; + int num = 1; + if (sim.elmList == null) + return 0; + for (i = 0; i != sim.elmList.size(); i++) { + CircuitElm ce = sim.getElm(i); + if (!(ce instanceof AudioOutputElm)) + continue; + int ln = ((AudioOutputElm) ce).labelNum; + if (ln >= num) + num = ln + 1; + } + return num; + } + + int getDumpType() { + return 211; + } + + int getPostCount() { + return 1; + } + + void reset() { + dataPtr = 0; + dataFull = false; + dataSampleCount = 0; + nextDataSample = 0; + dataSample = 0; + } + + void setPoints() { + super.setPoints(); + lead1 = new Point(); + } + + void draw(Graphics g) { + g.save(); + boolean selected = (needsHighlight()); + Font f = new Font("SansSerif", selected ? Font.BOLD : 0, 14); + String s = "Audio Out"; + if (labelNum > 1) + s = "Audio " + labelNum; + g.setFont(f); + int textWidth = (int) g.context.measureText(s).getWidth(); + g.setColor(Color.darkGray); + int pct = (dataFull) ? textWidth : textWidth * dataPtr / dataCount; + g.fillRect(x2 - textWidth / 2, y2 - 10, pct, 20); + g.setColor(selected ? selectColor : whiteColor); + interpPoint(point1, point2, lead1, 1 - (textWidth / 2. + 8) / dn); + setBbox(point1, lead1, 0); + drawCenteredText(g, s, x2, y2, true); + setVoltageColor(g, volts[0]); + if (selected) + g.setColor(selectColor); + drawThickLine(g, point1, lead1); + drawPosts(g); + g.restore(); + } + + double getVoltageDiff() { + return volts[0]; + } + + void getInfo(String arr[]) { + arr[0] = "audio output"; + arr[1] = "V = " + getVoltageText(volts[0]); + int ct = (dataFull ? dataCount : dataPtr); + double dur = sampleStep * ct; + arr[2] = "start = " + getUnitText(dataFull ? sim.t - duration : dataStart, "s"); + arr[3] = "dur = " + getUnitText(dur, "s"); + arr[4] = "samples = " + ct + (dataFull ? "" : "/" + dataCount); + } + + int dataSampleCount = 0; + double nextDataSample = 0; + double dataSample; + + void stepFinished() { + dataSample += volts[0]; + dataSampleCount++; + if (sim.t >= nextDataSample) { + nextDataSample += sampleStep; + data[dataPtr++] = dataSample / dataSampleCount; + dataSampleCount = 0; + dataSample = 0; + if (dataPtr >= dataCount) { + dataPtr = 0; + dataFull = true; } - - return null; - } - public void setEditValue(int n, EditInfo ei) { - if (n == 0 && ei.value > 0) { - duration = ei.value; - setDataCount(); - } - if (n == 1) { - int nsr = samplingRateChoices[ei.choice.getSelectedIndex()]; - if (nsr != samplingRate) { - samplingRate = nsr; - lastSamplingRate = nsr; - setDataCount(); - setTimeStep(); - } - } - } - - void setTimeStep() { + } + } + + void setDataCount() { + dataCount = (int) (samplingRate * duration); + data = new double[dataCount]; + dataStart = sim.t; + dataPtr = 0; + dataFull = false; + sampleStep = 1. / samplingRate; + nextDataSample = sim.t + sampleStep; + } + + int samplingRateChoices[] = {8000, 11025, 16000, 22050, 44100, 48000}; + + public EditInfo getEditInfo(int n) { + if (n == 0) { + EditInfo ei = new EditInfo("Duration (s)", duration, 0, 5); + return ei; + } + if (n == 1) { + EditInfo ei = new EditInfo("Sampling Rate", 0, -1, -1); + ei.choice = new Choice(); + int i; + for (i = 0; i != samplingRateChoices.length; i++) { + ei.choice.add(samplingRateChoices[i] + ""); + if (samplingRateChoices[i] == samplingRate) + ei.choice.select(i); + } + return ei; + } + if (n == 2) { + EditInfo ei = new EditInfo("", 0, -1, -1); + String url = getLastBlob(); + if (url == null) + return null; + Date date = new Date(); + DateTimeFormat dtf = DateTimeFormat.getFormat("yyyyMMdd-HHmm"); + String fname = "audio-" + dtf.format(date) + ".circuitjs.wav"; + Anchor a = new Anchor(Locale.LS("Download last played audio"), url); + a.getElement().setAttribute("Download", fname); + ei.widget = a; + return ei; + } + + return null; + } + + public void setEditValue(int n, EditInfo ei) { + if (n == 0 && ei.value > 0) { + duration = ei.value; + setDataCount(); + } + if (n == 1) { + int nsr = samplingRateChoices[ei.choice.getSelectedIndex()]; + if (nsr != samplingRate) { + samplingRate = nsr; + lastSamplingRate = nsr; + setDataCount(); + setTimeStep(); + } + } + } + + void setTimeStep() { /* // timestep must be smaller than 1/sampleRate if (sim.timeStep > sampleStep) @@ -206,36 +221,37 @@ void setTimeStep() { sim.timeStep = sampleStep; } */ - + // int frac = (int)Math.round(Math.max(sampleStep*33000, 1)); - double target = sampleStep/8; - if (sim.maxTimeStep != target) { - if (okToChangeTimeStep || Window.confirm(Locale.LS("Adjust timestep for best audio quality and performance?"))) { - sim.maxTimeStep = target; - okToChangeTimeStep = true; - } - } - } - - void createButton() { - String label = "▶ " + Locale.LS("Play Audio"); - if (labelNum > 1) - label += " " + labelNum; - sim.addWidgetToVerticalPanel(button = new Button(label)); - button.setStylePrimaryName("topButton"); - button.addClickHandler(new ClickHandler() { - public void onClick(ClickEvent event) { - play(); - } - }); - - } - void delete() { - sim.removeWidgetFromVerticalPanel(button); - super.delete(); + double target = sampleStep / 8; + if (sim.maxTimeStep != target) { + if (okToChangeTimeStep || Window.confirm(Locale.LS("Adjust timestep for best audio quality and performance?"))) { + sim.maxTimeStep = target; + okToChangeTimeStep = true; + } } - - public static native void playJS(JsArrayInteger samples, int sampleRate) + } + + void createButton() { + String label = "▶ " + Locale.LS("Play Audio"); + if (labelNum > 1) + label += " " + labelNum; + sim.addWidgetToVerticalPanel(button = new Button(label)); + button.setStylePrimaryName("topButton"); + button.addClickHandler(new ClickHandler() { + public void onClick(ClickEvent event) { + play(); + } + }); + + } + + void delete() { + sim.removeWidgetFromVerticalPanel(button); + super.delete(); + } + + public static native void playJS(JsArrayInteger samples, int sampleRate) /*-{ var Wav = function(opt_params){ this._sampleRate = opt_params && opt_params.sampleRate ? opt_params.sampleRate : 44100; @@ -350,45 +366,45 @@ public static native void playJS(JsArrayInteger samples, int sampleRate) audio.play(); }-*/; - static native String getLastBlob() /*-{ + static native String getLastBlob() /*-{ return $doc.audioBlob; }-*/; - - void play() { - int i; - JsArrayInteger arr = (JsArrayInteger)JsArrayInteger.createArray(); - int ct = dataPtr; - int base = 0; - if (dataFull) { - ct = dataCount; - base = dataPtr; - } - if (ct * sampleStep < .05) { - Window.alert(Locale.LS("Audio data is not ready yet. Increase simulation speed to make data ready sooner.")); - return; - } - - // rescale data to maximize - double max = -1e8; - double min = 1e8; - for (i = 0; i != ct; i++) { - if (data[i] > max) max = data[i]; - if (data[i] < min) min = data[i]; - } - - double adj = -(max+min)/2; - double mult = (.25*32766)/(max+adj); - - // fade in over 1/20 sec - int fadeLen = samplingRate/20; - int fadeOut = ct-fadeLen; - - double fadeMult = mult/fadeLen; - for (i = 0; i != ct; i++) { - double fade = (i < fadeLen) ? i*fadeMult : (i > fadeOut) ? (ct-i)*fadeMult : mult; - int s = (int)((data[(i+base)%dataCount]+adj)*fade); - arr.push(s); - } - playJS(arr, samplingRate); + + void play() { + int i; + JsArrayInteger arr = (JsArrayInteger) JsArrayInteger.createArray(); + int ct = dataPtr; + int base = 0; + if (dataFull) { + ct = dataCount; + base = dataPtr; + } + if (ct * sampleStep < .05) { + Window.alert(Locale.LS("Audio data is not ready yet. Increase simulation speed to make data ready sooner.")); + return; + } + + // rescale data to maximize + double max = -1e8; + double min = 1e8; + for (i = 0; i != ct; i++) { + if (data[i] > max) max = data[i]; + if (data[i] < min) min = data[i]; + } + + double adj = -(max + min) / 2; + double mult = (.25 * 32766) / (max + adj); + + // fade in over 1/20 sec + int fadeLen = samplingRate / 20; + int fadeOut = ct - fadeLen; + + double fadeMult = mult / fadeLen; + for (i = 0; i != ct; i++) { + double fade = (i < fadeLen) ? i * fadeMult : (i > fadeOut) ? (ct - i) * fadeMult : mult; + int s = (int) ((data[(i + base) % dataCount] + adj) * fade); + arr.push(s); } + playJS(arr, samplingRate); + } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/BoxElm.java b/src/main/java/com/lushprojects/circuitjs1/client/BoxElm.java index 8d1368b..a552eaf 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/BoxElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/BoxElm.java @@ -19,56 +19,58 @@ package com.lushprojects.circuitjs1.client; -class BoxElm extends GraphicElm { +public class BoxElm extends GraphicElm { public BoxElm(int xx, int yy) { - super(xx, yy); - x2 = xx; - y2 = yy; - setBbox(x, y, x2, y2); + super(xx, yy); + x2 = xx; + y2 = yy; + 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; - setBbox(x, y, x2, y2); + StringTokenizer st) { + super(xa, ya, xb, yb, f); + x2 = xb; + y2 = yb; + setBbox(x, y, x2, y2); } String dump() { - return super.dump(); + return super.dump(); } - int getDumpType() { return 'b'; } + int getDumpType() { + return 'b'; + } void drag(int xx, int yy) { - x2 = xx; - y2 = yy; + x2 = xx; + y2 = yy; } boolean creationFailed() { - return Math.abs(x2-x) < 32 || Math.abs(y2-y) < 32; + return Math.abs(x2 - x) < 32 || Math.abs(y2 - y) < 32; } - + void draw(Graphics g) { - //g.setColor(needsHighlight() ? selectColor : lightGrayColor); - g.setColor(needsHighlight() ? selectColor : Color.GRAY); - setBbox(x, y, x2, y2); - g.setLineDash(16, 6); - if ( x < x2 && y < y2 ) - g.drawRect(x,y, x2-x, y2-y); - else if ( x > x2 && y < y2 ) - g.drawRect(x2,y, x-x2, y2-y); - else if ( x < x2 && y > y2 ) - g.drawRect(x, y2, x2-x, y-y2); - else - g.drawRect(x2, y2, x-x2, y-y2); - g.setLineDash(0, 0); + //g.setColor(needsHighlight() ? selectColor : lightGrayColor); + g.setColor(needsHighlight() ? selectColor : Color.GRAY); + setBbox(x, y, x2, y2); + g.setLineDash(16, 6); + if (x < x2 && y < y2) + g.drawRect(x, y, x2 - x, y2 - y); + else if (x > x2 && y < y2) + g.drawRect(x2, y, x - x2, y2 - y); + else if (x < x2 && y > y2) + g.drawRect(x, y2, x2 - x, y - y2); + else + g.drawRect(x2, y2, x - x2, y - y2); + g.setLineDash(0, 0); } public EditInfo getEditInfo(int n) { - return null; + return null; } public void setEditValue(int n, EditInfo ei) { @@ -78,6 +80,8 @@ void getInfo(String arr[]) { } @Override - int getShortcut() { return 0; } + int getShortcut() { + return 0; + } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/CC2Elm.java b/src/main/java/com/lushprojects/circuitjs1/client/CC2Elm.java index 0f853f0..d222794 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/CC2Elm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/CC2Elm.java @@ -19,56 +19,92 @@ package com.lushprojects.circuitjs1.client; - 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-~"; // ~ is for localization - arr[1] = "X,Y = " + getVoltageText(volts[0]); - arr[2] = "Z = " + getVoltageText(volts[2]); - arr[3] = "I = " + getCurrentText(pins[0].current); - } - //boolean nonLinear() { return true; } - @Override boolean isDigitalChip() { return false; } - 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 calculateCurrent() { - super.calculateCurrent(); - pins[2].current = pins[0].current * gain; - } - void draw(Graphics g) { - 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; } +public 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-~"; // ~ is for localization + arr[1] = "X,Y = " + getVoltageText(volts[0]); + arr[2] = "Z = " + getVoltageText(volts[2]); + arr[3] = "I = " + getCurrentText(pins[0].current); + } + + //boolean nonLinear() { return true; } + @Override + boolean isDigitalChip() { + return false; + } + + 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 calculateCurrent() { + super.calculateCurrent(); + pins[2].current = pins[0].current * gain; + } + + void draw(Graphics g) { + drawChip(g); + } + + int getPostCount() { + return 3; + } + + int getVoltageSourceCount() { + return 1; + } + + int getDumpType() { + return 179; + } +} + +public class CC2NegElm extends CC2Elm { + public CC2NegElm(int xx, int yy) { + super(xx, yy, -1); + } + + Class getDumpClass() { + return CC2Elm.class; + } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/CCCSElm.java b/src/main/java/com/lushprojects/circuitjs1/client/CCCSElm.java index acd23f3..06136ca 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/CCCSElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/CCCSElm.java @@ -21,209 +21,229 @@ import java.util.Vector; -class CCCSElm extends VCCSElm { - static int FLAG_SPICE = 2; - VoltageElm voltageSources[]; - - public CCCSElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f, st); +public class CCCSElm extends VCCSElm { + static int FLAG_SPICE = 2; + VoltageElm voltageSources[]; + + public CCCSElm(int xa, int ya, int xb, int yb, int f, + StringTokenizer st) { + super(xa, ya, xb, yb, f, st); // exprString = CustomLogicModel.unescape(st.nextToken()); // inputCount = 2; // parseExpr(); - setupPins(); - } - public CCCSElm(int xx, int yy) { - super(xx, yy); - exprString = "2*a"; - parseExpr(); + setupPins(); + } + + public CCCSElm(int xx, int yy) { + super(xx, yy); + exprString = "2*a"; + parseExpr(); // setupPins(); - } - - int inputPairCount; - - void setupPins() { - sizeX = 2; - sizeY = inputCount > 2 ? inputCount : 2; - pins = new Pin[inputCount+2]; - inputPairCount = inputCount/2; - int i; - for (i = 0; i != inputPairCount; i++) { - pins[i*2 ] = new Pin(i*2, SIDE_W, Character.toString((char)('A'+i)) + "+"); - pins[i*2+1] = new Pin(i*2+1, SIDE_W, Character.toString((char)('A'+i)) + "-"); - pins[i*2+1].output = true; - } - pins[i*2] = new Pin(0, SIDE_E, "O+"); - pins[i*2].output = true; - pins[i*2+1] = new Pin(1, SIDE_E, "O-"); - exprState = new ExprState(inputPairCount); - lastCurrents = new double[inputPairCount]; - allocNodes(); - } - String getChipName() { return "CCCS"; } - void stamp() { - int i; - if (isSpiceStyle()) { - for (i = 0; i != inputCount; i += 2) - pins[i+1].voltSource = voltageSources[i/2].getVoltageSource(); - } else { - // voltage sources (0V) between C+ and C- so we can measure current - for (i = 0; i != inputCount; i += 2) { - int vn1 = pins[i+1].voltSource; - sim.stampVoltageSource(nodes[i], nodes[i+1], vn1, 0); - } - } - - sim.stampNonLinear(nodes[inputCount]); - sim.stampNonLinear(nodes[inputCount+1]); - } - - double lastCurrents[]; - - void doStep() { - // no current path? give up - if (broken) { - pins[inputCount].current = 0; - pins[inputCount+1].current = 0; - // avoid singular matrix errors - sim.stampResistor(nodes[inputCount], nodes[inputCount+1], 1e8); - return; - } + } - // converged yet? - double convergeLimit = getConvergeLimit()*.1; - - int i; - if (isSpiceStyle()) { - // get current from connected voltage sources - for (i = 0; i != inputPairCount; i++) - pins[i*2+1].current = voltageSources[i].getCurrent(); - } - - for (i = 0; i != inputPairCount; i++) { - double cur = pins[i*2+1].current; - if (Math.abs(cur-lastCurrents[i]) > convergeLimit) - sim.converged = false; - } + int inputPairCount; - if (expr != null) { - // calculate output - for (i = 0; i != inputPairCount; i++) - setCurrentExprValue(i, pins[i*2+1].current); - exprState.t = sim.t; - double v0 = expr.eval(exprState); - double rs = v0; - - pins[inputCount ].current = v0; - pins[inputCount+1].current = -v0; - - for (i = 0; i != inputPairCount; i++) { - double cur = pins[i*2+1].current; - double dv = cur-lastCurrents[i]; - if (Math.abs(dv) < 1e-6) - dv = 1e-6; - setCurrentExprValue(i, cur); - double v = expr.eval(exprState); - setCurrentExprValue(i, cur-dv); - double v2 = expr.eval(exprState); - double dx = (v-v2)/dv; - if (Math.abs(dx) < 1e-6) - dx = sign(dx, 1e-6); - sim.stampCCCS(nodes[inputCount+1], nodes[inputCount], pins[i*2+1].voltSource, dx); - - // adjust right side - rs -= dx*cur; -// if (sim.subIterations > 1) -// sim.console("ccedx " + i + " " + cur + " " + dx + " " + rs + " " + sim.subIterations + " " + sim.t); - setCurrentExprValue(i, cur); - } + void setupPins() { + sizeX = 2; + sizeY = inputCount > 2 ? inputCount : 2; + pins = new Pin[inputCount + 2]; + inputPairCount = inputCount / 2; + int i; + for (i = 0; i != inputPairCount; i++) { + pins[i * 2] = new Pin(i * 2, SIDE_W, Character.toString((char) ('A' + i)) + "+"); + pins[i * 2 + 1] = new Pin(i * 2 + 1, SIDE_W, Character.toString((char) ('A' + i)) + "-"); + pins[i * 2 + 1].output = true; + } + pins[i * 2] = new Pin(0, SIDE_E, "O+"); + pins[i * 2].output = true; + pins[i * 2 + 1] = new Pin(1, SIDE_E, "O-"); + exprState = new ExprState(inputPairCount); + lastCurrents = new double[inputPairCount]; + allocNodes(); + } - sim.stampCurrentSource(nodes[inputCount+1], nodes[inputCount], rs); - } + String getChipName() { + return "CCCS"; + } - for (i = 0; i != inputPairCount; i++) - lastCurrents[i] = pins[i*2+1].current; + void stamp() { + int i; + if (isSpiceStyle()) { + for (i = 0; i != inputCount; i += 2) + pins[i + 1].voltSource = voltageSources[i / 2].getVoltageSource(); + } else { + // voltage sources (0V) between C+ and C- so we can measure current + for (i = 0; i != inputCount; i += 2) { + int vn1 = pins[i + 1].voltSource; + sim.stampVoltageSource(nodes[i], nodes[i + 1], vn1, 0); + } } - - void stepFinished() { - exprState.updateLastValues(pins[inputCount].current); + + sim.stampNonLinear(nodes[inputCount]); + sim.stampNonLinear(nodes[inputCount + 1]); + } + + double lastCurrents[]; + + void doStep() { + // no current path? give up + if (broken) { + pins[inputCount].current = 0; + pins[inputCount + 1].current = 0; + // avoid singular matrix errors + sim.stampResistor(nodes[inputCount], nodes[inputCount + 1], 1e8); + return; } - - void setCurrentExprValue(int n, double cur) { - // set i to current for backward compatibility - if (n == 0 && inputPairCount < 9) - exprState.values[8] = cur; - exprState.values[n] = cur; + + // converged yet? + double convergeLimit = getConvergeLimit() * .1; + + int i; + if (isSpiceStyle()) { + // get current from connected voltage sources + for (i = 0; i != inputPairCount; i++) + pins[i * 2 + 1].current = voltageSources[i].getCurrent(); } - - int getPostCount() { return inputCount+2; } - int getVoltageSourceCount() { return isSpiceStyle() ? 0 : inputPairCount; } - int getDumpType() { return 215; } - boolean getConnection(int n1, int n2) { - return (n1/2 == n2/2); - } - boolean hasCurrentOutput() { return true; } - boolean isSpiceStyle() { return (flags & FLAG_SPICE) != 0; } - - void setCurrent(int vn, double c) { - int i; - for (i = 0; i != inputCount; i += 2) - if (pins[i+1].voltSource == vn) { - pins[i].current = -c; - pins[i+1].current = c; - return; - } + + for (i = 0; i != inputPairCount; i++) { + double cur = pins[i * 2 + 1].current; + if (Math.abs(cur - lastCurrents[i]) > convergeLimit) + sim.converged = false; } - - public void setEditValue(int n, EditInfo ei) { - if (n == 1) { - // make sure number of inputs is even - if (ei.value < 0 || ei.value > 8 || (ei.value % 2) == 1) - return; - inputCount = (int) ei.value; - setupPins(); - allocNodes(); - setPoints(); - } else - super.setEditValue(n, ei); + + if (expr != null) { + // calculate output + for (i = 0; i != inputPairCount; i++) + setCurrentExprValue(i, pins[i * 2 + 1].current); + exprState.t = sim.t; + double v0 = expr.eval(exprState); + double rs = v0; + + pins[inputCount].current = v0; + pins[inputCount + 1].current = -v0; + + for (i = 0; i != inputPairCount; i++) { + double cur = pins[i * 2 + 1].current; + double dv = cur - lastCurrents[i]; + if (Math.abs(dv) < 1e-6) + dv = 1e-6; + setCurrentExprValue(i, cur); + double v = expr.eval(exprState); + setCurrentExprValue(i, cur - dv); + double v2 = expr.eval(exprState); + double dx = (v - v2) / dv; + if (Math.abs(dx) < 1e-6) + dx = sign(dx, 1e-6); + sim.stampCCCS(nodes[inputCount + 1], nodes[inputCount], pins[i * 2 + 1].voltSource, dx); + + // adjust right side + rs -= dx * cur; +// if (sim.subIterations > 1) +// sim.console("ccedx " + i + " " + cur + " " + dx + " " + rs + " " + sim.subIterations + " " + sim.t); + setCurrentExprValue(i, cur); + } + + sim.stampCurrentSource(nodes[inputCount + 1], nodes[inputCount], rs); } - - void setParentList(Vector elmList) { - int i, j; - if (!isSpiceStyle()) + + for (i = 0; i != inputPairCount; i++) + lastCurrents[i] = pins[i * 2 + 1].current; + } + + void stepFinished() { + exprState.updateLastValues(pins[inputCount].current); + } + + void setCurrentExprValue(int n, double cur) { + // set i to current for backward compatibility + if (n == 0 && inputPairCount < 9) + exprState.values[8] = cur; + exprState.values[n] = cur; + } + + int getPostCount() { + return inputCount + 2; + } + + int getVoltageSourceCount() { + return isSpiceStyle() ? 0 : inputPairCount; + } + + int getDumpType() { + return 215; + } + + boolean getConnection(int n1, int n2) { + return (n1 / 2 == n2 / 2); + } + + boolean hasCurrentOutput() { + return true; + } + + boolean isSpiceStyle() { + return (flags & FLAG_SPICE) != 0; + } + + void setCurrent(int vn, double c) { + int i; + for (i = 0; i != inputCount; i += 2) + if (pins[i + 1].voltSource == vn) { + pins[i].current = -c; + pins[i + 1].current = c; return; - - // look for voltage sources across our inputs and use them rather than - // creating our own. this is useful for converting spice subcircuits - voltageSources = new VoltageElm[inputPairCount]; - for (i = 0; i != inputCount; i += 2) { - for (j = 0; j != elmList.size(); j++) { - CircuitElm ce = elmList.get(j); - if (!(ce instanceof VoltageElm)) - continue; - if (ce.getNode(0) == nodes[i] && ce.getNode(1) == nodes[i+1]) - voltageSources[i/2] = (VoltageElm)ce; - } + } + } + + public void setEditValue(int n, EditInfo ei) { + if (n == 1) { + // make sure number of inputs is even + if (ei.value < 0 || ei.value > 8 || (ei.value % 2) == 1) + return; + inputCount = (int) ei.value; + setupPins(); + allocNodes(); + setPoints(); + } else + super.setEditValue(n, ei); + } + + void setParentList(Vector elmList) { + int i, j; + if (!isSpiceStyle()) + return; + + // look for voltage sources across our inputs and use them rather than + // creating our own. this is useful for converting spice subcircuits + voltageSources = new VoltageElm[inputPairCount]; + for (i = 0; i != inputCount; i += 2) { + for (j = 0; j != elmList.size(); j++) { + CircuitElm ce = elmList.get(j); + if (!(ce instanceof VoltageElm)) + continue; + if (ce.getNode(0) == nodes[i] && ce.getNode(1) == nodes[i + 1]) + voltageSources[i / 2] = (VoltageElm) ce; } } - - void setVoltageSource(int j, int vs) { - if (isSpiceStyle()) - pins[inputCount].voltSource = vs; - else - super.setVoltageSource(j, vs); - } - - void getInfo(String arr[]) { - super.getInfo(arr); - int i = 1; - int j; - for (j = 0; j != inputCount; j += 2) - arr[i++] = pins[j].text + " = " + getCurrentText(-pins[j].current); - arr[i++] = pins[j].text + " = " + getVoltageText(volts[j]) + "; " + pins[j+1].text + " = " + getVoltageText(volts[j+1]); - arr[i++] = "I = " + getCurrentText(pins[j].current); - arr[i++] = null; - } - } + void setVoltageSource(int j, int vs) { + if (isSpiceStyle()) + pins[inputCount].voltSource = vs; + else + super.setVoltageSource(j, vs); + } + + void getInfo(String arr[]) { + super.getInfo(arr); + int i = 1; + int j; + for (j = 0; j != inputCount; j += 2) + arr[i++] = pins[j].text + " = " + getCurrentText(-pins[j].current); + arr[i++] = pins[j].text + " = " + getVoltageText(volts[j]) + "; " + pins[j + 1].text + " = " + getVoltageText(volts[j + 1]); + arr[i++] = "I = " + getCurrentText(pins[j].current); + arr[i++] = null; + } + +} + diff --git a/src/main/java/com/lushprojects/circuitjs1/client/CCVSElm.java b/src/main/java/com/lushprojects/circuitjs1/client/CCVSElm.java index 68d5017..6294e69 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/CCVSElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/CCVSElm.java @@ -21,209 +21,227 @@ import java.util.Vector; -class CCVSElm extends VCCSElm { - static int FLAG_SPICE = 2; - VoltageElm voltageSources[]; - int outputVS; - - public CCVSElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f, st); +public class CCVSElm extends VCCSElm { + static int FLAG_SPICE = 2; + VoltageElm voltageSources[]; + int outputVS; + + public CCVSElm(int xa, int ya, int xb, int yb, int f, + StringTokenizer st) { + super(xa, ya, xb, yb, f, st); // exprString = CustomLogicModel.unescape(st.nextToken()); // inputCount = 2; // parseExpr(); - setupPins(); - } - public CCVSElm(int xx, int yy) { - super(xx, yy); - exprString = "2*a"; - parseExpr(); + setupPins(); + } + + public CCVSElm(int xx, int yy) { + super(xx, yy); + exprString = "2*a"; + parseExpr(); // setupPins(); - } - - int inputPairCount; - - void setupPins() { - sizeX = 2; - sizeY = inputCount > 2 ? inputCount : 2; - inputPairCount = inputCount/2; - pins = new Pin[inputCount+2]; - int i; - for (i = 0; i != inputPairCount; i++) { - pins[i*2 ] = new Pin(i*2, SIDE_W, Character.toString((char)('A'+i)) + "+"); - pins[i*2+1] = new Pin(i*2+1, SIDE_W, Character.toString((char)('A'+i)) + "-"); - pins[i*2+1].output = true; - } - pins[i*2] = new Pin(0, SIDE_E, "V+"); - pins[i*2].output = true; - pins[i*2+1] = new Pin(1, SIDE_E, "V-"); - exprState = new ExprState(inputPairCount); - lastCurrents = new double[inputPairCount]; - allocNodes(); - } - - String getChipName() { return "CCVS"; } - - void stamp() { - int i; - if (isSpiceStyle()) { - for (i = 0; i != inputCount; i += 2) - pins[i+1].voltSource = voltageSources[i/2].getVoltageSource(); - } else { - // voltage source (0V) between C+ and C- so we can measure current - for (i = 0; i != inputCount; i += 2) { - int vn1 = pins[i+1].voltSource; - sim.stampVoltageSource(nodes[i], nodes[i+1], vn1, 0); - } - } - - // voltage source for outputs - int vn2 = pins[inputCount].voltSource; - outputVS = vn2; - sim.stampNonLinear(vn2 + sim.nodeList.size()); - sim.stampVoltageSource(nodes[inputCount+1], nodes[inputCount], vn2); - } - - double lastCurrents[]; - - void doStep() { - // converged yet? - double convergeLimit = getConvergeLimit()*.1; - - int i; - if (isSpiceStyle()) { - for (i = 0; i != inputPairCount; i++) - pins[i*2+1].current = voltageSources[i].getCurrent(); - } - - for (i = 0; i != inputPairCount; i++) { - double cur = pins[i*2+1].current; - if (Math.abs(cur-lastCurrents[i]) > convergeLimit) - sim.converged = false; - } - - int vno = outputVS + sim.nodeList.size(); - if (expr != null) { - // calculate output - for (i = 0; i != inputPairCount; i++) - setCurrentExprValue(i, pins[i*2+1].current); - exprState.t = sim.t; - double v0 = expr.eval(exprState); - double rs = v0; - - for (i = 0; i != inputPairCount; i++) { - double cur = pins[i*2+1].current; - double dv = cur-lastCurrents[i]; - int vni = pins[i*2+1].voltSource + sim.nodeList.size(); - if (Math.abs(dv) < 1e-6) - dv = 1e-6; - setCurrentExprValue(i, cur); - double v = expr.eval(exprState); - setCurrentExprValue(i, cur-dv); - double v2 = expr.eval(exprState); - double dx = (v-v2)/dv; - if (Math.abs(dx) < 1e-6) - dx = sign(dx, 1e-6); - sim.stampMatrix(vno, vni, -dx); - // adjust right side - rs -= dx*cur; - //if (sim.subIterations > 1) - //sim.console("ccedx " + i + " " + cur + " " + dx + " " + rs + " " + sim.subIterations + " " + sim.t); - setCurrentExprValue(i, cur); - } - sim.stampRightSide(vno, rs); + } + + int inputPairCount; + + void setupPins() { + sizeX = 2; + sizeY = inputCount > 2 ? inputCount : 2; + inputPairCount = inputCount / 2; + pins = new Pin[inputCount + 2]; + int i; + for (i = 0; i != inputPairCount; i++) { + pins[i * 2] = new Pin(i * 2, SIDE_W, Character.toString((char) ('A' + i)) + "+"); + pins[i * 2 + 1] = new Pin(i * 2 + 1, SIDE_W, Character.toString((char) ('A' + i)) + "-"); + pins[i * 2 + 1].output = true; + } + pins[i * 2] = new Pin(0, SIDE_E, "V+"); + pins[i * 2].output = true; + pins[i * 2 + 1] = new Pin(1, SIDE_E, "V-"); + exprState = new ExprState(inputPairCount); + lastCurrents = new double[inputPairCount]; + allocNodes(); + } + + String getChipName() { + return "CCVS"; + } + + void stamp() { + int i; + if (isSpiceStyle()) { + for (i = 0; i != inputCount; i += 2) + pins[i + 1].voltSource = voltageSources[i / 2].getVoltageSource(); + } else { + // voltage source (0V) between C+ and C- so we can measure current + for (i = 0; i != inputCount; i += 2) { + int vn1 = pins[i + 1].voltSource; + sim.stampVoltageSource(nodes[i], nodes[i + 1], vn1, 0); } + } + + // voltage source for outputs + int vn2 = pins[inputCount].voltSource; + outputVS = vn2; + sim.stampNonLinear(vn2 + sim.nodeList.size()); + sim.stampVoltageSource(nodes[inputCount + 1], nodes[inputCount], vn2); + } + + double lastCurrents[]; + + void doStep() { + // converged yet? + double convergeLimit = getConvergeLimit() * .1; + int i; + if (isSpiceStyle()) { for (i = 0; i != inputPairCount; i++) - lastCurrents[i] = pins[i*2+1].current; - } - - void stepFinished() { - exprState.updateLastValues(volts[inputCount]-volts[inputCount+1]); + pins[i * 2 + 1].current = voltageSources[i].getCurrent(); } - void setCurrentExprValue(int n, double cur) { - // set i to current for backward compatibility - if (n == 0 && inputPairCount < 9) - exprState.values[8] = cur; - exprState.values[n] = cur; + for (i = 0; i != inputPairCount; i++) { + double cur = pins[i * 2 + 1].current; + if (Math.abs(cur - lastCurrents[i]) > convergeLimit) + sim.converged = false; } - - int getPostCount() { return inputCount+2; } - int getVoltageSourceCount() { return isSpiceStyle() ? 1 : 1+inputPairCount; } - int getDumpType() { return 214; } - boolean getConnection(int n1, int n2) { - return (n1/2 == n2/2); - } - boolean hasCurrentOutput() { return false; } - boolean isSpiceStyle() { return (flags & FLAG_SPICE) != 0; } - - void setCurrent(int vn, double c) { - int i = 0; - if (!isSpiceStyle()) { - for (i = 0; i != inputCount; i += 2) - if (pins[i+1].voltSource == vn) { - pins[i].current = -c; - pins[i+1].current = c; - return; - } - } else - i = inputCount; - if (pins[i].voltSource == vn) { - pins[i].current = c; - pins[i+1].current = -c; + + int vno = outputVS + sim.nodeList.size(); + if (expr != null) { + // calculate output + for (i = 0; i != inputPairCount; i++) + setCurrentExprValue(i, pins[i * 2 + 1].current); + exprState.t = sim.t; + double v0 = expr.eval(exprState); + double rs = v0; + + for (i = 0; i != inputPairCount; i++) { + double cur = pins[i * 2 + 1].current; + double dv = cur - lastCurrents[i]; + int vni = pins[i * 2 + 1].voltSource + sim.nodeList.size(); + if (Math.abs(dv) < 1e-6) + dv = 1e-6; + setCurrentExprValue(i, cur); + double v = expr.eval(exprState); + setCurrentExprValue(i, cur - dv); + double v2 = expr.eval(exprState); + double dx = (v - v2) / dv; + if (Math.abs(dx) < 1e-6) + dx = sign(dx, 1e-6); + sim.stampMatrix(vno, vni, -dx); + // adjust right side + rs -= dx * cur; + //if (sim.subIterations > 1) + //sim.console("ccedx " + i + " " + cur + " " + dx + " " + rs + " " + sim.subIterations + " " + sim.t); + setCurrentExprValue(i, cur); } + sim.stampRightSide(vno, rs); } - - public void setChipEditValue(int n, EditInfo ei) { - if (n == 1) { - // make sure number of inputs is even - if (ei.value < 0 || ei.value > 8 || (ei.value % 2) == 1) + + for (i = 0; i != inputPairCount; i++) + lastCurrents[i] = pins[i * 2 + 1].current; + } + + void stepFinished() { + exprState.updateLastValues(volts[inputCount] - volts[inputCount + 1]); + } + + void setCurrentExprValue(int n, double cur) { + // set i to current for backward compatibility + if (n == 0 && inputPairCount < 9) + exprState.values[8] = cur; + exprState.values[n] = cur; + } + + int getPostCount() { + return inputCount + 2; + } + + int getVoltageSourceCount() { + return isSpiceStyle() ? 1 : 1 + inputPairCount; + } + + int getDumpType() { + return 214; + } + + boolean getConnection(int n1, int n2) { + return (n1 / 2 == n2 / 2); + } + + boolean hasCurrentOutput() { + return false; + } + + boolean isSpiceStyle() { + return (flags & FLAG_SPICE) != 0; + } + + void setCurrent(int vn, double c) { + int i = 0; + if (!isSpiceStyle()) { + for (i = 0; i != inputCount; i += 2) + if (pins[i + 1].voltSource == vn) { + pins[i].current = -c; + pins[i + 1].current = c; return; - inputCount = (int) ei.value; - setupPins(); - allocNodes(); - setPoints(); - } else - super.setChipEditValue(n, ei); + } + } else + i = inputCount; + if (pins[i].voltSource == vn) { + pins[i].current = c; + pins[i + 1].current = -c; } + } - void setParentList(Vector elmList) { - int i, j; - if (!isSpiceStyle()) - return; - - // look for voltage sources across our inputs and use them rather than - // creating our own. this is useful for converting spice subcircuits - voltageSources = new VoltageElm[inputPairCount]; - for (i = 0; i != inputCount; i += 2) { - for (j = 0; j != elmList.size(); j++) { - CircuitElm ce = elmList.get(j); - if (!(ce instanceof VoltageElm)) - continue; - if (ce.getNode(0) == nodes[i] && ce.getNode(1) == nodes[i+1]) - voltageSources[i/2] = (VoltageElm)ce; - } + public void setChipEditValue(int n, EditInfo ei) { + if (n == 1) { + // make sure number of inputs is even + if (ei.value < 0 || ei.value > 8 || (ei.value % 2) == 1) + return; + inputCount = (int) ei.value; + setupPins(); + allocNodes(); + setPoints(); + } else + super.setChipEditValue(n, ei); + } + + void setParentList(Vector elmList) { + int i, j; + if (!isSpiceStyle()) + return; + + // look for voltage sources across our inputs and use them rather than + // creating our own. this is useful for converting spice subcircuits + voltageSources = new VoltageElm[inputPairCount]; + for (i = 0; i != inputCount; i += 2) { + for (j = 0; j != elmList.size(); j++) { + CircuitElm ce = elmList.get(j); + if (!(ce instanceof VoltageElm)) + continue; + if (ce.getNode(0) == nodes[i] && ce.getNode(1) == nodes[i + 1]) + voltageSources[i / 2] = (VoltageElm) ce; } } - - void setVoltageSource(int j, int vs) { - if (isSpiceStyle()) - pins[inputCount].voltSource = vs; - else - super.setVoltageSource(j, vs); - } - - void getInfo(String arr[]) { - super.getInfo(arr); - int i = 1; - int j; - for (j = 0; j != inputCount; j += 2) - arr[i++] = pins[j].text + " = " + getCurrentText(-pins[j].current); - arr[i++] = pins[j].text + " = " + getVoltageText(volts[j]) + "; " + pins[j+1].text + " = " + getVoltageText(volts[j+1]); - arr[i++] = "I = " + getCurrentText(pins[j].current); - arr[i++] = null; - } + } + void setVoltageSource(int j, int vs) { + if (isSpiceStyle()) + pins[inputCount].voltSource = vs; + else + super.setVoltageSource(j, vs); } + void getInfo(String arr[]) { + super.getInfo(arr); + int i = 1; + int j; + for (j = 0; j != inputCount; j += 2) + arr[i++] = pins[j].text + " = " + getCurrentText(-pins[j].current); + arr[i++] = pins[j].text + " = " + getVoltageText(volts[j]) + "; " + pins[j + 1].text + " = " + getVoltageText(volts[j + 1]); + arr[i++] = "I = " + getCurrentText(pins[j].current); + arr[i++] = null; + } + +} + diff --git a/src/main/java/com/lushprojects/circuitjs1/client/CapacitorElm.java b/src/main/java/com/lushprojects/circuitjs1/client/CapacitorElm.java index e80f774..8aaaf4a 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/CapacitorElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/CapacitorElm.java @@ -21,191 +21,219 @@ import com.lushprojects.circuitjs1.client.util.Locale; -class CapacitorElm extends CircuitElm { - double capacitance; - double compResistance, voltdiff; - double initialVoltage; - Point plate1[], plate2[]; - public static final int FLAG_BACK_EULER = 2; - public CapacitorElm(int xx, int yy) { - super(xx, yy); - capacitance = 1e-5; - initialVoltage = 1e-3; - } - 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(); - initialVoltage = 1e-3; - try { - initialVoltage = new Double(st.nextToken()).doubleValue(); - } catch (Exception e) {} - } - boolean isTrapezoidal() { return (flags & FLAG_BACK_EULER) == 0; } - - void reset() { - super.reset(); - current = curcount = curSourceValue = 0; - // put small charge on caps when reset to start oscillators - voltdiff = initialVoltage; - } - void shorted() { - super.reset(); - voltdiff = current = curcount = curSourceValue = 0; - } - int getDumpType() { return 'c'; } - String dump() { - return super.dump() + " " + capacitance + " " + voltdiff + " " + initialVoltage; - } - - // used for PolarCapacitorElm - Point platePoints[]; - - 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); - if (platePoints == null) - drawThickLine(g, plate2[0], plate2[1]); - else { - int i; - for (i = 0; i != platePoints.length-1; i++) - drawThickLine(g, platePoints[i], platePoints[i+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() { - if (sim.dcAnalysisFlag) { - // when finding DC operating point, replace cap with a 100M resistor - sim.stampResistor(nodes[0], nodes[1], 1e8); - curSourceValue = 0; - return; - } - - // 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; - } - - void stepFinished() { - voltdiff = volts[0]-volts[1]; - calculateCurrent(); - } - - void setNodeVoltage(int n, double c) { - // do not calculate current, that only gets done in stepFinished(). otherwise calculateCurrent() may get - // called while stamping the circuit, which might discharge the cap (since we use that current to calculate - // curSourceValue in startIteration) - volts[n] = c; - } - - void calculateCurrent() { - double voltdiff = volts[0] - volts[1]; - if (sim.dcAnalysisFlag) { - current = voltdiff/1e8; - return; - } - // 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() { - if (sim.dcAnalysisFlag) - return; - 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"); - } - @Override - String getScopeText(int v) { - return Locale.LS("capacitor") + ", " + getUnitText(capacitance, "F"); - } - public EditInfo getEditInfo(int n) { - if (n == 0) - return new EditInfo("Capacitance (F)", capacitance, 1e-6, 1e-3); - if (n == 1) { - EditInfo ei = new EditInfo("", 0, -1, -1); - ei.checkbox = new Checkbox("Trapezoidal Approximation", isTrapezoidal()); - return ei; - } - if (n == 2) - return new EditInfo("Initial Voltage (on Reset)", initialVoltage); - // if you add more things here, check PolarCapacitorElm - return null; - } - public void setEditValue(int n, EditInfo ei) { - if (n == 0) - capacitance = (ei.value > 0) ? ei.value : 1e-12; - if (n == 1) { - if (ei.checkbox.getState()) - flags &= ~FLAG_BACK_EULER; - else - flags |= FLAG_BACK_EULER; - } - if (n == 2) - initialVoltage = ei.value; - } - int getShortcut() { return 'c'; } - public double getCapacitance() { return capacitance; } - public void setCapacitance(double c) { capacitance = c; } +public class CapacitorElm extends CircuitElm { + double capacitance; + double compResistance, voltdiff; + double initialVoltage; + Point plate1[], plate2[]; + public static final int FLAG_BACK_EULER = 2; + + public CapacitorElm(int xx, int yy) { + super(xx, yy); + capacitance = 1e-5; + initialVoltage = 1e-3; + } + + 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(); + initialVoltage = 1e-3; + try { + initialVoltage = new Double(st.nextToken()).doubleValue(); + } catch (Exception e) { + } + } + + boolean isTrapezoidal() { + return (flags & FLAG_BACK_EULER) == 0; + } + + void reset() { + super.reset(); + current = curcount = curSourceValue = 0; + // put small charge on caps when reset to start oscillators + voltdiff = initialVoltage; + } + + void shorted() { + super.reset(); + voltdiff = current = curcount = curSourceValue = 0; + } + + int getDumpType() { + return 'c'; + } + + String dump() { + return super.dump() + " " + capacitance + " " + voltdiff + " " + initialVoltage; + } + + // used for PolarCapacitorElm + Point platePoints[]; + + 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); + if (platePoints == null) + drawThickLine(g, plate2[0], plate2[1]); + else { + int i; + for (i = 0; i != platePoints.length - 1; i++) + drawThickLine(g, platePoints[i], platePoints[i + 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() { + if (sim.dcAnalysisFlag) { + // when finding DC operating point, replace cap with a 100M resistor + sim.stampResistor(nodes[0], nodes[1], 1e8); + curSourceValue = 0; + return; + } + + // 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; + } + + void stepFinished() { + voltdiff = volts[0] - volts[1]; + calculateCurrent(); + } + + void setNodeVoltage(int n, double c) { + // do not calculate current, that only gets done in stepFinished(). otherwise calculateCurrent() may get + // called while stamping the circuit, which might discharge the cap (since we use that current to calculate + // curSourceValue in startIteration) + volts[n] = c; + } + + void calculateCurrent() { + double voltdiff = volts[0] - volts[1]; + if (sim.dcAnalysisFlag) { + current = voltdiff / 1e8; + return; + } + // 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() { + if (sim.dcAnalysisFlag) + return; + 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"); + } + + @Override + String getScopeText(int v) { + return Locale.LS("capacitor") + ", " + getUnitText(capacitance, "F"); + } + + public EditInfo getEditInfo(int n) { + if (n == 0) + return new EditInfo("Capacitance (F)", capacitance, 1e-6, 1e-3); + if (n == 1) { + EditInfo ei = new EditInfo("", 0, -1, -1); + ei.checkbox = new Checkbox("Trapezoidal Approximation", isTrapezoidal()); + return ei; + } + if (n == 2) + return new EditInfo("Initial Voltage (on Reset)", initialVoltage); + // if you add more things here, check PolarCapacitorElm + return null; + } + + public void setEditValue(int n, EditInfo ei) { + if (n == 0) + capacitance = (ei.value > 0) ? ei.value : 1e-12; + if (n == 1) { + if (ei.checkbox.getState()) + flags &= ~FLAG_BACK_EULER; + else + flags |= FLAG_BACK_EULER; + } + if (n == 2) + initialVoltage = ei.value; + } + + int getShortcut() { + return 'c'; + } + + public double getCapacitance() { + return capacitance; + } + + public void setCapacitance(double c) { + capacitance = c; } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/Checkbox.java b/src/main/java/com/lushprojects/circuitjs1/client/Checkbox.java index 2aeeaf9..8273dfc 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/Checkbox.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/Checkbox.java @@ -22,22 +22,22 @@ import com.google.gwt.user.client.ui.CheckBox; import com.lushprojects.circuitjs1.client.util.Locale; -class Checkbox extends CheckBox { - public Checkbox(String s){ - super(Locale.LS(s)); - } - - public Checkbox(String s, boolean b){ - super(Locale.LS(s)); - this.setValue(b); - } - - public boolean getState(){ - return this.getValue(); - } - - public void setState(boolean s){ - this.setValue(s); - } - +public class Checkbox extends CheckBox { + public Checkbox(String s) { + super(Locale.LS(s)); + } + + public Checkbox(String s, boolean b) { + super(Locale.LS(s)); + this.setValue(b); + } + + public boolean getState() { + return this.getValue(); + } + + public void setState(boolean s) { + this.setValue(s); + } + } \ No newline at end of file diff --git a/src/main/java/com/lushprojects/circuitjs1/client/CheckboxAlignedMenuItem.java b/src/main/java/com/lushprojects/circuitjs1/client/CheckboxAlignedMenuItem.java index a667799..064167d 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/CheckboxAlignedMenuItem.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/CheckboxAlignedMenuItem.java @@ -24,9 +24,7 @@ import com.google.gwt.safehtml.shared.SafeHtmlUtils; public class CheckboxAlignedMenuItem extends MenuItem { - - public CheckboxAlignedMenuItem(String s, Command cmd) { - super(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml+" "+s), cmd); - } - + public CheckboxAlignedMenuItem(String s, Command cmd) { + super(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml + " " + s), cmd); + } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/CheckboxMenuItem.java b/src/main/java/com/lushprojects/circuitjs1/client/CheckboxMenuItem.java index 5ac96d8..89bda11 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/CheckboxMenuItem.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/CheckboxMenuItem.java @@ -23,78 +23,83 @@ import com.google.gwt.user.client.Command; public class CheckboxMenuItem extends MenuItem implements Command { - private boolean on=false; - private String name=""; - private String shortcut=""; - private Command extcmd=null; - static String checkBoxHtml="
"; - - public String getName() { return name; } - public String getShortcut() { return shortcut; } - - public CheckboxMenuItem(String s){ - super(s, (Command)null); - super.setScheduledCommand(this); - name=s; - setState(false); - } - - public CheckboxMenuItem(String s, Command cmd){ - super(s, (Command)null); - super.setScheduledCommand(this); - extcmd=cmd; - name=s; - setState(false); - } - - public CheckboxMenuItem(String s, String c, Command cmd){ - this(s, cmd); - shortcut=c; - } - - public CheckboxMenuItem(String s, String c){ - this(s); - shortcut=c; - } - - public void setShortcut(String s) { - shortcut=s; - } - - public void execute() { - setState(!on); - if (extcmd!=null) { - extcmd.execute(); - CircuitElm.sim.repaint(); - } - } - - public void setTitle(String s) { - name = s; - } - - public void setState(boolean newstate) { - on = newstate; - String s; + private boolean on = false; + private String name = ""; + private String shortcut = ""; + private Command extcmd = null; + static String checkBoxHtml = "
"; + + public String getName() { + return name; + } + + public String getShortcut() { + return shortcut; + } + + public CheckboxMenuItem(String s) { + super(s, (Command) null); + super.setScheduledCommand(this); + name = s; + setState(false); + } + + public CheckboxMenuItem(String s, Command cmd) { + super(s, (Command) null); + super.setScheduledCommand(this); + extcmd = cmd; + name = s; + setState(false); + } + + public CheckboxMenuItem(String s, String c, Command cmd) { + this(s, cmd); + shortcut = c; + } + + public CheckboxMenuItem(String s, String c) { + this(s); + shortcut = c; + } + + public void setShortcut(String s) { + shortcut = s; + } + + public void execute() { + setState(!on); + if (extcmd != null) { + extcmd.execute(); + CircuitElm.sim.repaint(); + } + } + + public void setTitle(String s) { + name = s; + } + + public void setState(boolean newstate) { + on = newstate; + String s; if (on) - // super.setHTML("✔ "+name); - s = checkBoxHtml+"✔
"+name; + // super.setHTML("✔ "+name); + s = checkBoxHtml + "✔
" + name; else // super.setHTML("  "+name); - s = checkBoxHtml+" "+name; - if (shortcut!="") - if (shortcut.length()==1) { - s = s + "
"+shortcut+"
"; - } else { - // add some space so menu text doesn't overlap shortcut - s = s+ ""; - s = s + "
"+shortcut+"
"; - } + s = checkBoxHtml + " " + name; + if (shortcut != "") + if (shortcut.length() == 1) { + s = s + "
" + shortcut + "
"; + } else { + // add some space so menu text doesn't overlap shortcut + s = s + ""; + s = s + "
" + shortcut + "
"; + } setHTML(s); - } - - public boolean getState(){ - return on; - } + } + + public boolean getState() { + return on; + } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/ChipElm.java b/src/main/java/com/lushprojects/circuitjs1/client/ChipElm.java index 822db14..5dc4dd9 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/ChipElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/ChipElm.java @@ -19,559 +19,618 @@ package com.lushprojects.circuitjs1.client; -abstract class ChipElm extends CircuitElm { - int csize, cspc, cspc2; - int bits; - double highVoltage; - - static final int FLAG_SMALL = 1; - static final int FLAG_FLIP_X = 1<<10; - static final int FLAG_FLIP_Y = 1<<11; - static final int FLAG_FLIP_XY = 1<<12; - static final int FLAG_CUSTOM_VOLTAGE = 1<<13; - public ChipElm(int xx, int yy) { - super(xx, yy); - if (needsBits()) - bits = defaultBitCount(); - highVoltage = 5; - 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()) - if (st.hasMoreTokens()) - bits = new Integer(st.nextToken()).intValue(); - else - bits = defaultBitCount(); - highVoltage = (hasCustomVoltage()) ? Double.parseDouble(st.nextToken()) : 5; - noDiagonal = true; - setupPins(); - setSize((f & FLAG_SMALL) != 0 ? 1 : 2); - int i; - for (i = 0; i != getPostCount(); i++) { - if (pins == null) - volts[i] = new Double(st.nextToken()).doubleValue(); - else if (pins[i].state) { - volts[i] = new Double(st.nextToken()).doubleValue(); - pins[i].value = volts[i] > getThreshold(); - } - } - } - boolean needsBits() { return false; } - boolean hasCustomVoltage() { return (flags & FLAG_CUSTOM_VOLTAGE) != 0; } - boolean isDigitalChip() { return true; } - double getThreshold() { return highVoltage/2; } - - int defaultBitCount() { return 4; } - 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; - g.save(); - Font f = new Font("normal", 0, 10*csize); +public abstract class ChipElm extends CircuitElm { + int csize, cspc, cspc2; + int bits; + double highVoltage; + static final int FLAG_SMALL = 1; + static final int FLAG_FLIP_X = 1 << 10; + static final int FLAG_FLIP_Y = 1 << 11; + static final int FLAG_FLIP_XY = 1 << 12; + static final int FLAG_CUSTOM_VOLTAGE = 1 << 13; + + public ChipElm(int xx, int yy) { + super(xx, yy); + if (needsBits()) + bits = defaultBitCount(); + highVoltage = 5; + 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()) + if (st.hasMoreTokens()) + bits = new Integer(st.nextToken()).intValue(); + else + bits = defaultBitCount(); + highVoltage = (hasCustomVoltage()) ? Double.parseDouble(st.nextToken()) : 5; + noDiagonal = true; + setupPins(); + setSize((f & FLAG_SMALL) != 0 ? 1 : 2); + int i; + for (i = 0; i != getPostCount(); i++) { + if (pins == null) + volts[i] = new Double(st.nextToken()).doubleValue(); + else if (pins[i].state) { + volts[i] = new Double(st.nextToken()).doubleValue(); + pins[i].value = volts[i] > getThreshold(); + } + } + } + + boolean needsBits() { + return false; + } + + boolean hasCustomVoltage() { + return (flags & FLAG_CUSTOM_VOLTAGE) != 0; + } + + boolean isDigitalChip() { + return true; + } + + double getThreshold() { + return highVoltage / 2; + } + + int defaultBitCount() { + return 4; + } + + 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; + g.save(); + Font f = new Font("normal", 0, 10 * csize); // FontMetrics fm = g.getFontMetrics(); - boolean hasVertical = false; - // check if there are any vertical pins. if not, we can make the labels wider - for (i = 0; i != getPostCount(); i++) - if (pins[i].side == SIDE_N || pins[i].side == SIDE_S) { - hasVertical = true; - break; - } - for (i = 0; i != getPostCount(); i++) { - g.setFont(f); - 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.getBackgroundColor()); - drawThickCircle(g, p.bubbleX, p.bubbleY, 1); - g.setColor(lightGrayColor); - drawThickCircle(g, p.bubbleX, p.bubbleY, 3); - } - if (p.clockPointsX != null) { - g.setColor(lightGrayColor); - g.drawPolyline(p.clockPointsX, p.clockPointsY, 3); - } - g.setColor(p.selected ? selectColor : whiteColor); - int fsz = 10*csize; - double availSpace = cspc*2-8; - // allow a little more space if the chip is wide and there are no vertical pins - // (we could still do this if vertical pins are present but then we would have to do - // more work to avoid overlaps) - if (!hasVertical && sizeX > 2) - availSpace = cspc*2.5+cspc*(sizeX-3); - while (true) { - int sw=(int)g.context.measureText(p.text).getWidth(); - // scale font down if it's too big - if (sw > availSpace) { - fsz -= 1; - Font f2 = new Font("normal", 0, fsz); - g.setFont(f2); - continue; - } - int asc=(int)g.currentFontSize; - int tx; - // put text closer to edge if it's on left or right. - if (p.side == flippedXSide(SIDE_W)) - tx = p.textloc.x-(cspc-5); - else if (p.side == flippedXSide(SIDE_E)) - tx = p.textloc.x+(cspc-5)-sw; - else - tx = p.textloc.x-sw/2; - g.drawString(p.text, tx, p.textloc.y+asc/3); - if (p.lineOver) { - int ya = p.textloc.y-asc+asc/3; - g.drawLine(tx, ya, tx+sw, ya); - } - break; - } - } - - drawLabel(g, labelX, labelY); - g.setColor(needsHighlight() ? selectColor : lightGrayColor); - drawThickPolygon(g, rectPointsX, rectPointsY, 4); - drawPosts(g); - g.restore(); - } - int rectPointsX[], rectPointsY[]; - Pin pins[]; - int sizeX, sizeY, flippedSizeX, flippedSizeY; - 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 drawLabel(Graphics g, int x, int y) {} - int labelX, labelY; - - void setPoints() { - if (x2-x > sizeX*cspc2 && this == sim.dragElm) - setSize(2); - int x0 = x+cspc2; int y0 = y; - int xr = x0-cspc; - int yr = y0-cspc; - flippedSizeX = sizeX; - flippedSizeY = sizeY; - if (isFlippedXY()) { - flippedSizeX = sizeY; - flippedSizeY = sizeX; - } - int xs = flippedSizeX*cspc2; - int ys = flippedSizeY*cspc2; - int i; - for (i = 0; i != getPostCount(); i++) { - Pin p = pins[i]; - p.side = p.side0; - if ((flags & FLAG_FLIP_XY) != 0) - p.side = sideFlipXY[p.side]; - 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; - } - } - 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]); - labelX = xr+xs/2; - labelY = yr+ys/2; - } - - // see if we can move pin to position xp, yp, and return the new position - boolean getPinPos(int xp, int yp, int pin, int pos[]) { - int x0 = x+cspc2; int y0 = y; - int xr = x0-cspc; - int yr = y0-cspc; - double xd = (xp-xr)/(double)cspc2 - .5; - double yd = (yp-yr)/(double)cspc2 - .5; - if (xd < .25 && yd > 0 && yd < sizeY-1) { - pos[0] = (int) Math.max(Math.round(yd), 0); - pos[1] = SIDE_W; - } else if (xd > sizeX-.75) { - pos[0] = (int) Math.min(Math.round(yd), sizeY-1); - pos[1] = SIDE_E; - } else if (yd < .25) { - pos[0] = (int) Math.max(Math.round(xd), 0); - pos[1] = SIDE_N; - } else if (yd > sizeY-.75) { - pos[0] = (int) Math.min(Math.round(xd), sizeX-1); - pos[1] = SIDE_S; - } else - return false; - - if (pos[0] < 0) - return false; - if ((pos[1] == SIDE_N || pos[1] == SIDE_S) && pos[0] >= sizeX) - return false; - if ((pos[1] == SIDE_W || pos[1] == SIDE_E) && pos[0] >= sizeY) - return false; - return true; - } - - int getOverlappingPin(int p1, int p2, int pin) { - for (int i = 0; i != getPostCount(); i++) { - if (pin == i) - continue; - if (pins[i].overlaps(p1, p2)) - return i; - } - return -1; - } - - 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; - int vsc = 0; - for (i = 0; i != getPostCount(); i++) { - Pin p = pins[i]; - if (p.output) { - sim.stampVoltageSource(0, nodes[i], p.voltSource); - vsc++; - } - } - if (vsc != getVoltageSourceCount()) - CirSim.console("voltage source count does not match number of outputs"); - } - void execute() {} - void doStep() { - int i; - for (i = 0; i != getPostCount(); i++) { - Pin p = pins[i]; - if (!p.output) - p.value = volts[i] > getThreshold(); - } - execute(); - for (i = 0; i != getPostCount(); i++) { - Pin p = pins[i]; - if (p.output) - sim.updateVoltageSource(0, nodes[i], p.voltSource, - p.value ? highVoltage : 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() { - if (highVoltage == 5) - flags &= ~FLAG_CUSTOM_VOLTAGE; - else - flags |= FLAG_CUSTOM_VOLTAGE; - - String s = super.dump(); - if (needsBits()) - s += " " + bits; - if (hasCustomVoltage()) - s += " " + highVoltage; - int i; - for (i = 0; i != getPostCount(); i++) { - if (pins[i].state) - s += " " + volts[i]; - } - return s; - } - - void writeOutput(int n, boolean value) { - if (!pins[n].output) - CirSim.console("pin " + n + " is not an output!"); - pins[n].value = value; - } - - 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; - } - - double getCurrentIntoNode(int n) { - return pins[n].current; - } - - boolean isFlippedXY() { return (flags & FLAG_FLIP_XY) != 0; } - - 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; - } - if (n == 2) { - EditInfo ei = new EditInfo("", 0, -1, -1); - ei.checkbox = new Checkbox("Flip X/Y", (flags & FLAG_FLIP_XY) != 0); - return ei; - } - if (!isDigitalChip()) - return getChipEditInfo(n-3); - - if (n == 3) - return new EditInfo("High Logic Voltage", highVoltage); - - return getChipEditInfo(n-4); - } - public void setEditValue(int n, EditInfo ei) { - if (n == 0) { - flags = ei.changeFlag(flags, FLAG_FLIP_X); - setPoints(); - } - if (n == 1) { - flags = ei.changeFlag(flags, FLAG_FLIP_Y); - setPoints(); - } - if (n == 2) { - flags = ei.changeFlag(flags, FLAG_FLIP_XY); - setPoints(); - } - if (!isDigitalChip()) { - if (n >= 3) - setChipEditValue(n-3, ei); - return; - } - - if (n == 3) - highVoltage = ei.value; - - if (n >= 4) - setChipEditValue(n-4, ei); - } - - public EditInfo getChipEditInfo(int n) { return null; } - public void setChipEditValue(int n, EditInfo ei) { } - - static String writeBits(boolean[] data) { - StringBuilder sb = new StringBuilder(); - int integer = 0; - int bitIndex = 0; - for (int i = 0; i < data.length; i++) { - if (bitIndex >= Integer.SIZE) { - //Flush completed integer - sb.append(' '); - sb.append(integer); - integer = 0; - bitIndex = 0; - } - if (data[i]) - integer |= 1 << bitIndex; - bitIndex++; - } - if (bitIndex > 0) { - sb.append(' '); - sb.append(integer); - } - return sb.toString(); - } - static void readBits(StringTokenizer st, boolean[] output) { - int integer = 0; - int bitIndex = Integer.MAX_VALUE; - for (int i = 0; i < output.length; i++) { - if (bitIndex >= Integer.SIZE) - if (st.hasMoreTokens()) { - integer = Integer.parseInt(st.nextToken()); //Load next integer - bitIndex = 0; - } else - break; //Data is absent - - output[i] = (integer & (1 << bitIndex)) != 0; - bitIndex++; - } - } - - static final int SIDE_N = 0; - static final int SIDE_S = 1; - static final int SIDE_W = 2; - static final int SIDE_E = 3; - - static final int sideFlipXY[] = { SIDE_W, SIDE_E, SIDE_N, SIDE_S }; - - int flippedXSide(int s) { - if ((flags & FLAG_FLIP_X) == 0) - return s; - if (s == SIDE_W) - return SIDE_E; - if (s == SIDE_E) - return SIDE_W; - return s; - } - - class Pin { - Pin(int p, int s, String t) { - pos = p; side0 = side = s; text = t; - } - Point post, stub; - Point textloc; - int pos, side, side0, voltSource, bubbleX, bubbleY; - String text; - boolean lineOver, bubble, clock, output, value, state, selected; - double curcount, current; - int clockPointsX[], clockPointsY[]; - 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*(flippedSizeX-1); - sx = -sx; - } - if ((flags & FLAG_FLIP_Y) != 0) { - dy = -dy; - day = -day; - py += cspc2*(flippedSizeY-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) { - if (clockPointsX == null) { - 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; - if (text.length() > 0) { - // See for example http://127.0.0.1:8000/circuitjs.html?ctz=CQAgjCAMB0l3BWcMBMcUHYMGZIA4UA2ATmIxAUgpABZsKBTAWjDACgAncDQkPKlDSr8oySGzTkwPPlTAo8s2iADCAGQDSALgCSAOQBqWle0khBwgUJDYUy9dv1GVKCZHIXwNGubyKw3vaauobG2G5SMgE+0rxgxHY+DiHONJzcvCKxXj5y8OnZ0ebWRXlw6Z5FniJl4kA - clockPointsX[1] += dax*cspc/2; - clockPointsY[1] += day*cspc/2; - textloc.x -= dax*cspc/2; - textloc.y -= day*cspc/4; - } - } - else { - clockPointsX = null; - clockPointsY = null; - } - } - - // convert position, side to a grid position (0=top left) so we can detect overlaps - int toGrid(int p, int s) { - if (s == SIDE_N) - return p; - if (s == SIDE_S) - return p+sizeX*(sizeY-1); - if (s == SIDE_W) - return p*sizeX; - if (s == SIDE_E) - return p*sizeX+sizeX-1; - return -1; - } - - boolean overlaps(int p, int s) { - int g = toGrid(p, s); - if (g == -1) - return true; - return toGrid(pos, side) == g; - } - - void fixName() { - if (text.startsWith("/")) { - text = text.substring(1); - lineOver = true; - } - else if (text.startsWith("#")) { - text = text.substring(1); - bubble = true; - } - - String result = text.replaceAll("CLK:", ""); - if (result.length() != text.length()) { - clock = true; - text = result; - } - result = text.replaceAll("INV:", ""); - if (result.length() != text.length()) { - bubble = true; - text = result; - } - - if (text.compareToIgnoreCase("clk") == 0) { - text = ""; - clock = true; - } - } - - - } + boolean hasVertical = false; + // check if there are any vertical pins. if not, we can make the labels wider + for (i = 0; i != getPostCount(); i++) + if (pins[i].side == SIDE_N || pins[i].side == SIDE_S) { + hasVertical = true; + break; + } + for (i = 0; i != getPostCount(); i++) { + g.setFont(f); + 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.getBackgroundColor()); + drawThickCircle(g, p.bubbleX, p.bubbleY, 1); + g.setColor(lightGrayColor); + drawThickCircle(g, p.bubbleX, p.bubbleY, 3); + } + if (p.clockPointsX != null) { + g.setColor(lightGrayColor); + g.drawPolyline(p.clockPointsX, p.clockPointsY, 3); + } + g.setColor(p.selected ? selectColor : whiteColor); + int fsz = 10 * csize; + double availSpace = cspc * 2 - 8; + // allow a little more space if the chip is wide and there are no vertical pins + // (we could still do this if vertical pins are present but then we would have to do + // more work to avoid overlaps) + if (!hasVertical && sizeX > 2) + availSpace = cspc * 2.5 + cspc * (sizeX - 3); + while (true) { + int sw = (int) g.context.measureText(p.text).getWidth(); + // scale font down if it's too big + if (sw > availSpace) { + fsz -= 1; + Font f2 = new Font("normal", 0, fsz); + g.setFont(f2); + continue; + } + int asc = (int) g.currentFontSize; + int tx; + // put text closer to edge if it's on left or right. + if (p.side == flippedXSide(SIDE_W)) + tx = p.textloc.x - (cspc - 5); + else if (p.side == flippedXSide(SIDE_E)) + tx = p.textloc.x + (cspc - 5) - sw; + else + tx = p.textloc.x - sw / 2; + g.drawString(p.text, tx, p.textloc.y + asc / 3); + if (p.lineOver) { + int ya = p.textloc.y - asc + asc / 3; + g.drawLine(tx, ya, tx + sw, ya); + } + break; + } + } + + drawLabel(g, labelX, labelY); + g.setColor(needsHighlight() ? selectColor : lightGrayColor); + drawThickPolygon(g, rectPointsX, rectPointsY, 4); + drawPosts(g); + g.restore(); + } + + int rectPointsX[], rectPointsY[]; + Pin pins[]; + int sizeX, sizeY, flippedSizeX, flippedSizeY; + 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 drawLabel(Graphics g, int x, int y) { + } + + int labelX, labelY; + + void setPoints() { + if (x2 - x > sizeX * cspc2 && this == sim.dragElm) + setSize(2); + int x0 = x + cspc2; + int y0 = y; + int xr = x0 - cspc; + int yr = y0 - cspc; + flippedSizeX = sizeX; + flippedSizeY = sizeY; + if (isFlippedXY()) { + flippedSizeX = sizeY; + flippedSizeY = sizeX; + } + int xs = flippedSizeX * cspc2; + int ys = flippedSizeY * cspc2; + int i; + for (i = 0; i != getPostCount(); i++) { + Pin p = pins[i]; + p.side = p.side0; + if ((flags & FLAG_FLIP_XY) != 0) + p.side = sideFlipXY[p.side]; + 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; + } + } + 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]); + labelX = xr + xs / 2; + labelY = yr + ys / 2; + } + + // see if we can move pin to position xp, yp, and return the new position + boolean getPinPos(int xp, int yp, int pin, int pos[]) { + int x0 = x + cspc2; + int y0 = y; + int xr = x0 - cspc; + int yr = y0 - cspc; + double xd = (xp - xr) / (double) cspc2 - .5; + double yd = (yp - yr) / (double) cspc2 - .5; + if (xd < .25 && yd > 0 && yd < sizeY - 1) { + pos[0] = (int) Math.max(Math.round(yd), 0); + pos[1] = SIDE_W; + } else if (xd > sizeX - .75) { + pos[0] = (int) Math.min(Math.round(yd), sizeY - 1); + pos[1] = SIDE_E; + } else if (yd < .25) { + pos[0] = (int) Math.max(Math.round(xd), 0); + pos[1] = SIDE_N; + } else if (yd > sizeY - .75) { + pos[0] = (int) Math.min(Math.round(xd), sizeX - 1); + pos[1] = SIDE_S; + } else + return false; + + if (pos[0] < 0) + return false; + if ((pos[1] == SIDE_N || pos[1] == SIDE_S) && pos[0] >= sizeX) + return false; + if ((pos[1] == SIDE_W || pos[1] == SIDE_E) && pos[0] >= sizeY) + return false; + return true; + } + + int getOverlappingPin(int p1, int p2, int pin) { + for (int i = 0; i != getPostCount(); i++) { + if (pin == i) + continue; + if (pins[i].overlaps(p1, p2)) + return i; + } + return -1; + } + + 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; + int vsc = 0; + for (i = 0; i != getPostCount(); i++) { + Pin p = pins[i]; + if (p.output) { + sim.stampVoltageSource(0, nodes[i], p.voltSource); + vsc++; + } + } + if (vsc != getVoltageSourceCount()) + CirSim.console("voltage source count does not match number of outputs"); + } + + void execute() { + } + + void doStep() { + int i; + for (i = 0; i != getPostCount(); i++) { + Pin p = pins[i]; + if (!p.output) + p.value = volts[i] > getThreshold(); + } + execute(); + for (i = 0; i != getPostCount(); i++) { + Pin p = pins[i]; + if (p.output) + sim.updateVoltageSource(0, nodes[i], p.voltSource, + p.value ? highVoltage : 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() { + if (highVoltage == 5) + flags &= ~FLAG_CUSTOM_VOLTAGE; + else + flags |= FLAG_CUSTOM_VOLTAGE; + + String s = super.dump(); + if (needsBits()) + s += " " + bits; + if (hasCustomVoltage()) + s += " " + highVoltage; + int i; + for (i = 0; i != getPostCount(); i++) { + if (pins[i].state) + s += " " + volts[i]; + } + return s; + } + + void writeOutput(int n, boolean value) { + if (!pins[n].output) + CirSim.console("pin " + n + " is not an output!"); + pins[n].value = value; + } + + 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; + } + + double getCurrentIntoNode(int n) { + return pins[n].current; + } + + boolean isFlippedXY() { + return (flags & FLAG_FLIP_XY) != 0; + } + + 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; + } + if (n == 2) { + EditInfo ei = new EditInfo("", 0, -1, -1); + ei.checkbox = new Checkbox("Flip X/Y", (flags & FLAG_FLIP_XY) != 0); + return ei; + } + if (!isDigitalChip()) + return getChipEditInfo(n - 3); + + if (n == 3) + return new EditInfo("High Logic Voltage", highVoltage); + + return getChipEditInfo(n - 4); + } + + public void setEditValue(int n, EditInfo ei) { + if (n == 0) { + flags = ei.changeFlag(flags, FLAG_FLIP_X); + setPoints(); + } + if (n == 1) { + flags = ei.changeFlag(flags, FLAG_FLIP_Y); + setPoints(); + } + if (n == 2) { + flags = ei.changeFlag(flags, FLAG_FLIP_XY); + setPoints(); + } + if (!isDigitalChip()) { + if (n >= 3) + setChipEditValue(n - 3, ei); + return; + } + + if (n == 3) + highVoltage = ei.value; + + if (n >= 4) + setChipEditValue(n - 4, ei); + } + + public EditInfo getChipEditInfo(int n) { + return null; + } + + public void setChipEditValue(int n, EditInfo ei) { + } + + static String writeBits(boolean[] data) { + StringBuilder sb = new StringBuilder(); + int integer = 0; + int bitIndex = 0; + for (int i = 0; i < data.length; i++) { + if (bitIndex >= Integer.SIZE) { + //Flush completed integer + sb.append(' '); + sb.append(integer); + integer = 0; + bitIndex = 0; + } + if (data[i]) + integer |= 1 << bitIndex; + bitIndex++; + } + if (bitIndex > 0) { + sb.append(' '); + sb.append(integer); + } + return sb.toString(); + } + + static void readBits(StringTokenizer st, boolean[] output) { + int integer = 0; + int bitIndex = Integer.MAX_VALUE; + for (int i = 0; i < output.length; i++) { + if (bitIndex >= Integer.SIZE) + if (st.hasMoreTokens()) { + integer = Integer.parseInt(st.nextToken()); //Load next integer + bitIndex = 0; + } else + break; //Data is absent + + output[i] = (integer & (1 << bitIndex)) != 0; + bitIndex++; + } + } + + static final int SIDE_N = 0; + static final int SIDE_S = 1; + static final int SIDE_W = 2; + static final int SIDE_E = 3; + + static final int sideFlipXY[] = {SIDE_W, SIDE_E, SIDE_N, SIDE_S}; + + int flippedXSide(int s) { + if ((flags & FLAG_FLIP_X) == 0) + return s; + if (s == SIDE_W) + return SIDE_E; + if (s == SIDE_E) + return SIDE_W; + return s; + } + + class Pin { + Pin(int p, int s, String t) { + pos = p; + side0 = side = s; + text = t; + } + + Point post, stub; + Point textloc; + int pos, side, side0, voltSource, bubbleX, bubbleY; + String text; + boolean lineOver, bubble, clock, output, value, state, selected; + double curcount, current; + int clockPointsX[], clockPointsY[]; + + 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 * (flippedSizeX - 1); + sx = -sx; + } + if ((flags & FLAG_FLIP_Y) != 0) { + dy = -dy; + day = -day; + py += cspc2 * (flippedSizeY - 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) { + if (clockPointsX == null) { + 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; + if (text.length() > 0) { + // See for example http://127.0.0.1:8000/circuitjs.html?ctz=CQAgjCAMB0l3BWcMBMcUHYMGZIA4UA2ATmIxAUgpABZsKBTAWjDACgAncDQkPKlDSr8oySGzTkwPPlTAo8s2iADCAGQDSALgCSAOQBqWle0khBwgUJDYUy9dv1GVKCZHIXwNGubyKw3vaauobG2G5SMgE+0rxgxHY+DiHONJzcvCKxXj5y8OnZ0ebWRXlw6Z5FniJl4kA + clockPointsX[1] += dax * cspc / 2; + clockPointsY[1] += day * cspc / 2; + textloc.x -= dax * cspc / 2; + textloc.y -= day * cspc / 4; + } + } else { + clockPointsX = null; + clockPointsY = null; + } + } + + // convert position, side to a grid position (0=top left) so we can detect overlaps + int toGrid(int p, int s) { + if (s == SIDE_N) + return p; + if (s == SIDE_S) + return p + sizeX * (sizeY - 1); + if (s == SIDE_W) + return p * sizeX; + if (s == SIDE_E) + return p * sizeX + sizeX - 1; + return -1; + } + + boolean overlaps(int p, int s) { + int g = toGrid(p, s); + if (g == -1) + return true; + return toGrid(pos, side) == g; + } + + void fixName() { + if (text.startsWith("/")) { + text = text.substring(1); + lineOver = true; + } else if (text.startsWith("#")) { + text = text.substring(1); + bubble = true; + } + + String result = text.replaceAll("CLK:", ""); + if (result.length() != text.length()) { + clock = true; + text = result; + } + result = text.replaceAll("INV:", ""); + if (result.length() != text.length()) { + bubble = true; + text = result; + } + + if (text.compareToIgnoreCase("clk") == 0) { + text = ""; + clock = true; + } + } + + } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/Choice.java b/src/main/java/com/lushprojects/circuitjs1/client/Choice.java index acbb153..4b4f912 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/Choice.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/Choice.java @@ -23,16 +23,16 @@ import com.lushprojects.circuitjs1.client.util.Locale; public class Choice extends ListBox { - - Choice() { - super(); - } - - public void add(String s){ - this.addItem(Locale.LS(s)); - } - - public void select(int i){ - this.setSelectedIndex(i); - } + + Choice() { + super(); + } + + public void add(String s) { + this.addItem(Locale.LS(s)); + } + + public void select(int i) { + this.setSelectedIndex(i); + } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/CirSim.java b/src/main/java/com/lushprojects/circuitjs1/client/CirSim.java index 2f5ec18..d877b71 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/CirSim.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/CirSim.java @@ -92,7 +92,9 @@ import com.google.gwt.safehtml.shared.SafeHtmlUtils; import com.google.gwt.storage.client.Storage; import com.google.gwt.user.client.ui.PopupPanel; + import static com.google.gwt.event.dom.client.KeyCodes.*; + import com.google.gwt.user.client.ui.Frame; import com.google.gwt.user.client.ui.Widget; import com.lushprojects.circuitjs1.client.matrix.DMatrixSparseCSC; @@ -103,12 +105,11 @@ import com.google.gwt.user.client.Window.Navigator; import com.google.gwt.event.logical.shared.ResizeEvent; import com.google.gwt.event.logical.shared.ResizeHandler; -import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.ui.ScrollPanel; public class CirSim implements MouseDownHandler, MouseMoveHandler, MouseUpHandler, -ClickHandler, DoubleClickHandler, ContextMenuHandler, NativePreviewHandler, -MouseOutHandler, MouseWheelHandler { + ClickHandler, DoubleClickHandler, ContextMenuHandler, NativePreviewHandler, + MouseOutHandler, MouseWheelHandler { Random random; Button resetButton; @@ -161,7 +162,7 @@ public class CirSim implements MouseDownHandler, MouseMoveHandler, MouseUpHandle MenuItem combineAllItem; MenuItem separateAllItem; MenuBar mainMenuBar; - boolean hideMenu = false; + boolean hideMenu = false; MenuBar selectScopeMenuBar; Vector selectScopeMenuItems; MenuBar subcircuitMenuBar[]; @@ -169,7 +170,7 @@ public class CirSim implements MouseDownHandler, MouseMoveHandler, MouseUpHandle MenuItem scopeSelectYMenuItem; ScopePopupMenu scopePopupMenu; Element sidePanelCheckboxLabel; - + String lastCursorStyle; boolean mouseWasOverSplitter = false; @@ -287,7 +288,7 @@ public class CirSim implements MouseDownHandler, MouseMoveHandler, MouseUpHandle Vector mainMenuItemNames = new Vector(); LoadFile loadFileInput; - Frame iFrame=null; + Frame iFrame = null; Canvas cv; Context2d cvcontext; @@ -328,48 +329,48 @@ native boolean isMobile(Element element) /*-{ var style = getComputedStyle(element); return style.display != 'none'; }-*/; - - public void setCanvasSize(){ - int width, height; - width=(int)RootLayoutPanel.get().getOffsetWidth(); - height=(int)RootLayoutPanel.get().getOffsetHeight(); - height=height-(hideMenu?0:MENUBARHEIGHT); - - //not needed on mobile since the width of the canvas' container div is set to 100% in ths CSS file - if (!isMobile(sidePanelCheckboxLabel)) - width=width-VERTICALPANELWIDTH; - - width = Math.max(width, 0); // avoid exception when setting negative width - height = Math.max(height, 0); - - if (cv != null) { - cv.setWidth(width + "PX"); - cv.setHeight(height + "PX"); - canvasWidth = width; - canvasHeight = height; - float scale = devicePixelRatio(); - cv.setCoordinateSpaceWidth((int)(width*scale)); - cv.setCoordinateSpaceHeight((int)(height*scale)); - } - - setCircuitArea(); - - // recenter circuit in case canvas was hidden at startup - if (transform[0] == 0) - centreCircuit(); + + public void setCanvasSize() { + int width, height; + width = (int) RootLayoutPanel.get().getOffsetWidth(); + height = (int) RootLayoutPanel.get().getOffsetHeight(); + height = height - (hideMenu ? 0 : MENUBARHEIGHT); + + //not needed on mobile since the width of the canvas' container div is set to 100% in ths CSS file + if (!isMobile(sidePanelCheckboxLabel)) + width = width - VERTICALPANELWIDTH; + + width = Math.max(width, 0); // avoid exception when setting negative width + height = Math.max(height, 0); + + if (cv != null) { + cv.setWidth(width + "PX"); + cv.setHeight(height + "PX"); + canvasWidth = width; + canvasHeight = height; + float scale = devicePixelRatio(); + cv.setCoordinateSpaceWidth((int) (width * scale)); + cv.setCoordinateSpaceHeight((int) (height * scale)); + } + + setCircuitArea(); + + // recenter circuit in case canvas was hidden at startup + if (transform[0] == 0) + centreCircuit(); } - + void setCircuitArea() { - int height = canvasHeight; - int width = canvasWidth; - int h = (int) ((double)height * scopeHeightFraction); + int height = canvasHeight; + int width = canvasWidth; + int h = (int) ((double) height * scopeHeightFraction); /*if (h < 128 && winSize.height > 300) h = 128;*/ - if (scopeCount == 0) - h = 0; - circuitArea = new Rectangle(0, 0, width, height-h); + if (scopeCount == 0) + h = 0; + circuitArea = new Rectangle(0, 0, width, height - h); } - + native String decompress(String dump) /*-{ return $wnd.LZString.decompressFromEncodedURIComponent(dump); }-*/; @@ -380,7 +381,7 @@ native String decompress(String dump) /*-{ // super("Circuit Simulator v1.6d"); // applet = a; // useFrame = false; - theSim = this; + theSim = this; } String startCircuit = null; @@ -388,139 +389,141 @@ native String decompress(String dump) /*-{ String startCircuitText = null; String startCircuitLink = null; // String baseURL = "http://www.falstad.com/circuit/"; - + public void init() { - //sets the meta tag to allow the css media queries to work - MetaElement meta = Document.get().createMetaElement(); - meta.setName("viewport"); - meta.setContent("width=device-width"); - NodeList node = Document.get().getElementsByTagName("head"); - node.getItem(0).appendChild(meta); - - - boolean printable = false; - boolean convention = true; - boolean euroRes = false; - boolean usRes = false; - boolean running = true; - boolean hideSidebar = false; - boolean noEditing = false; - boolean mouseWheelEdit = false; - MenuBar m; - - CircuitElm.initClass(this); - readRecovery(); - - QueryParameters qp = new QueryParameters(); - String positiveColor = null; - String negativeColor = null; - String neutralColor = null; - String selectColor = null; - String currentColor = null; - String mouseModeReq = null; - boolean euroGates = false; - - try { - //baseURL = applet.getDocumentBase().getFile(); - // look for circuit embedded in URL - // String doc = applet.getDocumentBase().toString(); - String cct=qp.getValue("cct"); - if (cct!=null) - startCircuitText = cct.replace("%24", "$"); - if (startCircuitText == null) - startCircuitText = getElectronStartCircuitText(); - String ctz=qp.getValue("ctz"); - if (ctz!= null) - startCircuitText = decompress(ctz); - startCircuit = qp.getValue("startCircuit"); - startLabel = qp.getValue("startLabel"); - startCircuitLink = qp.getValue("startCircuitLink"); - euroRes = qp.getBooleanValue("euroResistors", false); - euroGates = qp.getBooleanValue("IECGates", getOptionFromStorage("euroGates", weAreInGermany())); - usRes = qp.getBooleanValue("usResistors", false); - running = qp.getBooleanValue("running", true); - hideSidebar = qp.getBooleanValue("hideSidebar", false); - hideMenu = qp.getBooleanValue("hideMenu", false); - printable = qp.getBooleanValue("whiteBackground", getOptionFromStorage("whiteBackground", false)); - convention = qp.getBooleanValue("conventionalCurrent", - getOptionFromStorage("conventionalCurrent", true)); - noEditing = !qp.getBooleanValue("editable", true); - mouseWheelEdit = qp.getBooleanValue("mouseWheelEdit", getOptionFromStorage("mouseWheelEdit", true)); - positiveColor = qp.getValue("positiveColor"); - negativeColor = qp.getValue("negativeColor"); - neutralColor = qp.getValue("neutralColor"); - selectColor = qp.getValue("selectColor"); - currentColor = qp.getValue("currentColor"); - mouseModeReq = qp.getValue("mouseMode"); - hideInfoBox = qp.getBooleanValue("hideInfoBox", false); - } catch (Exception e) { } - - boolean euroSetting = false; - if (euroRes) - euroSetting = true; - else if (usRes) - euroSetting = false; - else - euroSetting = getOptionFromStorage("euroResistors", !weAreInUS(true)); - - transform = new double[6]; - String os = Navigator.getPlatform(); - isMac = (os.toLowerCase().contains("mac")); - ctrlMetaKey = (isMac) ? Locale.LS("Cmd-") : Locale.LS("Ctrl-"); - - shortcuts = new String[127]; - - layoutPanel = new DockLayoutPanel(Unit.PX); - - fileMenuBar = new MenuBar(true); - if (isElectron()) - fileMenuBar.addItem(menuItemWithShortcut("window", "New Window...", Locale.LS(ctrlMetaKey + "N"), - new MyCommand("file", "newwindow"))); - - fileMenuBar = new MenuBar(true); - fileMenuBar.addItem(iconMenuItem("popup", "New Window...", - new Command() { public void execute(){ - ScriptInjector.fromString("nw.Window.open('index.html', {}, function(new_win) {});") - .setRemoveTag(false) - .setWindow(ScriptInjector.TOP_WINDOW) - .inject(); - } - })); - - fileMenuBar.addItem(iconMenuItem("doc-new", "New Blank Circuit", new MyCommand("file", "newblankcircuit"))); - importFromLocalFileItem = menuItemWithShortcut("folder", "Open File...", Locale.LS(ctrlMetaKey + "O"), - new MyCommand("file","importfromlocalfile")); - importFromLocalFileItem.setEnabled(LoadFile.isSupported()); - fileMenuBar.addItem(importFromLocalFileItem); - importFromTextItem = iconMenuItem("doc-text", "Import From Text...", new MyCommand("file","importfromtext")); - fileMenuBar.addItem(importFromTextItem); - //importFromDropboxItem = iconMenuItem("dropbox", "Import From Dropbox...", new MyCommand("file", "importfromdropbox")); - //fileMenuBar.addItem(importFromDropboxItem); - if (isElectron()) { - saveFileItem = fileMenuBar.addItem(menuItemWithShortcut("floppy", "Save", Locale.LS(ctrlMetaKey + "S"), - new MyCommand("file", "save"))); - fileMenuBar.addItem(iconMenuItem("floppy", "Save As...", new MyCommand("file", "saveas"))); - } else { - exportAsLocalFileItem = menuItemWithShortcut("floppy", "Save As...", Locale.LS(ctrlMetaKey + "S"), - new MyCommand("file","exportaslocalfile")); - exportAsLocalFileItem.setEnabled(ExportAsLocalFileDialog.downloadIsSupported()); - fileMenuBar.addItem(exportAsLocalFileItem); - } - exportAsUrlItem = iconMenuItem("export", "Export As Link...", new MyCommand("file","exportasurl")); - fileMenuBar.addItem(exportAsUrlItem); - exportAsTextItem = iconMenuItem("export", "Export As Text...", new MyCommand("file","exportastext")); - fileMenuBar.addItem(exportAsTextItem); - fileMenuBar.addItem(iconMenuItem("export", "Export As Image...", new MyCommand("file","exportasimage"))); - fileMenuBar.addItem(iconMenuItem("export", "Export As SVG...", new MyCommand("file","exportassvg"))); - fileMenuBar.addItem(iconMenuItem("microchip", "Create Subcircuit...", new MyCommand("file","createsubcircuit"))); - fileMenuBar.addItem(iconMenuItem("magic", "Find DC Operating Point", new MyCommand("file", "dcanalysis"))); - recoverItem = iconMenuItem("back-in-time", "Recover Auto-Save", new MyCommand("file","recover")); - recoverItem.setEnabled(recovery != null); - fileMenuBar.addItem(recoverItem); - printItem = menuItemWithShortcut("print", "Print...", Locale.LS(ctrlMetaKey + "P"), new MyCommand("file","print")); - fileMenuBar.addItem(printItem); - fileMenuBar.addSeparator(); + //sets the meta tag to allow the css media queries to work + MetaElement meta = Document.get().createMetaElement(); + meta.setName("viewport"); + meta.setContent("width=device-width"); + NodeList node = Document.get().getElementsByTagName("head"); + node.getItem(0).appendChild(meta); + + + boolean printable = false; + boolean convention = true; + boolean euroRes = false; + boolean usRes = false; + boolean running = true; + boolean hideSidebar = false; + boolean noEditing = false; + boolean mouseWheelEdit = false; + MenuBar m; + + CircuitElm.initClass(this); + readRecovery(); + + QueryParameters qp = new QueryParameters(); + String positiveColor = null; + String negativeColor = null; + String neutralColor = null; + String selectColor = null; + String currentColor = null; + String mouseModeReq = null; + boolean euroGates = false; + + try { + //baseURL = applet.getDocumentBase().getFile(); + // look for circuit embedded in URL + // String doc = applet.getDocumentBase().toString(); + String cct = qp.getValue("cct"); + if (cct != null) + startCircuitText = cct.replace("%24", "$"); + if (startCircuitText == null) + startCircuitText = getElectronStartCircuitText(); + String ctz = qp.getValue("ctz"); + if (ctz != null) + startCircuitText = decompress(ctz); + startCircuit = qp.getValue("startCircuit"); + startLabel = qp.getValue("startLabel"); + startCircuitLink = qp.getValue("startCircuitLink"); + euroRes = qp.getBooleanValue("euroResistors", false); + euroGates = qp.getBooleanValue("IECGates", getOptionFromStorage("euroGates", weAreInGermany())); + usRes = qp.getBooleanValue("usResistors", false); + running = qp.getBooleanValue("running", true); + hideSidebar = qp.getBooleanValue("hideSidebar", false); + hideMenu = qp.getBooleanValue("hideMenu", false); + printable = qp.getBooleanValue("whiteBackground", getOptionFromStorage("whiteBackground", false)); + convention = qp.getBooleanValue("conventionalCurrent", + getOptionFromStorage("conventionalCurrent", true)); + noEditing = !qp.getBooleanValue("editable", true); + mouseWheelEdit = qp.getBooleanValue("mouseWheelEdit", getOptionFromStorage("mouseWheelEdit", true)); + positiveColor = qp.getValue("positiveColor"); + negativeColor = qp.getValue("negativeColor"); + neutralColor = qp.getValue("neutralColor"); + selectColor = qp.getValue("selectColor"); + currentColor = qp.getValue("currentColor"); + mouseModeReq = qp.getValue("mouseMode"); + hideInfoBox = qp.getBooleanValue("hideInfoBox", false); + } catch (Exception e) { + } + + boolean euroSetting = false; + if (euroRes) + euroSetting = true; + else if (usRes) + euroSetting = false; + else + euroSetting = getOptionFromStorage("euroResistors", !weAreInUS(true)); + + transform = new double[6]; + String os = Navigator.getPlatform(); + isMac = (os.toLowerCase().contains("mac")); + ctrlMetaKey = (isMac) ? Locale.LS("Cmd-") : Locale.LS("Ctrl-"); + + shortcuts = new String[127]; + + layoutPanel = new DockLayoutPanel(Unit.PX); + + fileMenuBar = new MenuBar(true); + if (isElectron()) + fileMenuBar.addItem(menuItemWithShortcut("window", "New Window...", Locale.LS(ctrlMetaKey + "N"), + new MyCommand("file", "newwindow"))); + + fileMenuBar = new MenuBar(true); + fileMenuBar.addItem(iconMenuItem("popup", "New Window...", + new Command() { + public void execute() { + ScriptInjector.fromString("nw.Window.open('index.html', {}, function(new_win) {});") + .setRemoveTag(false) + .setWindow(ScriptInjector.TOP_WINDOW) + .inject(); + } + })); + + fileMenuBar.addItem(iconMenuItem("doc-new", "New Blank Circuit", new MyCommand("file", "newblankcircuit"))); + importFromLocalFileItem = menuItemWithShortcut("folder", "Open File...", Locale.LS(ctrlMetaKey + "O"), + new MyCommand("file", "importfromlocalfile")); + importFromLocalFileItem.setEnabled(LoadFile.isSupported()); + fileMenuBar.addItem(importFromLocalFileItem); + importFromTextItem = iconMenuItem("doc-text", "Import From Text...", new MyCommand("file", "importfromtext")); + fileMenuBar.addItem(importFromTextItem); + //importFromDropboxItem = iconMenuItem("dropbox", "Import From Dropbox...", new MyCommand("file", "importfromdropbox")); + //fileMenuBar.addItem(importFromDropboxItem); + if (isElectron()) { + saveFileItem = fileMenuBar.addItem(menuItemWithShortcut("floppy", "Save", Locale.LS(ctrlMetaKey + "S"), + new MyCommand("file", "save"))); + fileMenuBar.addItem(iconMenuItem("floppy", "Save As...", new MyCommand("file", "saveas"))); + } else { + exportAsLocalFileItem = menuItemWithShortcut("floppy", "Save As...", Locale.LS(ctrlMetaKey + "S"), + new MyCommand("file", "exportaslocalfile")); + exportAsLocalFileItem.setEnabled(ExportAsLocalFileDialog.downloadIsSupported()); + fileMenuBar.addItem(exportAsLocalFileItem); + } + exportAsUrlItem = iconMenuItem("export", "Export As Link...", new MyCommand("file", "exportasurl")); + fileMenuBar.addItem(exportAsUrlItem); + exportAsTextItem = iconMenuItem("export", "Export As Text...", new MyCommand("file", "exportastext")); + fileMenuBar.addItem(exportAsTextItem); + fileMenuBar.addItem(iconMenuItem("export", "Export As Image...", new MyCommand("file", "exportasimage"))); + fileMenuBar.addItem(iconMenuItem("export", "Export As SVG...", new MyCommand("file", "exportassvg"))); + fileMenuBar.addItem(iconMenuItem("microchip", "Create Subcircuit...", new MyCommand("file", "createsubcircuit"))); + fileMenuBar.addItem(iconMenuItem("magic", "Find DC Operating Point", new MyCommand("file", "dcanalysis"))); + recoverItem = iconMenuItem("back-in-time", "Recover Auto-Save", new MyCommand("file", "recover")); + recoverItem.setEnabled(recovery != null); + fileMenuBar.addItem(recoverItem); + printItem = menuItemWithShortcut("print", "Print...", Locale.LS(ctrlMetaKey + "P"), new MyCommand("file", "print")); + fileMenuBar.addItem(printItem); + fileMenuBar.addSeparator(); /* MenuBar fsub = new MenuBar(true); fsub.setAutoOpen(true); @@ -538,88 +541,89 @@ else if (usRes) .inject(); } })); - + fileMenuBar.addItem(LS("Open circuitjs1 from web"), fsub); */ - fileMenuBar.addItem(iconMenuItem("resize-full-alt", "Toggle Full Screen", new MyCommand("view", "fullscreen"))); - fileMenuBar.addSeparator(); - fileMenuBar.addItem(iconMenuItem("exit", "Exit", - new Command() { public void execute(){ - ScriptInjector.fromString("close_app()") - .setWindow(ScriptInjector.TOP_WINDOW) - .inject(); - } - })); - /* + fileMenuBar.addItem(iconMenuItem("resize-full-alt", "Toggle Full Screen", new MyCommand("view", "fullscreen"))); + fileMenuBar.addSeparator(); + fileMenuBar.addItem(iconMenuItem("exit", "Exit", + new Command() { + public void execute() { + ScriptInjector.fromString("close_app()") + .setWindow(ScriptInjector.TOP_WINDOW) + .inject(); + } + })); + /* aboutItem = iconMenuItem("info-circled", "About...", (Command)null); fileMenuBar.addItem(aboutItem); aboutItem.setScheduledCommand(new MyCommand("file","about")); */ - int width=(int)RootLayoutPanel.get().getOffsetWidth(); - VERTICALPANELWIDTH = 166; /* = width/5; + int width = (int) RootLayoutPanel.get().getOffsetWidth(); + VERTICALPANELWIDTH = 166; /* = width/5; if (VERTICALPANELWIDTH > 166) VERTICALPANELWIDTH = 166; if (VERTICALPANELWIDTH < 128) VERTICALPANELWIDTH = 128;*/ - menuBar = new MenuBar(); - menuBar.addItem(Locale.LS("File"), fileMenuBar); - verticalPanel=new VerticalPanel(); - slidersPanel = new ScrollPanel(); - verticalPanel2=new VerticalPanel(); - - verticalPanel.getElement().addClassName("verticalPanel"); - verticalPanel.getElement().setId("painel"); - Element sidePanelCheckbox = DOM.createInputCheck(); - sidePanelCheckboxLabel = DOM.createLabel(); - sidePanelCheckboxLabel.addClassName("triggerLabel"); - sidePanelCheckbox.setId("trigger"); - sidePanelCheckboxLabel.setAttribute("for", "trigger" ); - sidePanelCheckbox.addClassName("trigger"); - // addClickHandler does not work for Element but I can use onclick attribute - sidePanelCheckbox.setAttribute("onclick", "CircuitJS1.setupScopes();SetBtnsStyle();"); - Element topPanelCheckbox = DOM.createInputCheck(); - Element topPanelCheckboxLabel = DOM.createLabel(); - topPanelCheckbox.setId("toptrigger"); - topPanelCheckbox.addClassName("toptrigger"); - topPanelCheckboxLabel.addClassName("toptriggerlabel"); - topPanelCheckboxLabel.setAttribute("for", "toptrigger"); - - // make buttons side by side if there's room - buttonPanel=(VERTICALPANELWIDTH == 166) ? new HorizontalPanel() : new VerticalPanel(); - - m = new MenuBar(true); - m.addItem(undoItem = menuItemWithShortcut("ccw", "Undo", Locale.LS(ctrlMetaKey + "Z"), new MyCommand("edit","undo"))); - m.addItem(redoItem = menuItemWithShortcut("cw", "Redo", Locale.LS(ctrlMetaKey + "Y"), new MyCommand("edit","redo"))); - m.addSeparator(); - m.addItem(cutItem = menuItemWithShortcut("scissors", "Cut", Locale.LS(ctrlMetaKey + "X"), new MyCommand("edit","cut"))); - m.addItem(copyItem = menuItemWithShortcut("copy", "Copy", Locale.LS(ctrlMetaKey + "C"), new MyCommand("edit","copy"))); - m.addItem(pasteItem = menuItemWithShortcut("paste", "Paste", Locale.LS(ctrlMetaKey + "V"), new MyCommand("edit","paste"))); - pasteItem.setEnabled(false); - - m.addItem(menuItemWithShortcut("clone", "Duplicate", Locale.LS(ctrlMetaKey + "D"), new MyCommand("edit","duplicate"))); - - m.addSeparator(); - m.addItem(selectAllItem = menuItemWithShortcut("select-all", "Select All", Locale.LS(ctrlMetaKey + "A"), new MyCommand("edit","selectAll"))); - m.addSeparator(); - m.addItem(menuItemWithShortcut("search", "Find Component...", "/", new MyCommand("edit", "search"))); - m.addItem(iconMenuItem("target", weAreInUS(false) ? "Center Circuit" : "Centre Circuit", new MyCommand("edit", "centrecircuit"))); - m.addItem(menuItemWithShortcut("zoom-11", "Zoom 100%", "0", new MyCommand("zoom", "zoom100"))); - m.addItem(menuItemWithShortcut("zoom-in", "Zoom In", "+", new MyCommand("zoom", "zoomin"))); - m.addItem(menuItemWithShortcut("zoom-out", "Zoom Out", "-", new MyCommand("zoom", "zoomout"))); - menuBar.addItem(Locale.LS("Edit"),m); - - MenuBar drawMenuBar = new MenuBar(true); - drawMenuBar.setAutoOpen(true); - - menuBar.addItem(Locale.LS("Draw"), drawMenuBar); - - m = new MenuBar(true); - m.addItem(stackAllItem = iconMenuItem("lines", "Stack All", new MyCommand("scopes", "stackAll"))); - m.addItem(unstackAllItem = iconMenuItem("columns", "Unstack All", new MyCommand("scopes", "unstackAll"))); - m.addItem(combineAllItem = iconMenuItem("object-group", "Combine All", new MyCommand("scopes", "combineAll"))); - m.addItem(separateAllItem = iconMenuItem("object-ungroup", "Separate All", new MyCommand("scopes", "separateAll"))); - menuBar.addItem(Locale.LS("Scopes"), m); + menuBar = new MenuBar(); + menuBar.addItem(Locale.LS("File"), fileMenuBar); + verticalPanel = new VerticalPanel(); + slidersPanel = new ScrollPanel(); + verticalPanel2 = new VerticalPanel(); + + verticalPanel.getElement().addClassName("verticalPanel"); + verticalPanel.getElement().setId("painel"); + Element sidePanelCheckbox = DOM.createInputCheck(); + sidePanelCheckboxLabel = DOM.createLabel(); + sidePanelCheckboxLabel.addClassName("triggerLabel"); + sidePanelCheckbox.setId("trigger"); + sidePanelCheckboxLabel.setAttribute("for", "trigger"); + sidePanelCheckbox.addClassName("trigger"); + // addClickHandler does not work for Element but I can use onclick attribute + sidePanelCheckbox.setAttribute("onclick", "CircuitJS1.setupScopes();SetBtnsStyle();"); + Element topPanelCheckbox = DOM.createInputCheck(); + Element topPanelCheckboxLabel = DOM.createLabel(); + topPanelCheckbox.setId("toptrigger"); + topPanelCheckbox.addClassName("toptrigger"); + topPanelCheckboxLabel.addClassName("toptriggerlabel"); + topPanelCheckboxLabel.setAttribute("for", "toptrigger"); + + // make buttons side by side if there's room + buttonPanel = (VERTICALPANELWIDTH == 166) ? new HorizontalPanel() : new VerticalPanel(); + + m = new MenuBar(true); + m.addItem(undoItem = menuItemWithShortcut("ccw", "Undo", Locale.LS(ctrlMetaKey + "Z"), new MyCommand("edit", "undo"))); + m.addItem(redoItem = menuItemWithShortcut("cw", "Redo", Locale.LS(ctrlMetaKey + "Y"), new MyCommand("edit", "redo"))); + m.addSeparator(); + m.addItem(cutItem = menuItemWithShortcut("scissors", "Cut", Locale.LS(ctrlMetaKey + "X"), new MyCommand("edit", "cut"))); + m.addItem(copyItem = menuItemWithShortcut("copy", "Copy", Locale.LS(ctrlMetaKey + "C"), new MyCommand("edit", "copy"))); + m.addItem(pasteItem = menuItemWithShortcut("paste", "Paste", Locale.LS(ctrlMetaKey + "V"), new MyCommand("edit", "paste"))); + pasteItem.setEnabled(false); + + m.addItem(menuItemWithShortcut("clone", "Duplicate", Locale.LS(ctrlMetaKey + "D"), new MyCommand("edit", "duplicate"))); + + m.addSeparator(); + m.addItem(selectAllItem = menuItemWithShortcut("select-all", "Select All", Locale.LS(ctrlMetaKey + "A"), new MyCommand("edit", "selectAll"))); + m.addSeparator(); + m.addItem(menuItemWithShortcut("search", "Find Component...", "/", new MyCommand("edit", "search"))); + m.addItem(iconMenuItem("target", weAreInUS(false) ? "Center Circuit" : "Centre Circuit", new MyCommand("edit", "centrecircuit"))); + m.addItem(menuItemWithShortcut("zoom-11", "Zoom 100%", "0", new MyCommand("zoom", "zoom100"))); + m.addItem(menuItemWithShortcut("zoom-in", "Zoom In", "+", new MyCommand("zoom", "zoomin"))); + m.addItem(menuItemWithShortcut("zoom-out", "Zoom Out", "-", new MyCommand("zoom", "zoomout"))); + menuBar.addItem(Locale.LS("Edit"), m); + + MenuBar drawMenuBar = new MenuBar(true); + drawMenuBar.setAutoOpen(true); + + menuBar.addItem(Locale.LS("Draw"), drawMenuBar); + + m = new MenuBar(true); + m.addItem(stackAllItem = iconMenuItem("lines", "Stack All", new MyCommand("scopes", "stackAll"))); + m.addItem(unstackAllItem = iconMenuItem("columns", "Unstack All", new MyCommand("scopes", "unstackAll"))); + m.addItem(combineAllItem = iconMenuItem("object-group", "Combine All", new MyCommand("scopes", "combineAll"))); + m.addItem(separateAllItem = iconMenuItem("object-ungroup", "Separate All", new MyCommand("scopes", "separateAll"))); + menuBar.addItem(Locale.LS("Scopes"), m); /*m.addItem(fullscreenCheckItem = new CheckboxMenuItem(LS("Fullscreen Mode"), new Command() { public void execute(){ if (fullscreenCheckItem.getState()) { @@ -640,143 +644,152 @@ else if (usRes) */ /* } }));*/ - optionsMenuBar = m = new MenuBar(true ); - menuBar.addItem(Locale.LS("Options"), optionsMenuBar); + optionsMenuBar = m = new MenuBar(true); + menuBar.addItem(Locale.LS("Options"), optionsMenuBar); + + m.addItem(dotsCheckItem = new CheckboxMenuItem(Locale.LS("Show Current"))); + dotsCheckItem.setState(true); + m.addItem(voltsCheckItem = new CheckboxMenuItem(Locale.LS("Show Voltage"), + new Command() { + public void execute() { + if (voltsCheckItem.getState()) + powerCheckItem.setState(false); + setPowerBarEnable(); + } + })); + voltsCheckItem.setState(true); + m.addItem(powerCheckItem = new CheckboxMenuItem(Locale.LS("Show Power"), + new Command() { + public void execute() { + if (powerCheckItem.getState()) + voltsCheckItem.setState(false); + setPowerBarEnable(); + } + })); + m.addItem(showValuesCheckItem = new CheckboxMenuItem(Locale.LS("Show Values"))); + showValuesCheckItem.setState(true); + //m.add(conductanceCheckItem = getCheckItem(LS("Show Conductance"))); + m.addItem(smallGridCheckItem = new CheckboxMenuItem(Locale.LS("Small Grid"), + new Command() { + public void execute() { + setGrid(); + } + })); + m.addItem(crossHairCheckItem = new CheckboxMenuItem(Locale.LS("Show Cursor Cross Hairs"), + new Command() { + public void execute() { + setOptionInStorage("crossHair", crossHairCheckItem.getState()); + } + })); + crossHairCheckItem.setState(getOptionFromStorage("crossHair", false)); + m.addItem(euroResistorCheckItem = new CheckboxMenuItem(Locale.LS("European Resistors"), + new Command() { + public void execute() { + setOptionInStorage("euroResistors", euroResistorCheckItem.getState()); + } + })); + euroResistorCheckItem.setState(euroSetting); + m.addItem(euroGatesCheckItem = new CheckboxMenuItem(Locale.LS("IEC Gates"), + new Command() { + public void execute() { + setOptionInStorage("euroGates", euroGatesCheckItem.getState()); + int i; + for (i = 0; i != elmList.size(); i++) + getElm(i).setPoints(); + } + })); + euroGatesCheckItem.setState(euroGates); + m.addItem(printableCheckItem = new CheckboxMenuItem(Locale.LS("White Background"), + new Command() { + public void execute() { + int i; + for (i = 0; i < scopeCount; i++) + scopes[i].setRect(scopes[i].rect); + setOptionInStorage("whiteBackground", printableCheckItem.getState()); + } + })); + printableCheckItem.setState(printable); + + m.addItem(conventionCheckItem = new CheckboxMenuItem(Locale.LS("Conventional Current Motion"), + new Command() { + public void execute() { + setOptionInStorage("conventionalCurrent", conventionCheckItem.getState()); + String cc = CircuitElm.currentColor.getHexValue(); + // change the current color if it hasn't changed from the default + if (cc.equals("#ffff00") || cc.equals("#00ffff")) + CircuitElm.currentColor = conventionCheckItem.getState() ? Color.yellow : Color.cyan; + } + })); + conventionCheckItem.setState(convention); + m.addItem(noEditCheckItem = new CheckboxMenuItem(Locale.LS("Disable Editing"))); + noEditCheckItem.setState(noEditing); + + m.addItem(mouseWheelEditCheckItem = new CheckboxMenuItem(Locale.LS("Edit Values With Mouse Wheel"), + new Command() { + public void execute() { + setOptionInStorage("mouseWheelEdit", mouseWheelEditCheckItem.getState()); + } + })); + mouseWheelEditCheckItem.setState(mouseWheelEdit); + + m.addItem(new CheckboxAlignedMenuItem(Locale.LS("Shortcuts..."), new MyCommand("options", "shortcuts"))); + m.addItem(optionsItem = new CheckboxAlignedMenuItem(Locale.LS("Other Options..."), new MyCommand("options", "other"))); + if (isElectron()) + m.addItem(new CheckboxAlignedMenuItem(Locale.LS("Toggle Dev Tools"), new MyCommand("options", "devtools"))); + + mainMenuBar = new MenuBar(true); + mainMenuBar.setAutoOpen(true); + composeMainMenu(mainMenuBar, 0); + composeMainMenu(drawMenuBar, 1); + loadShortcuts(); + + DOM.appendChild(layoutPanel.getElement(), topPanelCheckbox); + DOM.appendChild(layoutPanel.getElement(), topPanelCheckboxLabel); + if (!hideMenu) + layoutPanel.addNorth(menuBar, MENUBARHEIGHT); + + if (hideSidebar) + VERTICALPANELWIDTH = 0; + else { + DOM.appendChild(layoutPanel.getElement(), sidePanelCheckbox); + DOM.appendChild(layoutPanel.getElement(), sidePanelCheckboxLabel); + layoutPanel.addEast(verticalPanel, VERTICALPANELWIDTH); + } + menuBar.getElement().insertFirst(menuBar.getElement().getChild(1)); + menuBar.getElement().getFirstChildElement().setAttribute("onclick", "document.getElementsByClassName('toptrigger')[0].checked = false"); + RootLayoutPanel.get().add(layoutPanel); - m.addItem(dotsCheckItem = new CheckboxMenuItem(Locale.LS("Show Current"))); - dotsCheckItem.setState(true); - m.addItem(voltsCheckItem = new CheckboxMenuItem(Locale.LS("Show Voltage"), - new Command() { public void execute(){ - if (voltsCheckItem.getState()) - powerCheckItem.setState(false); - setPowerBarEnable(); - } - })); - voltsCheckItem.setState(true); - m.addItem(powerCheckItem = new CheckboxMenuItem(Locale.LS("Show Power"), - new Command() { public void execute(){ - if (powerCheckItem.getState()) - voltsCheckItem.setState(false); - setPowerBarEnable(); - } - })); - m.addItem(showValuesCheckItem = new CheckboxMenuItem(Locale.LS("Show Values"))); - showValuesCheckItem.setState(true); - //m.add(conductanceCheckItem = getCheckItem(LS("Show Conductance"))); - m.addItem(smallGridCheckItem = new CheckboxMenuItem(Locale.LS("Small Grid"), - new Command() { public void execute(){ - setGrid(); - } - })); - m.addItem(crossHairCheckItem = new CheckboxMenuItem(Locale.LS("Show Cursor Cross Hairs"), - new Command() { public void execute(){ - setOptionInStorage("crossHair", crossHairCheckItem.getState()); - } - })); - crossHairCheckItem.setState(getOptionFromStorage("crossHair", false)); - m.addItem(euroResistorCheckItem = new CheckboxMenuItem(Locale.LS("European Resistors"), - new Command() { public void execute(){ - setOptionInStorage("euroResistors", euroResistorCheckItem.getState()); - } - })); - euroResistorCheckItem.setState(euroSetting); - m.addItem(euroGatesCheckItem = new CheckboxMenuItem(Locale.LS("IEC Gates"), - new Command() { public void execute(){ - setOptionInStorage("euroGates", euroGatesCheckItem.getState()); - int i; - for (i = 0; i != elmList.size(); i++) - getElm(i).setPoints(); - } - })); - euroGatesCheckItem.setState(euroGates); - m.addItem(printableCheckItem = new CheckboxMenuItem(Locale.LS("White Background"), - new Command() { public void execute(){ - int i; - for (i=0;iRUN / Stop"))); - runStopButton.addClickHandler(new ClickHandler() { - public void onClick(ClickEvent event) { - setSimRunning(!simIsRunning()); - } - }); + Window.addResizeHandler(new ResizeHandler() { + public void onResize(ResizeEvent event) { + repaint(); + setSlidersPanelHeight(); + } + }); + + cvcontext = cv.getContext2d(); + setCanvasSize(); + layoutPanel.add(cv); + verticalPanel.add(buttonPanel); + buttonPanel.addStyleName("sidePanelElm"); + buttonPanel.add(resetButton = new Button(Locale.LS("Reset"))); + resetButton.addClickHandler(new ClickHandler() { + public void onClick(ClickEvent event) { + resetAction(); + } + }); + resetButton.setStylePrimaryName("topButton"); + buttonPanel.add(runStopButton = new Button(Locale.LSHTML("RUN / Stop"))); + runStopButton.addClickHandler(new ClickHandler() { + public void onClick(ClickEvent event) { + setSimRunning(!simIsRunning()); + } + }); /* dumpMatrixButton = new Button("Dump Matrix"); @@ -785,223 +798,222 @@ public void onClick(ClickEvent event) { verticalPanel.add(dumpMatrixButton);// IES for debugging */ - if (LoadFile.isSupported()){ - verticalPanel.add(loadFileInput = new LoadFile(this)); - loadFileInput.addStyleName("sidePanelElm"); - setSlidersPanelHeight(); - } + if (LoadFile.isSupported()) { + verticalPanel.add(loadFileInput = new LoadFile(this)); + loadFileInput.addStyleName("sidePanelElm"); + setSlidersPanelHeight(); + } - Label l; - verticalPanel.add(l = new Label(Locale.LS("Simulation Speed"))); - l.addStyleName("topSpace"); - l.addStyleName("sidePanelElm"); - - // was max of 140 - verticalPanel.add( speedBar = new Scrollbar(Scrollbar.HORIZONTAL, 3, 1, 0, 260)); - speedBar.addStyleName("sidePanelElm"); - - verticalPanel.add( l = new Label(Locale.LS("Current Speed"))); - l.addStyleName("topSpace"); - l.addStyleName("sidePanelElm"); - - currentBar = new Scrollbar(Scrollbar.HORIZONTAL, 50, 1, 1, 100); - verticalPanel.add(currentBar); - currentBar.addStyleName("sidePanelElm"); - - verticalPanel.add(powerLabel = new Label (Locale.LS("Power Brightness"))); - powerLabel.addStyleName("topSpace"); - powerLabel.addStyleName("sidePanelElm"); - - verticalPanel.add(powerBar = new Scrollbar(Scrollbar.HORIZONTAL, - 50, 1, 1, 100)); - powerBar.addStyleName("sidePanelElm"); - setPowerBarEnable(); - - // verticalPanel.add(new Label("")); - // Font f = new Font("SansSerif", 0, 10); - l = new Label(Locale.LS("Current Circuit:")); - l.addStyleName("topSpace"); - l.addStyleName("sidePanelElm"); - // l.setFont(f); - titleLabel = new Label("Label"); - titleLabel.addStyleName("sidePanelElm"); - // titleLabel.setFont(f); - verticalPanel.add(l); - verticalPanel.add(titleLabel); - - Label sab; - sab = new Label(Locale.LS("Sliders and buttons")+":"); - sab.addStyleName("sabLabel"); - verticalPanel.add(sab); - - verticalPanel.add(slidersPanel); - slidersPanel.add(verticalPanel2); - verticalPanel2.addStyleName("sidePanelvp2"); - verticalPanel2.setWidth("150px"); - - //slidersPanel.setAlwaysShowScrollBars(true); - slidersPanel.getElement().getStyle().setOverflowX(Overflow.HIDDEN); - slidersPanel.getElement().getStyle().setOverflowY(Overflow.SCROLL); - - setGrid(); - elmList = new Vector(); - adjustables = new Vector(); - // setupList = new Vector(); - undoStack = new Vector(); - redoStack = new Vector(); - - - scopes = new Scope[20]; - scopeColCount = new int[20]; - scopeCount = 0; - - random = new Random(); - // cv.setBackground(Color.black); - // cv.setForeground(Color.lightGray); - - elmMenuBar = new MenuBar(true); - elmMenuBar.setAutoOpen(true); - selectScopeMenuBar = new MenuBar(true) { - @Override - - // when mousing over scope menu item, select associated scope - public void onBrowserEvent(Event event) { - int currentItem = -1; - int i; - for (i = 0; i != selectScopeMenuItems.size(); i++) { - MenuItem item = selectScopeMenuItems.get(i); - if (DOM.isOrHasChild(item.getElement(), DOM.eventGetTarget(event))) { - //MenuItem found here - currentItem = i; - } - } - switch (DOM.eventGetType(event)) { - case Event.ONMOUSEOVER: - scopeMenuSelected = currentItem; - break; - case Event.ONMOUSEOUT: - scopeMenuSelected = -1; - break; - } - super.onBrowserEvent(event); - } - }; - - elmMenuBar.addItem(elmEditMenuItem = new MenuItem(Locale.LS("Edit..."),new MyCommand("elm","edit"))); - elmMenuBar.addItem(elmScopeMenuItem = new MenuItem(Locale.LS("View in New Scope"), new MyCommand("elm","viewInScope"))); - elmMenuBar.addItem(elmFloatScopeMenuItem = new MenuItem(Locale.LS("View in New Undocked Scope"), new MyCommand("elm","viewInFloatScope"))); - elmMenuBar.addItem(elmAddScopeMenuItem = new MenuItem(Locale.LS("Add to Existing Scope"), new MyCommand("elm", "addToScope0"))); - elmMenuBar.addItem(elmCutMenuItem = new MenuItem(Locale.LS("Cut"),new MyCommand("elm","cut"))); - elmMenuBar.addItem(elmCopyMenuItem = new MenuItem(Locale.LS("Copy"),new MyCommand("elm","copy"))); - elmMenuBar.addItem(elmDeleteMenuItem = new MenuItem(Locale.LS("Delete"),new MyCommand("elm","delete"))); - elmMenuBar.addItem( new MenuItem(Locale.LS("Duplicate"),new MyCommand("elm","duplicate"))); - elmMenuBar.addItem(elmFlipMenuItem = new MenuItem(Locale.LS("Swap Terminals"),new MyCommand("elm","flip"))); - elmMenuBar.addItem(elmSplitMenuItem = menuItemWithShortcut("", "Split Wire", Locale.LS(ctrlMetaKey + "click"), new MyCommand("elm","split"))); - elmMenuBar.addItem(elmSliderMenuItem = new MenuItem(Locale.LS("Sliders..."),new MyCommand("elm","sliders"))); - - scopePopupMenu = new ScopePopupMenu(); - - setColors(positiveColor, negativeColor, neutralColor, selectColor, currentColor); - - if (startCircuitText != null) { - getSetupList(false); - readCircuit(startCircuitText); - unsavedChanges = false; - } else { - if (stopMessage == null && startCircuitLink!=null) { - readCircuit(""); - getSetupList(false); - //ImportFromDropboxDialog.setSim(this); - //ImportFromDropboxDialog.doImportDropboxLink(startCircuitLink, false); - } else { - readCircuit(""); - if (stopMessage == null && startCircuit != null) { - getSetupList(false); - readSetupFile(startCircuit, startLabel); - } - else - getSetupList(true); - } - } + Label l; + verticalPanel.add(l = new Label(Locale.LS("Simulation Speed"))); + l.addStyleName("topSpace"); + l.addStyleName("sidePanelElm"); + + // was max of 140 + verticalPanel.add(speedBar = new Scrollbar(Scrollbar.HORIZONTAL, 3, 1, 0, 260)); + speedBar.addStyleName("sidePanelElm"); + + verticalPanel.add(l = new Label(Locale.LS("Current Speed"))); + l.addStyleName("topSpace"); + l.addStyleName("sidePanelElm"); + + currentBar = new Scrollbar(Scrollbar.HORIZONTAL, 50, 1, 1, 100); + verticalPanel.add(currentBar); + currentBar.addStyleName("sidePanelElm"); + + verticalPanel.add(powerLabel = new Label(Locale.LS("Power Brightness"))); + powerLabel.addStyleName("topSpace"); + powerLabel.addStyleName("sidePanelElm"); + + verticalPanel.add(powerBar = new Scrollbar(Scrollbar.HORIZONTAL, + 50, 1, 1, 100)); + powerBar.addStyleName("sidePanelElm"); + setPowerBarEnable(); + + // verticalPanel.add(new Label("")); + // Font f = new Font("SansSerif", 0, 10); + l = new Label(Locale.LS("Current Circuit:")); + l.addStyleName("topSpace"); + l.addStyleName("sidePanelElm"); + // l.setFont(f); + titleLabel = new Label("Label"); + titleLabel.addStyleName("sidePanelElm"); + // titleLabel.setFont(f); + verticalPanel.add(l); + verticalPanel.add(titleLabel); + + Label sab; + sab = new Label(Locale.LS("Sliders and buttons") + ":"); + sab.addStyleName("sabLabel"); + verticalPanel.add(sab); + + verticalPanel.add(slidersPanel); + slidersPanel.add(verticalPanel2); + verticalPanel2.addStyleName("sidePanelvp2"); + verticalPanel2.setWidth("150px"); + + //slidersPanel.setAlwaysShowScrollBars(true); + slidersPanel.getElement().getStyle().setOverflowX(Overflow.HIDDEN); + slidersPanel.getElement().getStyle().setOverflowY(Overflow.SCROLL); + + setGrid(); + elmList = new Vector(); + adjustables = new Vector(); + // setupList = new Vector(); + undoStack = new Vector(); + redoStack = new Vector(); + + + scopes = new Scope[20]; + scopeColCount = new int[20]; + scopeCount = 0; + + random = new Random(); + // cv.setBackground(Color.black); + // cv.setForeground(Color.lightGray); + + elmMenuBar = new MenuBar(true); + elmMenuBar.setAutoOpen(true); + selectScopeMenuBar = new MenuBar(true) { + @Override + + // when mousing over scope menu item, select associated scope + public void onBrowserEvent(Event event) { + int currentItem = -1; + int i; + for (i = 0; i != selectScopeMenuItems.size(); i++) { + MenuItem item = selectScopeMenuItems.get(i); + if (DOM.isOrHasChild(item.getElement(), DOM.eventGetTarget(event))) { + //MenuItem found here + currentItem = i; + } + } + switch (DOM.eventGetType(event)) { + case Event.ONMOUSEOVER: + scopeMenuSelected = currentItem; + break; + case Event.ONMOUSEOUT: + scopeMenuSelected = -1; + break; + } + super.onBrowserEvent(event); + } + }; + + elmMenuBar.addItem(elmEditMenuItem = new MenuItem(Locale.LS("Edit..."), new MyCommand("elm", "edit"))); + elmMenuBar.addItem(elmScopeMenuItem = new MenuItem(Locale.LS("View in New Scope"), new MyCommand("elm", "viewInScope"))); + elmMenuBar.addItem(elmFloatScopeMenuItem = new MenuItem(Locale.LS("View in New Undocked Scope"), new MyCommand("elm", "viewInFloatScope"))); + elmMenuBar.addItem(elmAddScopeMenuItem = new MenuItem(Locale.LS("Add to Existing Scope"), new MyCommand("elm", "addToScope0"))); + elmMenuBar.addItem(elmCutMenuItem = new MenuItem(Locale.LS("Cut"), new MyCommand("elm", "cut"))); + elmMenuBar.addItem(elmCopyMenuItem = new MenuItem(Locale.LS("Copy"), new MyCommand("elm", "copy"))); + elmMenuBar.addItem(elmDeleteMenuItem = new MenuItem(Locale.LS("Delete"), new MyCommand("elm", "delete"))); + elmMenuBar.addItem(new MenuItem(Locale.LS("Duplicate"), new MyCommand("elm", "duplicate"))); + elmMenuBar.addItem(elmFlipMenuItem = new MenuItem(Locale.LS("Swap Terminals"), new MyCommand("elm", "flip"))); + elmMenuBar.addItem(elmSplitMenuItem = menuItemWithShortcut("", "Split Wire", Locale.LS(ctrlMetaKey + "click"), new MyCommand("elm", "split"))); + elmMenuBar.addItem(elmSliderMenuItem = new MenuItem(Locale.LS("Sliders..."), new MyCommand("elm", "sliders"))); + + scopePopupMenu = new ScopePopupMenu(); + + setColors(positiveColor, negativeColor, neutralColor, selectColor, currentColor); + + if (startCircuitText != null) { + getSetupList(false); + readCircuit(startCircuitText); + unsavedChanges = false; + } else { + if (stopMessage == null && startCircuitLink != null) { + readCircuit(""); + getSetupList(false); + //ImportFromDropboxDialog.setSim(this); + //ImportFromDropboxDialog.doImportDropboxLink(startCircuitLink, false); + } else { + readCircuit(""); + if (stopMessage == null && startCircuit != null) { + getSetupList(false); + readSetupFile(startCircuit, startLabel); + } else + getSetupList(true); + } + } + + if (mouseModeReq != null) + menuPerformed("main", mouseModeReq); + + enableUndoRedo(); + enablePaste(); + setSlidersPanelHeight(); + cv.addMouseDownHandler(this); + cv.addMouseMoveHandler(this); + cv.addMouseOutHandler(this); + cv.addMouseUpHandler(this); + cv.addClickHandler(this); + cv.addDoubleClickHandler(this); + doTouchHandlers(this, cv.getCanvasElement()); + cv.addDomHandler(this, ContextMenuEvent.getType()); + menuBar.addDomHandler(new ClickHandler() { + public void onClick(ClickEvent event) { + doMainMenuChecks(); + } + }, ClickEvent.getType()); + Event.addNativePreviewHandler(this); + cv.addMouseWheelHandler(this); + + Window.addWindowClosingHandler(new Window.ClosingHandler() { + public void onWindowClosing(ClosingEvent event) { + // there is a bug in electron that makes it impossible to close the app if this warning is given + if (unsavedChanges && !isElectron()) + event.setMessage(Locale.LS("Are you sure? There are unsaved changes.")); + } + }); + setupJSInterface(); - if (mouseModeReq != null) - menuPerformed("main", mouseModeReq); - - enableUndoRedo(); - enablePaste(); - setSlidersPanelHeight(); - cv.addMouseDownHandler(this); - cv.addMouseMoveHandler(this); - cv.addMouseOutHandler(this); - cv.addMouseUpHandler(this); - cv.addClickHandler(this); - cv.addDoubleClickHandler(this); - doTouchHandlers(this, cv.getCanvasElement()); - cv.addDomHandler(this, ContextMenuEvent.getType()); - menuBar.addDomHandler(new ClickHandler() { - public void onClick(ClickEvent event) { - doMainMenuChecks(); - } - }, ClickEvent.getType()); - Event.addNativePreviewHandler(this); - cv.addMouseWheelHandler(this); - - Window.addWindowClosingHandler(new Window.ClosingHandler() { - public void onWindowClosing(ClosingEvent event) { - // there is a bug in electron that makes it impossible to close the app if this warning is given - if (unsavedChanges && !isElectron()) - event.setMessage(Locale.LS("Are you sure? There are unsaved changes.")); - } - }); - setupJSInterface(); - - setSimRunning(running); + setSimRunning(running); } void setColors(String positiveColor, String negativeColor, String neutralColor, String selectColor, String currentColor) { Storage stor = Storage.getLocalStorageIfSupported(); if (stor != null) { if (positiveColor == null) - positiveColor = stor.getItem("positiveColor"); + positiveColor = stor.getItem("positiveColor"); if (negativeColor == null) - negativeColor = stor.getItem("negativeColor"); + negativeColor = stor.getItem("negativeColor"); if (neutralColor == null) - neutralColor = stor.getItem("neutralColor"); + neutralColor = stor.getItem("neutralColor"); if (selectColor == null) - selectColor = stor.getItem("selectColor"); + selectColor = stor.getItem("selectColor"); if (currentColor == null) - currentColor = stor.getItem("currentColor"); + currentColor = stor.getItem("currentColor"); } - if (positiveColor != null) - CircuitElm.positiveColor = new Color(URL.decodeQueryString(positiveColor)); - else if (getOptionFromStorage("alternativeColor", false)) - CircuitElm.positiveColor = Color.blue; + if (positiveColor != null) + CircuitElm.positiveColor = new Color(URL.decodeQueryString(positiveColor)); + else if (getOptionFromStorage("alternativeColor", false)) + CircuitElm.positiveColor = Color.blue; - if (negativeColor != null) - CircuitElm.negativeColor = new Color(URL.decodeQueryString(negativeColor)); - if (neutralColor != null) - CircuitElm.neutralColor = new Color(URL.decodeQueryString(neutralColor)); + if (negativeColor != null) + CircuitElm.negativeColor = new Color(URL.decodeQueryString(negativeColor)); + if (neutralColor != null) + CircuitElm.neutralColor = new Color(URL.decodeQueryString(neutralColor)); - if (selectColor != null) - CircuitElm.selectColor = new Color(URL.decodeQueryString(selectColor)); - else - CircuitElm.selectColor = Color.cyan; + if (selectColor != null) + CircuitElm.selectColor = new Color(URL.decodeQueryString(selectColor)); + else + CircuitElm.selectColor = Color.cyan; - if (currentColor != null) - CircuitElm.currentColor = new Color(URL.decodeQueryString(currentColor)); - else - CircuitElm.currentColor = conventionCheckItem.getState() ? Color.yellow : Color.cyan; + if (currentColor != null) + CircuitElm.currentColor = new Color(URL.decodeQueryString(currentColor)); + else + CircuitElm.currentColor = conventionCheckItem.getState() ? Color.yellow : Color.cyan; - CircuitElm.setColorScale(); + CircuitElm.setColorScale(); } MenuItem menuItemWithShortcut(String icon, String text, String shortcut, MyCommand cmd) { - final String edithtml="
" + nbsp + Locale.LS(text) + "
" + shortcut + "
"; - return new MenuItem(SafeHtmlUtils.fromTrustedString(sn), cmd); + final String edithtml = "
" + nbsp + Locale.LS(text) + "
" + shortcut + "
"; + return new MenuItem(SafeHtmlUtils.fromTrustedString(sn), cmd); } MenuItem iconMenuItem(String icon, String text, Command cmd) { @@ -1023,7 +1035,7 @@ void setOptionInStorage(String key, boolean val) { Storage stor = Storage.getLocalStorageIfSupported(); if (stor == null) return; - stor.setItem(key, val ? "true" : "false"); + stor.setItem(key, val ? "true" : "false"); } // save shortcuts to local storage @@ -1037,7 +1049,7 @@ void saveShortcuts() { for (i = 0; i != shortcuts.length; i++) { String sh = shortcuts[i]; if (sh == null) - continue; + continue; str += ";" + i + "=" + sh; } stor.setItem("shortcuts", str); @@ -1063,7 +1075,7 @@ void loadShortcuts() { CheckboxMenuItem item = mainMenuItems.get(i); // stop when we get to drag menu items if (item.getShortcut().length() > 1) - break; + break; item.setShortcut(""); } @@ -1071,7 +1083,7 @@ void loadShortcuts() { for (i = 1; i < keys.length; i++) { String arr[] = keys[i].split("="); if (arr.length != 2) - continue; + continue; int c = Integer.parseInt(arr[0]); String className = arr[1]; shortcuts[c] = className; @@ -1079,11 +1091,11 @@ void loadShortcuts() { // find menu item and fix it int j; for (j = 0; j != mainMenuItems.size(); j++) { - if (mainMenuItemNames.get(j) == className) { - CheckboxMenuItem item = mainMenuItems.get(j); - item.setShortcut(Character.toString((char)c)); - break; - } + if (mainMenuItemNames.get(j) == className) { + CheckboxMenuItem item = mainMenuItems.get(j); + item.setShortcut(Character.toString((char) c)); + break; + } } } } @@ -1164,332 +1176,331 @@ function getTouchPos(canvasDom, touchEvent) { // this is called twice, once for the Draw menu, once for the right mouse popup menu public void composeMainMenu(MenuBar mainMenuBar, int num) { - mainMenuBar.addItem(getClassCheckItem(Locale.LS("Add Wire"), "WireElm")); - mainMenuBar.addItem(getClassCheckItem(Locale.LS("Add Resistor"), "ResistorElm")); - - MenuBar passMenuBar = new MenuBar(true); - passMenuBar.addItem(getClassCheckItem(Locale.LS("Add Capacitor"), "CapacitorElm")); - passMenuBar.addItem(getClassCheckItem(Locale.LS("Add Capacitor (polarized)"), "PolarCapacitorElm")); - passMenuBar.addItem(getClassCheckItem(Locale.LS("Add Inductor"), "InductorElm")); - passMenuBar.addItem(getClassCheckItem(Locale.LS("Add Switch"), "SwitchElm")); - passMenuBar.addItem(getClassCheckItem(Locale.LS("Add Push Switch"), "PushSwitchElm")); - passMenuBar.addItem(getClassCheckItem(Locale.LS("Add SPDT Switch"), "Switch2Elm")); - passMenuBar.addItem(getClassCheckItem(Locale.LS("Add Make-Before-Break Switch"), "MBBSwitchElm")); - passMenuBar.addItem(getClassCheckItem(Locale.LS("Add Potentiometer"), "PotElm")); - passMenuBar.addItem(getClassCheckItem(Locale.LS("Add Transformer"), "TransformerElm")); - passMenuBar.addItem(getClassCheckItem(Locale.LS("Add Tapped Transformer"), "TappedTransformerElm")); - passMenuBar.addItem(getClassCheckItem(Locale.LS("Add Transmission Line"), "TransLineElm")); - passMenuBar.addItem(getClassCheckItem(Locale.LS("Add Relay"), "RelayElm")); - passMenuBar.addItem(getClassCheckItem(Locale.LS("Add Double Coil Relay"), "Relay2Elm")); - passMenuBar.addItem(getClassCheckItem(Locale.LS("Add Memristor"), "MemristorElm")); - passMenuBar.addItem(getClassCheckItem(Locale.LS("Add Spark Gap"), "SparkGapElm")); - passMenuBar.addItem(getClassCheckItem(Locale.LS("Add Fuse"), "FuseElm")); - passMenuBar.addItem(getClassCheckItem(Locale.LS("Add Custom Transformer"), "CustomTransformerElm")); - passMenuBar.addItem(getClassCheckItem(Locale.LS("Add Crystal"), "CrystalElm")); - passMenuBar.addItem(getClassCheckItem(Locale.LS("Add Photoresistor"), "LDRElm")); - passMenuBar.addItem(getClassCheckItem(Locale.LS("Add Thermistor"), "ThermistorNTCElm")); - mainMenuBar.addItem(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml+Locale.LS(" Passive Components")), passMenuBar); - - MenuBar inputMenuBar = new MenuBar(true); - inputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Ground"), "GroundElm")); - inputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Voltage Source (2-terminal)"), "DCVoltageElm")); - inputMenuBar.addItem(getClassCheckItem(Locale.LS("Add A/C Voltage Source (2-terminal)"), "ACVoltageElm")); - inputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Voltage Source (1-terminal)"), "RailElm")); - inputMenuBar.addItem(getClassCheckItem(Locale.LS("Add A/C Voltage Source (1-terminal)"), "ACRailElm")); - inputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Square Wave Source (1-terminal)"), "SquareRailElm")); - inputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Clock"), "ClockElm")); - inputMenuBar.addItem(getClassCheckItem(Locale.LS("Add A/C Sweep"), "SweepElm")); - inputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Variable Voltage"), "VarRailElm")); - inputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Antenna"), "AntennaElm")); - inputMenuBar.addItem(getClassCheckItem(Locale.LS("Add AM Source"), "AMElm")); - inputMenuBar.addItem(getClassCheckItem(Locale.LS("Add FM Source"), "FMElm")); - inputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Current Source"), "CurrentElm")); - inputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Noise Generator"), "NoiseElm")); - inputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Audio Input"), "AudioInputElm")); - inputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Data Input"), "DataInputElm")); - inputMenuBar.addItem(getClassCheckItem(Locale.LS("Add External Voltage (JavaScript)"), "ExtVoltageElm")); - - mainMenuBar.addItem(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml+Locale.LS(" Inputs and Sources")), inputMenuBar); - - MenuBar outputMenuBar = new MenuBar(true); - outputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Analog Output"), "OutputElm")); - outputMenuBar.addItem(getClassCheckItem(Locale.LS("Add LED"), "LEDElm")); - outputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Lamp"), "LampElm")); - outputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Text"), "TextElm")); - outputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Box"), "BoxElm")); - outputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Line"), "LineElm")); - outputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Voltmeter/Scope Probe"), "ProbeElm")); - outputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Ohmmeter"), "OhmMeterElm")); - outputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Labeled Node"), "LabeledNodeElm")); - outputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Test Point"), "TestPointElm")); - outputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Ammeter"), "AmmeterElm")); - outputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Decimal Display"), "DecimalDisplayElm")); - outputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Data Export"), "DataRecorderElm")); - outputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Audio Output"), "AudioOutputElm")); - outputMenuBar.addItem(getClassCheckItem(Locale.LS("Add LED Array"), "LEDArrayElm")); - outputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Stop Trigger"), "StopTriggerElm")); - outputMenuBar.addItem(getClassCheckItem(Locale.LS("Add DC Motor"), "DCMotorElm")); - outputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Wattmeter"), "WattmeterElm")); - mainMenuBar.addItem(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml+Locale.LS(" Outputs and Labels")), outputMenuBar); - - MenuBar activeMenuBar = new MenuBar(true); - activeMenuBar.addItem(getClassCheckItem(Locale.LS("Add Diode"), "DiodeElm")); - activeMenuBar.addItem(getClassCheckItem(Locale.LS("Add Zener Diode"), "ZenerElm")); - activeMenuBar.addItem(getClassCheckItem(Locale.LS("Add Transistor (bipolar, NPN)"), "NTransistorElm")); - activeMenuBar.addItem(getClassCheckItem(Locale.LS("Add Transistor (bipolar, PNP)"), "PTransistorElm")); - activeMenuBar.addItem(getClassCheckItem(Locale.LS("Add MOSFET (N-Channel)"), "NMosfetElm")); - activeMenuBar.addItem(getClassCheckItem(Locale.LS("Add MOSFET (P-Channel)"), "PMosfetElm")); - activeMenuBar.addItem(getClassCheckItem(Locale.LS("Add JFET (N-Channel)"), "NJfetElm")); - activeMenuBar.addItem(getClassCheckItem(Locale.LS("Add JFET (P-Channel)"), "PJfetElm")); - activeMenuBar.addItem(getClassCheckItem(Locale.LS("Add SCR"), "SCRElm")); - activeMenuBar.addItem(getClassCheckItem(Locale.LS("Add DIAC"), "DiacElm")); - activeMenuBar.addItem(getClassCheckItem(Locale.LS("Add TRIAC"), "TriacElm")); - activeMenuBar.addItem(getClassCheckItem(Locale.LS("Add Darlington Pair (NPN)"), "NDarlingtonElm")); - activeMenuBar.addItem(getClassCheckItem(Locale.LS("Add Darlington Pair (PNP)"), "PDarlingtonElm")); - activeMenuBar.addItem(getClassCheckItem(Locale.LS("Add Varactor/Varicap"), "VaractorElm")); - activeMenuBar.addItem(getClassCheckItem(Locale.LS("Add Tunnel Diode"), "TunnelDiodeElm")); - activeMenuBar.addItem(getClassCheckItem(Locale.LS("Add Triode"), "TriodeElm")); - activeMenuBar.addItem(getClassCheckItem(Locale.LS("Add Unijunction Transistor"), "UnijunctionElm")); - mainMenuBar.addItem(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml+Locale.LS(" Active Components")), activeMenuBar); - - MenuBar activeBlocMenuBar = new MenuBar(true); - activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add Op Amp (ideal, - on top)"), "OpAmpElm")); - activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add Op Amp (ideal, + on top)"), "OpAmpSwapElm")); - activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add Op Amp (real)"), "OpAmpRealElm")); - activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add Analog Switch (SPST)"), "AnalogSwitchElm")); - activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add Analog Switch (SPDT)"), "AnalogSwitch2Elm")); - activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add Tristate Buffer"), "TriStateElm")); - activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add Schmitt Trigger"), "SchmittElm")); - activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add Schmitt Trigger (Inverting)"), "InvertingSchmittElm")); - activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add Delay Buffer"), "DelayBufferElm")); - activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add CCII+"), "CC2Elm")); - activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add CCII-"), "CC2NegElm")); - activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add Comparator (Hi-Z/GND output)"), "ComparatorElm")); - activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add OTA (LM13700 style)"), "OTAElm")); - activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add Voltage-Controlled Voltage Source (VCVS)"), "VCVSElm")); - activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add Voltage-Controlled Current Source (VCCS)"), "VCCSElm")); - activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add Current-Controlled Voltage Source (CCVS)"), "CCVSElm")); - activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add Current-Controlled Current Source (CCCS)"), "CCCSElm")); - activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add Optocoupler"), "OptocouplerElm")); - activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add Time Delay Relay"), "TimeDelayRelayElm")); - activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add LM317"), "CustomCompositeElm:~LM317-v2")); - activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add TL431"), "CustomCompositeElm:~TL431")); - activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add Subcircuit Instance"), "CustomCompositeElm")); - mainMenuBar.addItem(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml+Locale.LS(" Active Building Blocks")), activeBlocMenuBar); - - MenuBar gateMenuBar = new MenuBar(true); - gateMenuBar.addItem(getClassCheckItem(Locale.LS("Add Logic Input"), "LogicInputElm")); - gateMenuBar.addItem(getClassCheckItem(Locale.LS("Add Logic Output"), "LogicOutputElm")); - gateMenuBar.addItem(getClassCheckItem(Locale.LS("Add Inverter"), "InverterElm")); - gateMenuBar.addItem(getClassCheckItem(Locale.LS("Add NAND Gate"), "NandGateElm")); - gateMenuBar.addItem(getClassCheckItem(Locale.LS("Add NOR Gate"), "NorGateElm")); - gateMenuBar.addItem(getClassCheckItem(Locale.LS("Add AND Gate"), "AndGateElm")); - gateMenuBar.addItem(getClassCheckItem(Locale.LS("Add OR Gate"), "OrGateElm")); - gateMenuBar.addItem(getClassCheckItem(Locale.LS("Add XOR Gate"), "XorGateElm")); - mainMenuBar.addItem(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml+Locale.LS(" Logic Gates, Input and Output")), gateMenuBar); - - MenuBar chipMenuBar = new MenuBar(true); - chipMenuBar.addItem(getClassCheckItem(Locale.LS("Add D Flip-Flop"), "DFlipFlopElm")); - chipMenuBar.addItem(getClassCheckItem(Locale.LS("Add JK Flip-Flop"), "JKFlipFlopElm")); - chipMenuBar.addItem(getClassCheckItem(Locale.LS("Add T Flip-Flop"), "TFlipFlopElm")); - chipMenuBar.addItem(getClassCheckItem(Locale.LS("Add 7 Segment LED"), "SevenSegElm")); - chipMenuBar.addItem(getClassCheckItem(Locale.LS("Add 7 Segment Decoder"), "SevenSegDecoderElm")); - chipMenuBar.addItem(getClassCheckItem(Locale.LS("Add Multiplexer"), "MultiplexerElm")); - chipMenuBar.addItem(getClassCheckItem(Locale.LS("Add Demultiplexer"), "DeMultiplexerElm")); - chipMenuBar.addItem(getClassCheckItem(Locale.LS("Add SIPO shift register"), "SipoShiftElm")); - chipMenuBar.addItem(getClassCheckItem(Locale.LS("Add PISO shift register"), "PisoShiftElm")); - chipMenuBar.addItem(getClassCheckItem(Locale.LS("Add Counter"), "CounterElm")); - chipMenuBar.addItem(getClassCheckItem(Locale.LS("Add Counter w/ Load"), "Counter2Elm")); - chipMenuBar.addItem(getClassCheckItem(Locale.LS("Add Ring Counter"), "DecadeElm")); - chipMenuBar.addItem(getClassCheckItem(Locale.LS("Add Latch"), "LatchElm")); - chipMenuBar.addItem(getClassCheckItem(Locale.LS("Add Sequence generator"), "SeqGenElm")); - chipMenuBar.addItem(getClassCheckItem(Locale.LS("Add Adder"), "FullAdderElm")); - chipMenuBar.addItem(getClassCheckItem(Locale.LS("Add Half Adder"), "HalfAdderElm")); - chipMenuBar.addItem(getClassCheckItem(Locale.LS("Add Custom Logic"), "UserDefinedLogicElm")); // don't change this, it will break people's saved shortcuts - chipMenuBar.addItem(getClassCheckItem(Locale.LS("Add Static RAM"), "SRAMElm")); - mainMenuBar.addItem(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml+Locale.LS(" Digital Chips")), chipMenuBar); - - MenuBar achipMenuBar = new MenuBar(true); - achipMenuBar.addItem(getClassCheckItem(Locale.LS("Add 555 Timer"), "TimerElm")); - achipMenuBar.addItem(getClassCheckItem(Locale.LS("Add Phase Comparator"), "PhaseCompElm")); - achipMenuBar.addItem(getClassCheckItem(Locale.LS("Add DAC"), "DACElm")); - achipMenuBar.addItem(getClassCheckItem(Locale.LS("Add ADC"), "ADCElm")); - achipMenuBar.addItem(getClassCheckItem(Locale.LS("Add VCO"), "VCOElm")); - achipMenuBar.addItem(getClassCheckItem(Locale.LS("Add Monostable"), "MonostableElm")); - mainMenuBar.addItem(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml+Locale.LS(" Analog and Hybrid Chips")), achipMenuBar); - - if (subcircuitMenuBar == null) - subcircuitMenuBar = new MenuBar[2]; - subcircuitMenuBar[num] = new MenuBar(true); - mainMenuBar.addItem(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml+Locale.LS(" Subcircuits")), subcircuitMenuBar[num]); - - MenuBar otherMenuBar = new MenuBar(true); - CheckboxMenuItem mi; - otherMenuBar.addItem(mi=getClassCheckItem(Locale.LS("Drag All"), "DragAll")); - mi.setShortcut(Locale.LS("(Alt-drag)")); - otherMenuBar.addItem(mi=getClassCheckItem(Locale.LS("Drag Row"), "DragRow")); - mi.setShortcut(Locale.LS("(A-S-drag)")); - otherMenuBar.addItem(mi=getClassCheckItem(Locale.LS("Drag Column"), "DragColumn")); - mi.setShortcut(isMac ? Locale.LS("(A-Cmd-drag)") : Locale.LS("(A-M-drag)")); - otherMenuBar.addItem(getClassCheckItem(Locale.LS("Drag Selected"), "DragSelected")); - otherMenuBar.addItem(mi=getClassCheckItem(Locale.LS("Drag Post"), "DragPost")); - mi.setShortcut("(" + ctrlMetaKey + "drag)"); - - mainMenuBar.addItem(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml+Locale.LS(" Drag")), otherMenuBar); - - mainMenuBar.addItem(mi=getClassCheckItem(Locale.LS("Select/Drag Sel"), "Select")); - mi.setShortcut(Locale.LS("(space or Shift-drag)")); + mainMenuBar.addItem(getClassCheckItem(Locale.LS("Add Wire"), "WireElm")); + mainMenuBar.addItem(getClassCheckItem(Locale.LS("Add Resistor"), "ResistorElm")); + + MenuBar passMenuBar = new MenuBar(true); + passMenuBar.addItem(getClassCheckItem(Locale.LS("Add Capacitor"), "CapacitorElm")); + passMenuBar.addItem(getClassCheckItem(Locale.LS("Add Capacitor (polarized)"), "PolarCapacitorElm")); + passMenuBar.addItem(getClassCheckItem(Locale.LS("Add Inductor"), "InductorElm")); + passMenuBar.addItem(getClassCheckItem(Locale.LS("Add Switch"), "SwitchElm")); + passMenuBar.addItem(getClassCheckItem(Locale.LS("Add Push Switch"), "PushSwitchElm")); + passMenuBar.addItem(getClassCheckItem(Locale.LS("Add SPDT Switch"), "Switch2Elm")); + passMenuBar.addItem(getClassCheckItem(Locale.LS("Add Make-Before-Break Switch"), "MBBSwitchElm")); + passMenuBar.addItem(getClassCheckItem(Locale.LS("Add Potentiometer"), "PotElm")); + passMenuBar.addItem(getClassCheckItem(Locale.LS("Add Transformer"), "TransformerElm")); + passMenuBar.addItem(getClassCheckItem(Locale.LS("Add Tapped Transformer"), "TappedTransformerElm")); + passMenuBar.addItem(getClassCheckItem(Locale.LS("Add Transmission Line"), "TransLineElm")); + passMenuBar.addItem(getClassCheckItem(Locale.LS("Add Relay"), "RelayElm")); + passMenuBar.addItem(getClassCheckItem(Locale.LS("Add Double Coil Relay"), "Relay2Elm")); + passMenuBar.addItem(getClassCheckItem(Locale.LS("Add Memristor"), "MemristorElm")); + passMenuBar.addItem(getClassCheckItem(Locale.LS("Add Spark Gap"), "SparkGapElm")); + passMenuBar.addItem(getClassCheckItem(Locale.LS("Add Fuse"), "FuseElm")); + passMenuBar.addItem(getClassCheckItem(Locale.LS("Add Custom Transformer"), "CustomTransformerElm")); + passMenuBar.addItem(getClassCheckItem(Locale.LS("Add Crystal"), "CrystalElm")); + passMenuBar.addItem(getClassCheckItem(Locale.LS("Add Photoresistor"), "LDRElm")); + passMenuBar.addItem(getClassCheckItem(Locale.LS("Add Thermistor"), "ThermistorNTCElm")); + mainMenuBar.addItem(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml + Locale.LS(" Passive Components")), passMenuBar); + + MenuBar inputMenuBar = new MenuBar(true); + inputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Ground"), "GroundElm")); + inputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Voltage Source (2-terminal)"), "DCVoltageElm")); + inputMenuBar.addItem(getClassCheckItem(Locale.LS("Add A/C Voltage Source (2-terminal)"), "ACVoltageElm")); + inputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Voltage Source (1-terminal)"), "RailElm")); + inputMenuBar.addItem(getClassCheckItem(Locale.LS("Add A/C Voltage Source (1-terminal)"), "ACRailElm")); + inputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Square Wave Source (1-terminal)"), "SquareRailElm")); + inputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Clock"), "ClockElm")); + inputMenuBar.addItem(getClassCheckItem(Locale.LS("Add A/C Sweep"), "SweepElm")); + inputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Variable Voltage"), "VarRailElm")); + inputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Antenna"), "AntennaElm")); + inputMenuBar.addItem(getClassCheckItem(Locale.LS("Add AM Source"), "AMElm")); + inputMenuBar.addItem(getClassCheckItem(Locale.LS("Add FM Source"), "FMElm")); + inputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Current Source"), "CurrentElm")); + inputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Noise Generator"), "NoiseElm")); + inputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Audio Input"), "AudioInputElm")); + inputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Data Input"), "DataInputElm")); + inputMenuBar.addItem(getClassCheckItem(Locale.LS("Add External Voltage (JavaScript)"), "ExtVoltageElm")); + + mainMenuBar.addItem(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml + Locale.LS(" Inputs and Sources")), inputMenuBar); + + MenuBar outputMenuBar = new MenuBar(true); + outputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Analog Output"), "OutputElm")); + outputMenuBar.addItem(getClassCheckItem(Locale.LS("Add LED"), "LEDElm")); + outputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Lamp"), "LampElm")); + outputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Text"), "TextElm")); + outputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Box"), "BoxElm")); + outputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Line"), "LineElm")); + outputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Voltmeter/Scope Probe"), "ProbeElm")); + outputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Ohmmeter"), "OhmMeterElm")); + outputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Labeled Node"), "LabeledNodeElm")); + outputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Test Point"), "TestPointElm")); + outputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Ammeter"), "AmmeterElm")); + outputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Decimal Display"), "DecimalDisplayElm")); + outputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Data Export"), "DataRecorderElm")); + outputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Audio Output"), "AudioOutputElm")); + outputMenuBar.addItem(getClassCheckItem(Locale.LS("Add LED Array"), "LEDArrayElm")); + outputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Stop Trigger"), "StopTriggerElm")); + outputMenuBar.addItem(getClassCheckItem(Locale.LS("Add DC Motor"), "DCMotorElm")); + outputMenuBar.addItem(getClassCheckItem(Locale.LS("Add Wattmeter"), "WattmeterElm")); + mainMenuBar.addItem(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml + Locale.LS(" Outputs and Labels")), outputMenuBar); + + MenuBar activeMenuBar = new MenuBar(true); + activeMenuBar.addItem(getClassCheckItem(Locale.LS("Add Diode"), "DiodeElm")); + activeMenuBar.addItem(getClassCheckItem(Locale.LS("Add Zener Diode"), "ZenerElm")); + activeMenuBar.addItem(getClassCheckItem(Locale.LS("Add Transistor (bipolar, NPN)"), "NTransistorElm")); + activeMenuBar.addItem(getClassCheckItem(Locale.LS("Add Transistor (bipolar, PNP)"), "PTransistorElm")); + activeMenuBar.addItem(getClassCheckItem(Locale.LS("Add MOSFET (N-Channel)"), "NMosfetElm")); + activeMenuBar.addItem(getClassCheckItem(Locale.LS("Add MOSFET (P-Channel)"), "PMosfetElm")); + activeMenuBar.addItem(getClassCheckItem(Locale.LS("Add JFET (N-Channel)"), "NJfetElm")); + activeMenuBar.addItem(getClassCheckItem(Locale.LS("Add JFET (P-Channel)"), "PJfetElm")); + activeMenuBar.addItem(getClassCheckItem(Locale.LS("Add SCR"), "SCRElm")); + activeMenuBar.addItem(getClassCheckItem(Locale.LS("Add DIAC"), "DiacElm")); + activeMenuBar.addItem(getClassCheckItem(Locale.LS("Add TRIAC"), "TriacElm")); + activeMenuBar.addItem(getClassCheckItem(Locale.LS("Add Darlington Pair (NPN)"), "NDarlingtonElm")); + activeMenuBar.addItem(getClassCheckItem(Locale.LS("Add Darlington Pair (PNP)"), "PDarlingtonElm")); + activeMenuBar.addItem(getClassCheckItem(Locale.LS("Add Varactor/Varicap"), "VaractorElm")); + activeMenuBar.addItem(getClassCheckItem(Locale.LS("Add Tunnel Diode"), "TunnelDiodeElm")); + activeMenuBar.addItem(getClassCheckItem(Locale.LS("Add Triode"), "TriodeElm")); + activeMenuBar.addItem(getClassCheckItem(Locale.LS("Add Unijunction Transistor"), "UnijunctionElm")); + mainMenuBar.addItem(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml + Locale.LS(" Active Components")), activeMenuBar); + + MenuBar activeBlocMenuBar = new MenuBar(true); + activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add Op Amp (ideal, - on top)"), "OpAmpElm")); + activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add Op Amp (ideal, + on top)"), "OpAmpSwapElm")); + activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add Op Amp (real)"), "OpAmpRealElm")); + activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add Analog Switch (SPST)"), "AnalogSwitchElm")); + activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add Analog Switch (SPDT)"), "AnalogSwitch2Elm")); + activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add Tristate Buffer"), "TriStateElm")); + activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add Schmitt Trigger"), "SchmittElm")); + activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add Schmitt Trigger (Inverting)"), "InvertingSchmittElm")); + activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add Delay Buffer"), "DelayBufferElm")); + activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add CCII+"), "CC2Elm")); + activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add CCII-"), "CC2NegElm")); + activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add Comparator (Hi-Z/GND output)"), "ComparatorElm")); + activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add OTA (LM13700 style)"), "OTAElm")); + activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add Voltage-Controlled Voltage Source (VCVS)"), "VCVSElm")); + activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add Voltage-Controlled Current Source (VCCS)"), "VCCSElm")); + activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add Current-Controlled Voltage Source (CCVS)"), "CCVSElm")); + activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add Current-Controlled Current Source (CCCS)"), "CCCSElm")); + activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add Optocoupler"), "OptocouplerElm")); + activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add Time Delay Relay"), "TimeDelayRelayElm")); + activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add LM317"), "CustomCompositeElm:~LM317-v2")); + activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add TL431"), "CustomCompositeElm:~TL431")); + activeBlocMenuBar.addItem(getClassCheckItem(Locale.LS("Add Subcircuit Instance"), "CustomCompositeElm")); + mainMenuBar.addItem(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml + Locale.LS(" Active Building Blocks")), activeBlocMenuBar); + + MenuBar gateMenuBar = new MenuBar(true); + gateMenuBar.addItem(getClassCheckItem(Locale.LS("Add Logic Input"), "LogicInputElm")); + gateMenuBar.addItem(getClassCheckItem(Locale.LS("Add Logic Output"), "LogicOutputElm")); + gateMenuBar.addItem(getClassCheckItem(Locale.LS("Add Inverter"), "InverterElm")); + gateMenuBar.addItem(getClassCheckItem(Locale.LS("Add NAND Gate"), "NandGateElm")); + gateMenuBar.addItem(getClassCheckItem(Locale.LS("Add NOR Gate"), "NorGateElm")); + gateMenuBar.addItem(getClassCheckItem(Locale.LS("Add AND Gate"), "AndGateElm")); + gateMenuBar.addItem(getClassCheckItem(Locale.LS("Add OR Gate"), "OrGateElm")); + gateMenuBar.addItem(getClassCheckItem(Locale.LS("Add XOR Gate"), "XorGateElm")); + mainMenuBar.addItem(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml + Locale.LS(" Logic Gates, Input and Output")), gateMenuBar); + + MenuBar chipMenuBar = new MenuBar(true); + chipMenuBar.addItem(getClassCheckItem(Locale.LS("Add D Flip-Flop"), "DFlipFlopElm")); + chipMenuBar.addItem(getClassCheckItem(Locale.LS("Add JK Flip-Flop"), "JKFlipFlopElm")); + chipMenuBar.addItem(getClassCheckItem(Locale.LS("Add T Flip-Flop"), "TFlipFlopElm")); + chipMenuBar.addItem(getClassCheckItem(Locale.LS("Add 7 Segment LED"), "SevenSegElm")); + chipMenuBar.addItem(getClassCheckItem(Locale.LS("Add 7 Segment Decoder"), "SevenSegDecoderElm")); + chipMenuBar.addItem(getClassCheckItem(Locale.LS("Add Multiplexer"), "MultiplexerElm")); + chipMenuBar.addItem(getClassCheckItem(Locale.LS("Add Demultiplexer"), "DeMultiplexerElm")); + chipMenuBar.addItem(getClassCheckItem(Locale.LS("Add SIPO shift register"), "SipoShiftElm")); + chipMenuBar.addItem(getClassCheckItem(Locale.LS("Add PISO shift register"), "PisoShiftElm")); + chipMenuBar.addItem(getClassCheckItem(Locale.LS("Add Counter"), "CounterElm")); + chipMenuBar.addItem(getClassCheckItem(Locale.LS("Add Counter w/ Load"), "Counter2Elm")); + chipMenuBar.addItem(getClassCheckItem(Locale.LS("Add Ring Counter"), "DecadeElm")); + chipMenuBar.addItem(getClassCheckItem(Locale.LS("Add Latch"), "LatchElm")); + chipMenuBar.addItem(getClassCheckItem(Locale.LS("Add Sequence generator"), "SeqGenElm")); + chipMenuBar.addItem(getClassCheckItem(Locale.LS("Add Adder"), "FullAdderElm")); + chipMenuBar.addItem(getClassCheckItem(Locale.LS("Add Half Adder"), "HalfAdderElm")); + chipMenuBar.addItem(getClassCheckItem(Locale.LS("Add Custom Logic"), "UserDefinedLogicElm")); // don't change this, it will break people's saved shortcuts + chipMenuBar.addItem(getClassCheckItem(Locale.LS("Add Static RAM"), "SRAMElm")); + mainMenuBar.addItem(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml + Locale.LS(" Digital Chips")), chipMenuBar); + + MenuBar achipMenuBar = new MenuBar(true); + achipMenuBar.addItem(getClassCheckItem(Locale.LS("Add 555 Timer"), "TimerElm")); + achipMenuBar.addItem(getClassCheckItem(Locale.LS("Add Phase Comparator"), "PhaseCompElm")); + achipMenuBar.addItem(getClassCheckItem(Locale.LS("Add DAC"), "DACElm")); + achipMenuBar.addItem(getClassCheckItem(Locale.LS("Add ADC"), "ADCElm")); + achipMenuBar.addItem(getClassCheckItem(Locale.LS("Add VCO"), "VCOElm")); + achipMenuBar.addItem(getClassCheckItem(Locale.LS("Add Monostable"), "MonostableElm")); + mainMenuBar.addItem(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml + Locale.LS(" Analog and Hybrid Chips")), achipMenuBar); + + if (subcircuitMenuBar == null) + subcircuitMenuBar = new MenuBar[2]; + subcircuitMenuBar[num] = new MenuBar(true); + mainMenuBar.addItem(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml + Locale.LS(" Subcircuits")), subcircuitMenuBar[num]); + + MenuBar otherMenuBar = new MenuBar(true); + CheckboxMenuItem mi; + otherMenuBar.addItem(mi = getClassCheckItem(Locale.LS("Drag All"), "DragAll")); + mi.setShortcut(Locale.LS("(Alt-drag)")); + otherMenuBar.addItem(mi = getClassCheckItem(Locale.LS("Drag Row"), "DragRow")); + mi.setShortcut(Locale.LS("(A-S-drag)")); + otherMenuBar.addItem(mi = getClassCheckItem(Locale.LS("Drag Column"), "DragColumn")); + mi.setShortcut(isMac ? Locale.LS("(A-Cmd-drag)") : Locale.LS("(A-M-drag)")); + otherMenuBar.addItem(getClassCheckItem(Locale.LS("Drag Selected"), "DragSelected")); + otherMenuBar.addItem(mi = getClassCheckItem(Locale.LS("Drag Post"), "DragPost")); + mi.setShortcut("(" + ctrlMetaKey + "drag)"); + + mainMenuBar.addItem(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml + Locale.LS(" Drag")), otherMenuBar); + + mainMenuBar.addItem(mi = getClassCheckItem(Locale.LS("Select/Drag Sel"), "Select")); + mi.setShortcut(Locale.LS("(space or Shift-drag)")); } void composeSubcircuitMenu() { - if (subcircuitMenuBar == null) - return; - int mi; - - // there are two menus to update: the one in the Draw menu, and the one in the right mouse menu - for (mi = 0; mi != 2; mi++) { - MenuBar menu = subcircuitMenuBar[mi]; - menu.clearItems(); - Vector list = CustomCompositeModel.getModelList(); - int i; - for (i = 0; i != list.size(); i++) { - String name = list.get(i).name; - menu.addItem(getClassCheckItem(Locale.LS("Add ") + name, "CustomCompositeElm:" + name)); - } - } - lastSubcircuitMenuUpdate = CustomCompositeModel.sequenceNumber; + if (subcircuitMenuBar == null) + return; + int mi; + + // there are two menus to update: the one in the Draw menu, and the one in the right mouse menu + for (mi = 0; mi != 2; mi++) { + MenuBar menu = subcircuitMenuBar[mi]; + menu.clearItems(); + Vector list = CustomCompositeModel.getModelList(); + int i; + for (i = 0; i != list.size(); i++) { + String name = list.get(i).name; + menu.addItem(getClassCheckItem(Locale.LS("Add ") + name, "CustomCompositeElm:" + name)); + } + } + lastSubcircuitMenuUpdate = CustomCompositeModel.sequenceNumber; } public void composeSelectScopeMenu(MenuBar sb) { - sb.clearItems(); - selectScopeMenuItems = new Vector(); - for( int i = 0; i < scopeCount; i++) { - String s, l; - s = Locale.LS("Scope")+" "+ Integer.toString(i+1); - l=scopes[i].getScopeLabelOrText(); - if (l!="") - s+=" ("+SafeHtmlUtils.htmlEscape(l)+")"; - selectScopeMenuItems.add(new MenuItem(s ,new MyCommand("elm", "addToScope"+Integer.toString(i)))); - } - int c = countScopeElms(); - for (int j = 0; j < c; j++) { - String s,l; - s = Locale.LS("Undocked Scope")+" "+ Integer.toString(j+1); - l = getNthScopeElm(j).elmScope.getScopeLabelOrText(); - if (l!="") - s += " ("+SafeHtmlUtils.htmlEscape(l)+")"; - selectScopeMenuItems.add(new MenuItem(s, new MyCommand("elm", "addToScope"+Integer.toString(scopeCount+j)))); - } - for (MenuItem mi : selectScopeMenuItems) - sb.addItem(mi); + sb.clearItems(); + selectScopeMenuItems = new Vector(); + for (int i = 0; i < scopeCount; i++) { + String s, l; + s = Locale.LS("Scope") + " " + Integer.toString(i + 1); + l = scopes[i].getScopeLabelOrText(); + if (l != "") + s += " (" + SafeHtmlUtils.htmlEscape(l) + ")"; + selectScopeMenuItems.add(new MenuItem(s, new MyCommand("elm", "addToScope" + Integer.toString(i)))); + } + int c = countScopeElms(); + for (int j = 0; j < c; j++) { + String s, l; + s = Locale.LS("Undocked Scope") + " " + Integer.toString(j + 1); + l = getNthScopeElm(j).elmScope.getScopeLabelOrText(); + if (l != "") + s += " (" + SafeHtmlUtils.htmlEscape(l) + ")"; + selectScopeMenuItems.add(new MenuItem(s, new MyCommand("elm", "addToScope" + Integer.toString(scopeCount + j)))); + } + for (MenuItem mi : selectScopeMenuItems) + sb.addItem(mi); } public void setSlidersPanelHeight() { - int i; - int cumheight=0; - for (i=0; i < verticalPanel.getWidgetIndex(slidersPanel); i++) { - if (verticalPanel.getWidget(i) !=loadFileInput) { - cumheight=cumheight+verticalPanel.getWidget(i).getOffsetHeight(); - if (verticalPanel.getWidget(i).getStyleName().contains("topSpace")) - cumheight+=12; - } - } - int ih=RootLayoutPanel.get().getOffsetHeight()-MENUBARHEIGHT-cumheight; - if (ih<0) - ih=0; - slidersPanel.setHeight(ih+"px"); + int i; + int cumheight = 0; + for (i = 0; i < verticalPanel.getWidgetIndex(slidersPanel); i++) { + if (verticalPanel.getWidget(i) != loadFileInput) { + cumheight = cumheight + verticalPanel.getWidget(i).getOffsetHeight(); + if (verticalPanel.getWidget(i).getStyleName().contains("topSpace")) + cumheight += 12; + } + } + int ih = RootLayoutPanel.get().getOffsetHeight() - MENUBARHEIGHT - cumheight; + if (ih < 0) + ih = 0; + slidersPanel.setHeight(ih + "px"); } - - CheckboxMenuItem getClassCheckItem(String s, String t) { - // try { - // Class c = Class.forName(t); - String shortcut=""; - CircuitElm elm = null; - try { - elm = constructElement(t, 0, 0); - } catch (Exception e) {} - CheckboxMenuItem mi; - // register(c, elm); - if ( elm!=null ) { - if (elm.needsShortcut() ) { - shortcut += (char)elm.getShortcut(); - if (shortcuts[elm.getShortcut()] != null && !shortcuts[elm.getShortcut()].equals(t)) - console("already have shortcut for " + (char)elm.getShortcut() + " " + elm); - shortcuts[elm.getShortcut()]=t; - } - elm.delete(); - } + // try { + // Class c = Class.forName(t); + String shortcut = ""; + CircuitElm elm = null; + try { + elm = constructElement(t, 0, 0); + } catch (Exception e) { + } + CheckboxMenuItem mi; + // register(c, elm); + if (elm != null) { + if (elm.needsShortcut()) { + shortcut += (char) elm.getShortcut(); + if (shortcuts[elm.getShortcut()] != null && !shortcuts[elm.getShortcut()].equals(t)) + console("already have shortcut for " + (char) elm.getShortcut() + " " + elm); + shortcuts[elm.getShortcut()] = t; + } + elm.delete(); + } // else // GWT.log("Coudn't create class: "+t); - // } catch (Exception ee) { - // ee.printStackTrace(); - // } - if (shortcut=="") - mi= new CheckboxMenuItem(s); - else - mi = new CheckboxMenuItem(s, shortcut); - mi.setScheduledCommand(new MyCommand("main", t) ); - mainMenuItems.add(mi); - mainMenuItemNames.add(t); - return mi; + // } catch (Exception ee) { + // ee.printStackTrace(); + // } + if (shortcut == "") + mi = new CheckboxMenuItem(s); + else + mi = new CheckboxMenuItem(s, shortcut); + mi.setScheduledCommand(new MyCommand("main", t)); + mainMenuItems.add(mi); + mainMenuItemNames.add(t); + return mi; } - + void centreCircuit() { - if (elmList == null) // avoid exception if called during initialization - return; + if (elmList == null) // avoid exception if called during initialization + return; - Rectangle bounds = getCircuitBounds(); - setCircuitArea(); + Rectangle bounds = getCircuitBounds(); + setCircuitArea(); - double scale = 1; - int cheight = circuitArea.height; + double scale = 1; + int cheight = circuitArea.height; - // if there's no scope, and the window isn't very wide, then don't use all of the circuit area when - // centering, because the info in the corner might not get in the way. We still want circuitArea to be the full - // height though, to allow the user to put stuff there manually. - if (scopeCount == 0 && circuitArea.width < 800) { - int h = (int) ((double)cheight * scopeHeightFraction); - cheight -= h; - } + // if there's no scope, and the window isn't very wide, then don't use all of the circuit area when + // centering, because the info in the corner might not get in the way. We still want circuitArea to be the full + // height though, to allow the user to put stuff there manually. + if (scopeCount == 0 && circuitArea.width < 800) { + int h = (int) ((double) cheight * scopeHeightFraction); + cheight -= h; + } - if (bounds != null) - // add some space on edges because bounds calculation is not perfect - scale = Math.min(circuitArea.width /(double)(bounds.width+140), - cheight/(double)(bounds.height+100)); - scale = Math.min(scale, 1.5); // Limit scale so we don't create enormous circuits in big windows - - // calculate transform so circuit fills most of screen - transform[0] = transform[3] = scale; - transform[1] = transform[2] = transform[4] = transform[5] = 0; - if (bounds != null) { - transform[4] = (circuitArea.width -bounds.width *scale)/2 - bounds.x*scale; - transform[5] = (cheight-bounds.height*scale)/2 - bounds.y*scale; - } + if (bounds != null) + // add some space on edges because bounds calculation is not perfect + scale = Math.min(circuitArea.width / (double) (bounds.width + 140), + cheight / (double) (bounds.height + 100)); + scale = Math.min(scale, 1.5); // Limit scale so we don't create enormous circuits in big windows + + // calculate transform so circuit fills most of screen + transform[0] = transform[3] = scale; + transform[1] = transform[2] = transform[4] = transform[5] = 0; + if (bounds != null) { + transform[4] = (circuitArea.width - bounds.width * scale) / 2 - bounds.x * scale; + transform[5] = (cheight - bounds.height * scale) / 2 - bounds.y * scale; + } } // get circuit bounds. remember this doesn't use setBbox(). That is calculated when we draw // the circuit, but this needs to be ready before we first draw it, so we use this crude method Rectangle getCircuitBounds() { - int i; - int minx = 30000, maxx = -30000, miny = 30000, maxy = -30000; - 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)); - } - if (minx > maxx) - return null; - return new Rectangle(minx, miny, maxx-minx, maxy-miny); + int i; + int minx = 30000, maxx = -30000, miny = 30000, maxy = -30000; + 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)); + } + if (minx > maxx) + return null; + return new Rectangle(minx, miny, maxx - minx, maxy - miny); } long lastTime = 0, lastFrameTime, lastIterTime, secTime = 0; @@ -1500,39 +1511,39 @@ Rectangle getCircuitBounds() { public void setSimRunning(boolean s) { - if (s) { - if (stopMessage != null) - return; - simRunning = true; - runStopButton.setHTML(Locale.LSHTML("RUN / Stop")); - runStopButton.setStylePrimaryName("topButton"); - timer.scheduleRepeating(FASTTIMER); - } else { - simRunning = false; - runStopButton.setHTML(Locale.LSHTML("Run / STOP")); - runStopButton.setStylePrimaryName("topButton-red"); - timer.cancel(); - repaint(); - } + if (s) { + if (stopMessage != null) + return; + simRunning = true; + runStopButton.setHTML(Locale.LSHTML("RUN / Stop")); + runStopButton.setStylePrimaryName("topButton"); + timer.scheduleRepeating(FASTTIMER); + } else { + simRunning = false; + runStopButton.setHTML(Locale.LSHTML("Run / STOP")); + runStopButton.setStylePrimaryName("topButton-red"); + timer.cancel(); + repaint(); + } } public boolean simIsRunning() { - return simRunning; + return simRunning; } boolean needsRepaint; void repaint() { - if (!needsRepaint) { - needsRepaint = true; - Scheduler.get().scheduleFixedDelay(new Scheduler.RepeatingCommand() { - public boolean execute() { - updateCircuit(); - needsRepaint = false; - return false; - } - }, FASTTIMER); - } + if (!needsRepaint) { + needsRepaint = true; + Scheduler.get().scheduleFixedDelay(new Scheduler.RepeatingCommand() { + public boolean execute() { + updateCircuit(); + needsRepaint = false; + return false; + } + }, FASTTIMER); + } } // ***************************************************************** @@ -1661,9 +1672,9 @@ public void updateCircuit() { // for some mouse modes, what matters is not the posts but the endpoints (which // are only the same for 2-terminal elements). We draw those now if needed if (tempMouseMode == MODE_DRAG_ROW || - tempMouseMode == MODE_DRAG_COLUMN || - tempMouseMode == MODE_DRAG_POST || - tempMouseMode == MODE_DRAG_SELECTED) { + tempMouseMode == MODE_DRAG_COLUMN || + tempMouseMode == MODE_DRAG_POST || + tempMouseMode == MODE_DRAG_SELECTED) { for (int i = 0; i != elmList.size(); i++) { CircuitElm ce = getElm(i); @@ -1765,54 +1776,54 @@ public void updateCircuit() { } void drawBottomArea(Graphics g) { - int leftX = 0; - int h = 0; - if (stopMessage == null && scopeCount == 0) { - leftX = max(canvasWidth-infoWidth, 0); - int h0 = (int) (canvasHeight * scopeHeightFraction); - h = (mouseElm == null) ? 70 : h0; - if (hideInfoBox) - h = 0; - } - if (stopMessage != null && circuitArea.height > canvasHeight-30) - h = 30; - g.setColor(printableCheckItem.getState() ? "#eee" : "#111"); - g.fillRect(leftX, circuitArea.height-h, circuitArea.width, canvasHeight-circuitArea.height+h); - g.setFont(CircuitElm.unitsFont); - int ct = scopeCount; - if (stopMessage != null) - ct = 0; - int i; - Scope.clearCursorInfo(); - for (i = 0; i != ct; i++) - scopes[i].selectScope(mouseCursorX, mouseCursorY); - if (scopeElmArr != null) - for (i=0; i != scopeElmArr.length; i++) - scopeElmArr[i].selectScope(mouseCursorX, mouseCursorY); - for (i = 0; i != ct; i++) - scopes[i].draw(g); - if (mouseWasOverSplitter) { - g.setColor(CircuitElm.selectColor); - g.setLineWidth(4.0); - g.drawLine(0, circuitArea.height-2, circuitArea.width, circuitArea.height-2); - g.setLineWidth(1.0); - } - g.setColor(CircuitElm.whiteColor); - - if (stopMessage != null) { - g.drawString(stopMessage, 10, canvasHeight-10); - } else if (!hideInfoBox) { - // in JS it doesn't matter how big this is, there's no out-of-bounds exception - String info[] = new String[10]; - if (mouseElm != null) { - if (mousePost == -1) { - mouseElm.getInfo(info); - info[0] = Locale.LS(info[0]); - if (info[1] != null) - info[1] = Locale.LS(info[1]); - } else - info[0] = "V = " + - CircuitElm.getUnitText(mouseElm.getPostVoltage(mousePost), "V"); + int leftX = 0; + int h = 0; + if (stopMessage == null && scopeCount == 0) { + leftX = max(canvasWidth - infoWidth, 0); + int h0 = (int) (canvasHeight * scopeHeightFraction); + h = (mouseElm == null) ? 70 : h0; + if (hideInfoBox) + h = 0; + } + if (stopMessage != null && circuitArea.height > canvasHeight - 30) + h = 30; + g.setColor(printableCheckItem.getState() ? "#eee" : "#111"); + g.fillRect(leftX, circuitArea.height - h, circuitArea.width, canvasHeight - circuitArea.height + h); + g.setFont(CircuitElm.unitsFont); + int ct = scopeCount; + if (stopMessage != null) + ct = 0; + int i; + Scope.clearCursorInfo(); + for (i = 0; i != ct; i++) + scopes[i].selectScope(mouseCursorX, mouseCursorY); + if (scopeElmArr != null) + for (i = 0; i != scopeElmArr.length; i++) + scopeElmArr[i].selectScope(mouseCursorX, mouseCursorY); + for (i = 0; i != ct; i++) + scopes[i].draw(g); + if (mouseWasOverSplitter) { + g.setColor(CircuitElm.selectColor); + g.setLineWidth(4.0); + g.drawLine(0, circuitArea.height - 2, circuitArea.width, circuitArea.height - 2); + g.setLineWidth(1.0); + } + g.setColor(CircuitElm.whiteColor); + + if (stopMessage != null) { + g.drawString(stopMessage, 10, canvasHeight - 10); + } else if (!hideInfoBox) { + // in JS it doesn't matter how big this is, there's no out-of-bounds exception + String info[] = new String[10]; + if (mouseElm != null) { + if (mousePost == -1) { + mouseElm.getInfo(info); + info[0] = Locale.LS(info[0]); + if (info[1] != null) + info[1] = Locale.LS(info[1]); + } else + info[0] = "V = " + + CircuitElm.getUnitText(mouseElm.getPostVoltage(mousePost), "V"); // /* //shownodes // for (i = 0; i != mouseElm.getPostCount(); i++) // info[0] += " " + mouseElm.nodes[i]; @@ -1820,187 +1831,187 @@ void drawBottomArea(Graphics g) { // info[0] += ";" + (mouseElm.getVoltageSource()+nodeList.size()); // */ - } else { - info[0] = "t = " + CircuitElm.getTimeText(t); - double timerate = 160*getIterCount()*timeStep; - if (timerate >= .1) - info[0] += " (" + CircuitElm.showFormat.format(timerate) + "x)"; - info[1] = Locale.LS("time step = ") + CircuitElm.getTimeText(timeStep); - } - if (hintType != -1) { - for (i = 0; info[i] != null; i++) - ; - String s = getHint(); - if (s == null) - hintType = -1; - else - info[i] = s; - } - int x = leftX + 5; - if (ct != 0) - x = scopes[ct-1].rightEdge() + 20; + } else { + info[0] = "t = " + CircuitElm.getTimeText(t); + double timerate = 160 * getIterCount() * timeStep; + if (timerate >= .1) + info[0] += " (" + CircuitElm.showFormat.format(timerate) + "x)"; + info[1] = Locale.LS("time step = ") + CircuitElm.getTimeText(timeStep); + } + if (hintType != -1) { + for (i = 0; info[i] != null; i++) + ; + String s = getHint(); + if (s == null) + hintType = -1; + else + info[i] = s; + } + int x = leftX + 5; + if (ct != 0) + x = scopes[ct - 1].rightEdge() + 20; // x = max(x, canvasWidth*2/3); - // x=cv.getCoordinateSpaceWidth()*2/3; - - // count lines of data - for (i = 0; info[i] != null; i++) - ; - int badnodes = badConnectionList.size(); - if (badnodes > 0) - info[i++] = badnodes + ((badnodes == 1) ? - Locale.LS(" bad connection") : Locale.LS(" bad connections")); - if (savedFlag) - info[i++] = "(saved)"; - - int ybase = circuitArea.height-h; - for (i = 0; info[i] != null; i++) - g.drawString(info[i], x, ybase+15*(i+1)); - } + // x=cv.getCoordinateSpaceWidth()*2/3; + + // count lines of data + for (i = 0; info[i] != null; i++) + ; + int badnodes = badConnectionList.size(); + if (badnodes > 0) + info[i++] = badnodes + ((badnodes == 1) ? + Locale.LS(" bad connection") : Locale.LS(" bad connections")); + if (savedFlag) + info[i++] = "(saved)"; + + int ybase = circuitArea.height - h; + for (i = 0; info[i] != null; i++) + g.drawString(info[i], x, ybase + 15 * (i + 1)); + } } Color getBackgroundColor() { - if (printableCheckItem.getState()) - return Color.white; - return Color.black; + if (printableCheckItem.getState()) + return Color.white; + return Color.black; } int oldScopeCount = -1; boolean scopeMenuIsSelected(Scope s) { - if (scopeMenuSelected < 0) - return false; - if (scopeMenuSelected < scopeCount) - return scopes[scopeMenuSelected] == s; - return getNthScopeElm(scopeMenuSelected-scopeCount).elmScope == s; + if (scopeMenuSelected < 0) + return false; + if (scopeMenuSelected < scopeCount) + return scopes[scopeMenuSelected] == s; + return getNthScopeElm(scopeMenuSelected - scopeCount).elmScope == s; } - native boolean isSidePanelCheckboxChecked() /*-{ + native boolean isSidePanelCheckboxChecked() /*-{ return $doc.getElementById("trigger").checked; }-*/; 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 (scopes[i].needToRemove()) { - 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].getElm() == null) - scopeCount--; - int h = canvasHeight - 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 = (canvasWidth-iw) / colct; // Оно! - if (isSidePanelCheckboxChecked()) - w = (canvasWidth-iw-VERTICALPANELWIDTH) / 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; - } - s.stackCount = scopeColCount[pos]; - if (s.speed != speed) { - s.speed = speed; - s.resetGraph(); - } - Rectangle r = new Rectangle(pos*w, canvasHeight-h+colh*row, w-marg, colh); - row++; - if (!r.equals(s.rect)) - s.setRect(r); - } - if (oldScopeCount != scopeCount) { - setCircuitArea(); - oldScopeCount = scopeCount; - } + 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 (scopes[i].needToRemove()) { + 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].getElm() == null) + scopeCount--; + int h = canvasHeight - 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 = (canvasWidth - iw) / colct; // Оно! + if (isSidePanelCheckboxChecked()) + w = (canvasWidth - iw - VERTICALPANELWIDTH) / 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; + } + s.stackCount = scopeColCount[pos]; + if (s.speed != speed) { + s.speed = speed; + s.resetGraph(); + } + Rectangle r = new Rectangle(pos * w, canvasHeight - h + colh * row, w - marg, colh); + row++; + if (!r.equals(s.rect)) + s.setRect(r); + } + if (oldScopeCount != scopeCount) { + setCircuitArea(); + oldScopeCount = scopeCount; + } } 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 Locale.LS("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 Locale.LS("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 Locale.LS("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 Locale.LS("fc = ") + - CircuitElm.getUnitText(1/(2*pi*re.resistance*ce.capacitance), "Hz"); - } - return null; + 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 Locale.LS("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 Locale.LS("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 Locale.LS("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 Locale.LS("fc = ") + + CircuitElm.getUnitText(1 / (2 * pi * re.resistance * ce.capacitance), "Hz"); + } + return null; } // public void toggleSwitch(int n) { @@ -2020,8 +2031,8 @@ String getHint() { // } void needAnalyze() { - analyzeFlag = true; - repaint(); + analyzeFlag = true; + repaint(); } Vector nodeList; @@ -2030,25 +2041,25 @@ void needAnalyze() { CircuitElm voltageSources[]; public CircuitNode getCircuitNode(int n) { - if (n >= nodeList.size()) - return null; - return nodeList.elementAt(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); + if (n >= elmList.size()) + return null; + return elmList.elementAt(n); } public Adjustable findAdjustable(CircuitElm elm, int item) { - int i; - for (i = 0; i != adjustables.size(); i++) { - Adjustable a = adjustables.get(i); - if (a.elm == elm && a.editItem == item) - return a; - } - return null; + int i; + for (i = 0; i != adjustables.size(); i++) { + Adjustable a = adjustables.get(i); + if (a.elm == elm && a.editItem == item) + return a; + } + return null; } public static native void console(String text) @@ -2059,21 +2070,29 @@ public static native void console(String text) public static native void debugger() /*-{ debugger; }-*/; class NodeMapEntry { - int node; - NodeMapEntry() { node = -1; } - NodeMapEntry(int n) { node = n; } + int node; + + NodeMapEntry() { + node = -1; + } + + NodeMapEntry(int n) { + node = n; + } } + // map points to node numbers - HashMap nodeMap; - HashMap postCountMap; + HashMap nodeMap; + HashMap postCountMap; class WireInfo { - CircuitElm wire; - Vector neighbors; - int post; - WireInfo(CircuitElm w) { - wire = w; - } + CircuitElm wire; + Vector neighbors; + int post; + + WireInfo(CircuitElm w) { + wire = w; + } } // info about each wire and its neighbors, used to calculate wire currents @@ -2083,54 +2102,54 @@ class WireInfo { // up considerably by reducing the size of the matrix. We do this for wires, labeled nodes, and ground. // The actual node we map to is not assigned yet. Instead we map to the same NodeMapEntry. void calculateWireClosure() { - int i; - LabeledNodeElm.resetNodeList(); - GroundElm.resetNodeList(); - nodeMap = new HashMap(); + int i; + LabeledNodeElm.resetNodeList(); + GroundElm.resetNodeList(); + nodeMap = new HashMap(); // int mergeCount = 0; - wireInfoList = new Vector(); - for (i = 0; i != elmList.size(); i++) { - CircuitElm ce = getElm(i); - if (!ce.isRemovableWire()) - continue; - ce.hasWireInfo = false; - wireInfoList.add(new WireInfo(ce)); - Point p0 = ce.getPost(0); - NodeMapEntry cn = nodeMap.get(p0); - - // what post are we connected to - Point p1 = ce.getConnectedPost(); - if (p1 == null) { - // no connected post (true for labeled node the first time it's encountered, or ground) - if (cn == null) { - cn = new NodeMapEntry(); - nodeMap.put(p0, cn); - } - continue; - } - NodeMapEntry cn2 = nodeMap.get(p1); - if (cn != null && cn2 != null) { - // merge nodes; go through map and change all keys pointing to cn2 to point to cn - for (Map.Entry entry : nodeMap.entrySet()) { - if (entry.getValue() == cn2) - entry.setValue(cn); - } + wireInfoList = new Vector(); + for (i = 0; i != elmList.size(); i++) { + CircuitElm ce = getElm(i); + if (!ce.isRemovableWire()) + continue; + ce.hasWireInfo = false; + wireInfoList.add(new WireInfo(ce)); + Point p0 = ce.getPost(0); + NodeMapEntry cn = nodeMap.get(p0); + + // what post are we connected to + Point p1 = ce.getConnectedPost(); + if (p1 == null) { + // no connected post (true for labeled node the first time it's encountered, or ground) + if (cn == null) { + cn = new NodeMapEntry(); + nodeMap.put(p0, cn); + } + continue; + } + NodeMapEntry cn2 = nodeMap.get(p1); + if (cn != null && cn2 != null) { + // merge nodes; go through map and change all keys pointing to cn2 to point to cn + for (Map.Entry entry : nodeMap.entrySet()) { + if (entry.getValue() == cn2) + entry.setValue(cn); + } // mergeCount++; - continue; - } - if (cn != null) { - nodeMap.put(p1, cn); - continue; - } - if (cn2 != null) { - nodeMap.put(p0, cn2); - continue; - } - // new entry - cn = new NodeMapEntry(); - nodeMap.put(p0, cn); - nodeMap.put(p1, cn); - } + continue; + } + if (cn != null) { + nodeMap.put(p1, cn); + continue; + } + if (cn2 != null) { + nodeMap.put(p0, cn2); + continue; + } + // new entry + cn = new NodeMapEntry(); + nodeMap.put(p0, cn); + nodeMap.put(p1, cn); + } // console("got " + (groupCount-mergeCount) + " groups with " + nodeMap.size() + " nodes " + mergeCount); } @@ -2145,183 +2164,183 @@ void calculateWireClosure() { // each one containing a list of neighbors and which end to use (since one end may be ready before // the other) boolean calcWireInfo() { - int i; - int moved = 0; - - for (i = 0; i != wireInfoList.size(); i++) { - WireInfo wi = wireInfoList.get(i); - CircuitElm wire = wi.wire; - CircuitNode cn1 = nodeList.get(wire.getNode(0)); // both ends of wire have same node # - int j; - - Vector neighbors0 = new Vector(); - Vector neighbors1 = new Vector(); - - // assume each end is ready (except ground nodes which have one end) - // labeled nodes are treated as having 2 terminals, see below - boolean isReady0 = true, isReady1 = !(wire instanceof GroundElm); - - // go through elements sharing a node with this wire (may be connected indirectly - // by other wires, but at least it's faster than going through all elements) - for (j = 0; j != cn1.links.size(); j++) { - CircuitNodeLink cnl = cn1.links.get(j); - CircuitElm ce = cnl.elm; - if (ce == wire) - continue; - Point pt = ce.getPost(cnl.num); - - // is this a wire that doesn't have wire info yet? If so we can't use it yet. - // That would create a circular dependency. So that side isn't ready. - boolean notReady = (ce.isRemovableWire() && !ce.hasWireInfo); - - // which post does this element connect to, if any? - if (pt.x == wire.x && pt.y == wire.y) { - neighbors0.add(ce); - if (notReady) isReady0 = false; - } else if (wire.getPostCount() > 1) { - Point p2 = wire.getConnectedPost(); - if (pt.x == p2.x && pt.y == p2.y) { - neighbors1.add(ce); - if (notReady) isReady1 = false; - } - } else if (ce instanceof LabeledNodeElm && wire instanceof LabeledNodeElm && - ((LabeledNodeElm) ce).text == ((LabeledNodeElm) wire).text) { - // ce and wire are both labeled nodes with matching labels. treat them as neighbors - neighbors1.add(ce); - if (notReady) isReady1 = false; - } - } - - // does one of the posts have all information necessary to calculate current? - if (isReady0) { - wi.neighbors = neighbors0; - wi.post = 0; - wire.hasWireInfo = true; - moved = 0; - } else if (isReady1) { - wi.neighbors = neighbors1; - wi.post = 1; - wire.hasWireInfo = true; - moved = 0; - } else { - // no, so move to the end of the list and try again later - wireInfoList.add(wireInfoList.remove(i--)); - moved++; - if (moved > wireInfoList.size() * 2) { - stop("wire loop detected", wire); - return false; - } - } - } + int i; + int moved = 0; + + for (i = 0; i != wireInfoList.size(); i++) { + WireInfo wi = wireInfoList.get(i); + CircuitElm wire = wi.wire; + CircuitNode cn1 = nodeList.get(wire.getNode(0)); // both ends of wire have same node # + int j; + + Vector neighbors0 = new Vector(); + Vector neighbors1 = new Vector(); + + // assume each end is ready (except ground nodes which have one end) + // labeled nodes are treated as having 2 terminals, see below + boolean isReady0 = true, isReady1 = !(wire instanceof GroundElm); + + // go through elements sharing a node with this wire (may be connected indirectly + // by other wires, but at least it's faster than going through all elements) + for (j = 0; j != cn1.links.size(); j++) { + CircuitNodeLink cnl = cn1.links.get(j); + CircuitElm ce = cnl.elm; + if (ce == wire) + continue; + Point pt = ce.getPost(cnl.num); + + // is this a wire that doesn't have wire info yet? If so we can't use it yet. + // That would create a circular dependency. So that side isn't ready. + boolean notReady = (ce.isRemovableWire() && !ce.hasWireInfo); + + // which post does this element connect to, if any? + if (pt.x == wire.x && pt.y == wire.y) { + neighbors0.add(ce); + if (notReady) isReady0 = false; + } else if (wire.getPostCount() > 1) { + Point p2 = wire.getConnectedPost(); + if (pt.x == p2.x && pt.y == p2.y) { + neighbors1.add(ce); + if (notReady) isReady1 = false; + } + } else if (ce instanceof LabeledNodeElm && wire instanceof LabeledNodeElm && + ((LabeledNodeElm) ce).text == ((LabeledNodeElm) wire).text) { + // ce and wire are both labeled nodes with matching labels. treat them as neighbors + neighbors1.add(ce); + if (notReady) isReady1 = false; + } + } + + // does one of the posts have all information necessary to calculate current? + if (isReady0) { + wi.neighbors = neighbors0; + wi.post = 0; + wire.hasWireInfo = true; + moved = 0; + } else if (isReady1) { + wi.neighbors = neighbors1; + wi.post = 1; + wire.hasWireInfo = true; + moved = 0; + } else { + // no, so move to the end of the list and try again later + wireInfoList.add(wireInfoList.remove(i--)); + moved++; + if (moved > wireInfoList.size() * 2) { + stop("wire loop detected", wire); + return false; + } + } + } - return true; + return true; } // find or allocate ground node void setGroundNode() { - int i; - 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; - - // set ground node to 0 - NodeMapEntry nme = nodeMap.get(ce.getPost(0)); - nme.node = 0; - break; - } - if (ce instanceof RailElm) - gotRail = true; - if (volt == null && ce instanceof VoltageElm) - volt = ce; - } + int i; + 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; + + // set ground node to 0 + NodeMapEntry nme = nodeMap.get(ce.getPost(0)); + nme.node = 0; + 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); - nodeList.addElement(cn); - - // update node map - NodeMapEntry cln = nodeMap.get(pt); - if (cln != null) - cln.node = 0; - else - nodeMap.put(pt, new NodeMapEntry(0)); - } else { - // otherwise allocate extra node for ground - CircuitNode cn = new CircuitNode(); - nodeList.addElement(cn); - } + // 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); + nodeList.addElement(cn); + + // update node map + NodeMapEntry cln = nodeMap.get(pt); + if (cln != null) + cln.node = 0; + else + nodeMap.put(pt, new NodeMapEntry(0)); + } else { + // otherwise allocate extra node for ground + CircuitNode cn = new CircuitNode(); + nodeList.addElement(cn); + } } // make list of nodes void makeNodeList() { - int i, j; - int vscount = 0; - 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); - Integer g = postCountMap.get(pt); - postCountMap.put(pt, g == null ? 1 : g+1); - NodeMapEntry cln = nodeMap.get(pt); - - // is this node not in map yet? or is the node number unallocated? - // (we don't allocate nodes before this because changing the allocation order - // of nodes changes circuit behavior and breaks backward compatibility; - // the code below to connect unconnected nodes may connect a different node to ground) - if (cln == null || cln.node == -1) { - CircuitNode cn = new CircuitNode(); - CircuitNodeLink cnl = new CircuitNodeLink(); - cnl.num = j; - cnl.elm = ce; - cn.links.addElement(cnl); - ce.setNode(j, nodeList.size()); - if (cln != null) - cln.node = nodeList.size(); - else - nodeMap.put(pt, new NodeMapEntry(nodeList.size())); - nodeList.addElement(cn); - } else { - int n = cln.node; - CircuitNodeLink cnl = new CircuitNodeLink(); - cnl.num = j; - cnl.elm = ce; - getCircuitNode(n).links.addElement(cnl); - ce.setNode(j, n); - // if it's the ground node, make sure the node voltage is 0, - // cause it may not get set later - if (n == 0) - ce.setNodeVoltage(j, 0); - } - } - for (j = 0; j != inodes; j++) { - CircuitNode cn = new CircuitNode(); - 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); - } - - // also count voltage sources so we can allocate array - vscount += ivs; - } + int i, j; + int vscount = 0; + 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); + Integer g = postCountMap.get(pt); + postCountMap.put(pt, g == null ? 1 : g + 1); + NodeMapEntry cln = nodeMap.get(pt); + + // is this node not in map yet? or is the node number unallocated? + // (we don't allocate nodes before this because changing the allocation order + // of nodes changes circuit behavior and breaks backward compatibility; + // the code below to connect unconnected nodes may connect a different node to ground) + if (cln == null || cln.node == -1) { + CircuitNode cn = new CircuitNode(); + CircuitNodeLink cnl = new CircuitNodeLink(); + cnl.num = j; + cnl.elm = ce; + cn.links.addElement(cnl); + ce.setNode(j, nodeList.size()); + if (cln != null) + cln.node = nodeList.size(); + else + nodeMap.put(pt, new NodeMapEntry(nodeList.size())); + nodeList.addElement(cn); + } else { + int n = cln.node; + CircuitNodeLink cnl = new CircuitNodeLink(); + cnl.num = j; + cnl.elm = ce; + getCircuitNode(n).links.addElement(cnl); + ce.setNode(j, n); + // if it's the ground node, make sure the node voltage is 0, + // cause it may not get set later + if (n == 0) + ce.setNodeVoltage(j, 0); + } + } + for (j = 0; j != inodes; j++) { + CircuitNode cn = new CircuitNode(); + 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); + } + + // also count voltage sources so we can allocate array + vscount += ivs; + } voltageSources = new CircuitElm[vscount]; } @@ -2331,955 +2350,960 @@ void makeNodeList() { int nodesWithGroundConnectionCount; void findUnconnectedNodes() { - int i, j; - - // determine nodes that are not connected indirectly to ground. - // all nodes must be connected to ground somehow, or else we - // will get a matrix error. - boolean closure[] = new boolean[nodeList.size()]; - boolean changed = true; - unconnectedNodes = new Vector(); - nodesWithGroundConnection = new Vector(); - closure[0] = true; - while (changed) { - changed = false; - for (i = 0; i != elmList.size(); i++) { - CircuitElm ce = getElm(i); - if (ce instanceof WireElm) - continue; - // loop through all ce's nodes to see if they are connected - // to other nodes not in closure - boolean hasGround = false; - for (j = 0; j < ce.getConnectionNodeCount(); j++) { - boolean hg = ce.hasGroundConnection(j); - if (hg) - hasGround = true; - if (!closure[ce.getConnectionNode(j)]) { - if (hg) - closure[ce.getConnectionNode(j)] = changed = true; - continue; - } - int k; - for (k = 0; k != ce.getConnectionNodeCount(); k++) { - if (j == k) - continue; - int kn = ce.getConnectionNode(k); - if (ce.getConnection(j, k) && !closure[kn]) { - closure[kn] = true; - changed = true; - } - } - } - if (hasGround) - nodesWithGroundConnection.add(ce); - } - if (changed) - continue; - - // connect one of the unconnected nodes to ground with a big resistor, then try again - for (i = 0; i != nodeList.size(); i++) - if (!closure[i] && !getCircuitNode(i).internal) { - unconnectedNodes.add(i); - console("node " + i + " unconnected"); + int i, j; + + // determine nodes that are not connected indirectly to ground. + // all nodes must be connected to ground somehow, or else we + // will get a matrix error. + boolean closure[] = new boolean[nodeList.size()]; + boolean changed = true; + unconnectedNodes = new Vector(); + nodesWithGroundConnection = new Vector(); + closure[0] = true; + while (changed) { + changed = false; + for (i = 0; i != elmList.size(); i++) { + CircuitElm ce = getElm(i); + if (ce instanceof WireElm) + continue; + // loop through all ce's nodes to see if they are connected + // to other nodes not in closure + boolean hasGround = false; + for (j = 0; j < ce.getConnectionNodeCount(); j++) { + boolean hg = ce.hasGroundConnection(j); + if (hg) + hasGround = true; + if (!closure[ce.getConnectionNode(j)]) { + if (hg) + closure[ce.getConnectionNode(j)] = changed = true; + continue; + } + int k; + for (k = 0; k != ce.getConnectionNodeCount(); k++) { + if (j == k) + continue; + int kn = ce.getConnectionNode(k); + if (ce.getConnection(j, k) && !closure[kn]) { + closure[kn] = true; + changed = true; + } + } + } + if (hasGround) + nodesWithGroundConnection.add(ce); + } + if (changed) + continue; + + // connect one of the unconnected nodes to ground with a big resistor, then try again + for (i = 0; i != nodeList.size(); i++) + if (!closure[i] && !getCircuitNode(i).internal) { + unconnectedNodes.add(i); + console("node " + i + " unconnected"); // stampResistor(0, i, 1e8); // do this later in connectUnconnectedNodes() - closure[i] = true; - changed = true; - break; - } - } + closure[i] = true; + changed = true; + break; + } + } } // take list of unconnected nodes, which we identified earlier, and connect them to ground // with a big resistor. otherwise we will get matrix errors. The resistor has to be big, // otherwise circuits like 555 Square Wave will break void connectUnconnectedNodes() { - int i; - for (i = 0; i != unconnectedNodes.size(); i++) { - int n = unconnectedNodes.get(i); - stampResistor(0, n, 1e8); - } + int i; + for (i = 0; i != unconnectedNodes.size(); i++) { + int n = unconnectedNodes.get(i); + stampResistor(0, n, 1e8); + } } boolean validateCircuit() { - int i, j; - - 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)); - if (!fpi.findPath(ce.getNode(0))) { + int i, j; + + 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)); + if (!fpi.findPath(ce.getNode(0))) { // console(ce + " no path"); - ce.reset(); - } - } - // look for current sources with no current path - if (ce instanceof CurrentElm) { - CurrentElm cur = (CurrentElm) ce; - FindPathInfo fpi = new FindPathInfo(FindPathInfo.INDUCT, ce, - ce.getNode(1)); - cur.setBroken(!fpi.findPath(ce.getNode(0))); - } - if (ce instanceof VCCSElm) { - VCCSElm cur = (VCCSElm) ce; - FindPathInfo fpi = new FindPathInfo(FindPathInfo.INDUCT, ce, - cur.getOutputNode(0)); - if (cur.hasCurrentOutput() && !fpi.findPath(cur.getOutputNode(1))) { - cur.broken = true; - } else - cur.broken = false; - } - - // look for voltage source or wire loops. we do this for voltage sources - if (ce.getPostCount() == 2) { - if (ce instanceof VoltageElm) { - 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 false; - } - } - } - - // look for path from rail to ground - if (ce instanceof RailElm || ce instanceof LogicInputElm) { - FindPathInfo fpi = new FindPathInfo(FindPathInfo.VOLTAGE, ce, ce.getNode(0)); - if (fpi.findPath(0)) { - stop("Path to ground with no resistance!", ce); - return false; - } - } - - // 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))) { - console(ce + " shorted"); - ((CapacitorElm) ce).shorted(); - } else { - // a capacitor loop used to cause a matrix error. but we changed the capacitor model - // so it works fine now. The only issue is if a capacitor is added in parallel with - // another capacitor with a nonzero voltage; in that case we will get oscillation unless - // we reset both capacitors to have the same voltage. Rather than check for that, we just - // give an error. - fpi = new FindPathInfo(FindPathInfo.CAP_V, ce, ce.getNode(1)); - if (fpi.findPath(ce.getNode(0))) { - stop("Capacitor loop with no resistance!", ce); - return false; - } - } - } - } - return true; + ce.reset(); + } + } + // look for current sources with no current path + if (ce instanceof CurrentElm) { + CurrentElm cur = (CurrentElm) ce; + FindPathInfo fpi = new FindPathInfo(FindPathInfo.INDUCT, ce, + ce.getNode(1)); + cur.setBroken(!fpi.findPath(ce.getNode(0))); + } + if (ce instanceof VCCSElm) { + VCCSElm cur = (VCCSElm) ce; + FindPathInfo fpi = new FindPathInfo(FindPathInfo.INDUCT, ce, + cur.getOutputNode(0)); + if (cur.hasCurrentOutput() && !fpi.findPath(cur.getOutputNode(1))) { + cur.broken = true; + } else + cur.broken = false; + } + + // look for voltage source or wire loops. we do this for voltage sources + if (ce.getPostCount() == 2) { + if (ce instanceof VoltageElm) { + 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 false; + } + } + } + + // look for path from rail to ground + if (ce instanceof RailElm || ce instanceof LogicInputElm) { + FindPathInfo fpi = new FindPathInfo(FindPathInfo.VOLTAGE, ce, ce.getNode(0)); + if (fpi.findPath(0)) { + stop("Path to ground with no resistance!", ce); + return false; + } + } + + // 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))) { + console(ce + " shorted"); + ((CapacitorElm) ce).shorted(); + } else { + // a capacitor loop used to cause a matrix error. but we changed the capacitor model + // so it works fine now. The only issue is if a capacitor is added in parallel with + // another capacitor with a nonzero voltage; in that case we will get oscillation unless + // we reset both capacitors to have the same voltage. Rather than check for that, we just + // give an error. + fpi = new FindPathInfo(FindPathInfo.CAP_V, ce, ce.getNode(1)); + if (fpi.findPath(ce.getNode(0))) { + stop("Capacitor loop with no resistance!", ce); + return false; + } + } + } + } + return true; } // analyze the circuit when something changes, so it can be simulated void analyzeCircuit() { - stopMessage = null; - stopElm = null; - if (elmList.isEmpty()) { - postDrawList = new Vector(); - badConnectionList = new Vector(); - return; - } - int i, j; - nodeList = new Vector(); - postCountMap = new HashMap(); - - calculateWireClosure(); - setGroundNode(); - - // allocate nodes and voltage sources - makeNodeList(); - - makePostDrawList(); - if (!calcWireInfo()) - return; - nodeMap = null; // done with this - - int vscount = 0; - circuitNonLinear = false; - - // determine if circuit is nonlinear. also set voltage sources - 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; - - // show resistance in voltage sources if there's only one. - // can't use voltageSourceCount here since that counts internal voltage sources, like the one in GroundElm - boolean gotVoltageSource = false; - showResistanceInVoltageSources = true; - for (i = 0; i != elmList.size(); i++) { - CircuitElm ce = getElm(i); - if (ce instanceof VoltageElm) { - if (gotVoltageSource) - showResistanceInVoltageSources = false; - else - gotVoltageSource = true; - } - } + stopMessage = null; + stopElm = null; + if (elmList.isEmpty()) { + postDrawList = new Vector(); + badConnectionList = new Vector(); + return; + } + int i, j; + nodeList = new Vector(); + postCountMap = new HashMap(); + + calculateWireClosure(); + setGroundNode(); + + // allocate nodes and voltage sources + makeNodeList(); + + makePostDrawList(); + if (!calcWireInfo()) + return; + nodeMap = null; // done with this + + int vscount = 0; + circuitNonLinear = false; + + // determine if circuit is nonlinear. also set voltage sources + 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; + + // show resistance in voltage sources if there's only one. + // can't use voltageSourceCount here since that counts internal voltage sources, like the one in GroundElm + boolean gotVoltageSource = false; + showResistanceInVoltageSources = true; + for (i = 0; i != elmList.size(); i++) { + CircuitElm ce = getElm(i); + if (ce instanceof VoltageElm) { + if (gotVoltageSource) + showResistanceInVoltageSources = false; + else + gotVoltageSource = true; + } + } - findUnconnectedNodes(); - if (!validateCircuit()) - return; + findUnconnectedNodes(); + if (!validateCircuit()) + return; - nodesWithGroundConnectionCount = nodesWithGroundConnection.size(); - // only need this for validation - nodesWithGroundConnection = null; + nodesWithGroundConnectionCount = nodesWithGroundConnection.size(); + // only need this for validation + nodesWithGroundConnection = null; - timeStep = maxTimeStep; - needsStamp = true; + timeStep = maxTimeStep; + needsStamp = true; - callAnalyzeHook(); + callAnalyzeHook(); } // stamp the matrix, meaning populate the matrix as required to simulate the circuit (for all linear elements, at least) void stampCircuit() { - int i; - int matrixSize = nodeList.size()-1 + voltageSourceCount; - circuitMatrix = new double[matrixSize][matrixSize]; - circuitRightSide = new double[matrixSize]; - nodeVoltages = new double[nodeList.size()-1]; - if (lastNodeVoltages == null || lastNodeVoltages.length != nodeVoltages.length) - lastNodeVoltages = new double[nodeList.size()-1]; - origMatrix = new double[matrixSize][matrixSize]; - origRightSide = new double[matrixSize]; - circuitMatrixSize = circuitMatrixFullSize = matrixSize; - circuitRowInfo = new RowInfo[matrixSize]; - circuitPermute = new int[matrixSize]; - for (i = 0; i != matrixSize; i++) - circuitRowInfo[i] = new RowInfo(); - circuitNeedsMap = false; - - connectUnconnectedNodes(); - - // stamp linear circuit elements - for (i = 0; i != elmList.size(); i++) { - CircuitElm ce = getElm(i); - ce.setParentList(elmList); - ce.stamp(); - } + int i; + int matrixSize = nodeList.size() - 1 + voltageSourceCount; + circuitMatrix = new double[matrixSize][matrixSize]; + circuitRightSide = new double[matrixSize]; + nodeVoltages = new double[nodeList.size() - 1]; + if (lastNodeVoltages == null || lastNodeVoltages.length != nodeVoltages.length) + lastNodeVoltages = new double[nodeList.size() - 1]; + origMatrix = new double[matrixSize][matrixSize]; + origRightSide = new double[matrixSize]; + circuitMatrixSize = circuitMatrixFullSize = matrixSize; + circuitRowInfo = new RowInfo[matrixSize]; + circuitPermute = new int[matrixSize]; + for (i = 0; i != matrixSize; i++) + circuitRowInfo[i] = new RowInfo(); + circuitNeedsMap = false; + + connectUnconnectedNodes(); + + // stamp linear circuit elements + for (i = 0; i != elmList.size(); i++) { + CircuitElm ce = getElm(i); + ce.setParentList(elmList); + ce.stamp(); + } - if (!simplifyMatrix(matrixSize)) - return; + if (!simplifyMatrix(matrixSize)) + return; - // check if we called stop() - if (circuitMatrix == null) - return; + // check if we called stop() + if (circuitMatrix == null) + return; - // 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; - } - } + // 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; + } + } - // copy elmList to an array to avoid a bunch of calls to canCast() when doing simulation - elmArr = new CircuitElm[elmList.size()]; - int scopeElmCount = 0; - for (i = 0; i != elmList.size(); i++) { - elmArr[i] = elmList.get(i); - if (elmArr[i] instanceof ScopeElm) - scopeElmCount++; - } + // copy elmList to an array to avoid a bunch of calls to canCast() when doing simulation + elmArr = new CircuitElm[elmList.size()]; + int scopeElmCount = 0; + for (i = 0; i != elmList.size(); i++) { + elmArr[i] = elmList.get(i); + if (elmArr[i] instanceof ScopeElm) + scopeElmCount++; + } - // copy ScopeElms to an array to avoid a second pass over entire list of elms during simulation - scopeElmArr = new ScopeElm[scopeElmCount]; - int j = 0; - for (i = 0; i != elmList.size(); i++) { - if (elmArr[i] instanceof ScopeElm) - scopeElmArr[j++] = (ScopeElm) elmArr[i]; - } + // copy ScopeElms to an array to avoid a second pass over entire list of elms during simulation + scopeElmArr = new ScopeElm[scopeElmCount]; + int j = 0; + for (i = 0; i != elmList.size(); i++) { + if (elmArr[i] instanceof ScopeElm) + scopeElmArr[j++] = (ScopeElm) elmArr[i]; + } - needsStamp = false; + needsStamp = false; } // simplify the matrix; this speeds things up quite a bit, especially for digital circuits. // or at least it did before we added wire removal boolean simplifyMatrix(int matrixSize) { - int i, j; - for (i = 0; i != matrixSize; i++) { - int qp = -1; - double qv = 0; - RowInfo re = circuitRowInfo[i]; + int i, j; + for (i = 0; i != matrixSize; i++) { + int qp = -1; + double qv = 0; + RowInfo re = circuitRowInfo[i]; /*System.out.println("row " + i + " " + re.lsChanges + " " + re.rsChanges + " " + re.dropRow);*/ // if (qp != -100) continue; // uncomment this line to disable matrix simplification for debugging purposes - if (re.lsChanges || re.dropRow || re.rsChanges) - continue; - double rsadd = 0; - - // see if this row 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; - } - // ignore zeroes - if (q == 0) - continue; - // keep track of first nonzero element that is not ROW_CONST - if (qp == -1) { - qp = j; - qv = q; - continue; - } - // more than one nonzero element? give up - break; - } - if (j == matrixSize) { - if (qp == -1) { - // probably a singular matrix, try disabling matrix simplification above to check this - stop("Matrix error", null); - return false; - } - RowInfo elt = circuitRowInfo[qp]; - // we found a row with only one nonzero nonconst entry; that value - // is a constant - if (elt.type != RowInfo.ROW_NORMAL) { - System.out.println("type already " + elt.type + " for " + qp + "!"); - continue; - } - elt.type = RowInfo.ROW_CONST; + if (re.lsChanges || re.dropRow || re.rsChanges) + continue; + double rsadd = 0; + + // see if this row 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; + } + // ignore zeroes + if (q == 0) + continue; + // keep track of first nonzero element that is not ROW_CONST + if (qp == -1) { + qp = j; + qv = q; + continue; + } + // more than one nonzero element? give up + break; + } + if (j == matrixSize) { + if (qp == -1) { + // probably a singular matrix, try disabling matrix simplification above to check this + stop("Matrix error", null); + return false; + } + RowInfo elt = circuitRowInfo[qp]; + // we found a row with only one nonzero nonconst entry; that value + // is a constant + if (elt.type != RowInfo.ROW_NORMAL) { + System.out.println("type already " + elt.type + " for " + qp + "!"); + continue; + } + elt.type = RowInfo.ROW_CONST; // console("ROW_CONST " + i + " " + rsadd); - elt.value = (circuitRightSide[i]+rsadd)/qv; - circuitRowInfo[i].dropRow = true; - // find first row that referenced the element we just deleted - for (j = 0; j != i; j++) - if (circuitMatrix[j][qp] != 0) - break; - // start over just before that - i = j-1; - } - } - //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_CONST) - elt.mapCol = -1; - } + elt.value = (circuitRightSide[i] + rsadd) / qv; + circuitRowInfo[i].dropRow = true; + // find first row that referenced the element we just deleted + for (j = 0; j != i; j++) + if (circuitMatrix[j][qp] != 0) + break; + // start over just before that + i = j - 1; + } + } + //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_CONST) + elt.mapCol = -1; + } - // 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++; - } + // 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++; + } // console("old size = " + matrixSize + " new size = " + newsize); - 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; - return true; + 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; + return true; } // make list of posts we need to draw. posts shared by 2 elements should be hidden, all // others should be drawn. We can't use the node list for this purpose anymore because wires // have the same node number at both ends. void makePostDrawList() { - postDrawList = new Vector(); - badConnectionList = new Vector(); - for (Map.Entry entry : postCountMap.entrySet()) { - if (entry.getValue() != 2) - postDrawList.add(entry.getKey()); - - // look for bad connections, posts not connected to other elements which intersect - // other elements' bounding boxes - if (entry.getValue() == 1) { - int j; - boolean bad = false; - Point cn = entry.getKey(); - for (j = 0; j != elmList.size() && !bad; j++) { - CircuitElm ce = getElm(j); - if ( ce instanceof GraphicElm ) - continue; - // does this post intersect elm's bounding box? - if (!ce.boundingBox.contains(cn.x, cn.y)) - continue; - int k; - // does this post belong to the elm? - int pc = ce.getPostCount(); - for (k = 0; k != pc; k++) - if (ce.getPost(k).equals(cn)) - break; - if (k == pc) - bad = true; - } - if (bad) - badConnectionList.add(cn); - } - } - postCountMap = null; + postDrawList = new Vector(); + badConnectionList = new Vector(); + for (Map.Entry entry : postCountMap.entrySet()) { + if (entry.getValue() != 2) + postDrawList.add(entry.getKey()); + + // look for bad connections, posts not connected to other elements which intersect + // other elements' bounding boxes + if (entry.getValue() == 1) { + int j; + boolean bad = false; + Point cn = entry.getKey(); + for (j = 0; j != elmList.size() && !bad; j++) { + CircuitElm ce = getElm(j); + if (ce instanceof GraphicElm) + continue; + // does this post intersect elm's bounding box? + if (!ce.boundingBox.contains(cn.x, cn.y)) + continue; + int k; + // does this post belong to the elm? + int pc = ce.getPostCount(); + for (k = 0; k != pc; k++) + if (ce.getPost(k).equals(cn)) + break; + if (k == pc) + bad = true; + } + if (bad) + badConnectionList.add(cn); + } + } + postCountMap = null; } class FindPathInfo { - static final int INDUCT = 1; - static final int VOLTAGE = 2; - static final int SHORT = 3; - static final int CAP_V = 4; - boolean visited[]; - int dest; - CircuitElm firstElm; - int type; - - // State object to help find loops in circuit subject to various conditions (depending on type_) - // elm_ = source and destination element. dest_ = destination node. - FindPathInfo(int type_, CircuitElm elm_, int dest_) { - dest = dest_; - type = type_; - firstElm = elm_; - visited = new boolean[nodeList.size()]; - } + static final int INDUCT = 1; + static final int VOLTAGE = 2; + static final int SHORT = 3; + static final int CAP_V = 4; + boolean visited[]; + int dest; + CircuitElm firstElm; + int type; + + // State object to help find loops in circuit subject to various conditions (depending on type_) + // elm_ = source and destination element. dest_ = destination node. + FindPathInfo(int type_, CircuitElm elm_, int dest_) { + dest = dest_; + type = type_; + firstElm = elm_; + visited = new boolean[nodeList.size()]; + } - // look through circuit for loop starting at node n1 of firstElm, for a path back to - // dest node of firstElm - boolean findPath(int n1) { - if (n1 == dest) - return true; - - // depth first search, don't need to revisit already visited nodes! - if (visited[n1]) - return false; - - visited[n1] = true; - CircuitNode cn = getCircuitNode(n1); - int i; - if (cn == null) - return false; - for (i = 0; i != cn.links.size(); i++) { - CircuitNodeLink cnl = cn.links.get(i); - CircuitElm ce = cnl.elm; - if (checkElm(n1, ce)) - return true; - } - if (n1 == 0) { - for (i = 0; i != nodesWithGroundConnection.size(); i++) - if (checkElm(0, nodesWithGroundConnection.get(i))) - return true; - } - return false; - } + // look through circuit for loop starting at node n1 of firstElm, for a path back to + // dest node of firstElm + boolean findPath(int n1) { + if (n1 == dest) + return true; - boolean checkElm(int n1, CircuitElm ce) { - if (ce == firstElm) - return false; - if (type == INDUCT) { - // inductors need a path free of current sources - if (ce instanceof CurrentElm) - return false; - } - if (type == VOLTAGE) { - // when checking for voltage loops, we only care about voltage sources/wires/ground - if (!(ce.isWireEquivalent() || ce instanceof VoltageElm || ce instanceof GroundElm)) - return false; - } - // when checking for shorts, just check wires - if (type == SHORT && !ce.isWireEquivalent()) - return false; - if (type == CAP_V) { - // checking for capacitor/voltage source loops - if (!(ce.isWireEquivalent() || ce instanceof CapacitorElm || ce instanceof VoltageElm)) - return false; - } - if (n1 == 0) { - // look for posts which have a ground connection; - // our path can go through ground - int j; - for (j = 0; j != ce.getConnectionNodeCount(); j++) - if (ce.hasGroundConnection(j) && findPath(ce.getConnectionNode(j))) - return true; - } - int j; - for (j = 0; j != ce.getConnectionNodeCount(); j++) { - if (ce.getConnectionNode(j) == n1) { - if (ce.hasGroundConnection(j) && findPath(0)) - return true; - if (type == INDUCT && ce instanceof InductorElm) { - // inductors can use paths with other inductors of matching current - double c = ce.getCurrent(); - if (j == 0) - c = -c; - if (Math.abs(c-firstElm.getCurrent()) > 1e-10) - continue; - } - int k; - for (k = 0; k != ce.getConnectionNodeCount(); k++) { - if (j == k) - continue; - if (ce.getConnection(j, k) && findPath(ce.getConnectionNode(k))) { - //System.out.println("got findpath " + n1); - return true; - } - } - } - } - return false; - } + // depth first search, don't need to revisit already visited nodes! + if (visited[n1]) + return false; + + visited[n1] = true; + CircuitNode cn = getCircuitNode(n1); + int i; + if (cn == null) + return false; + for (i = 0; i != cn.links.size(); i++) { + CircuitNodeLink cnl = cn.links.get(i); + CircuitElm ce = cnl.elm; + if (checkElm(n1, ce)) + return true; + } + if (n1 == 0) { + for (i = 0; i != nodesWithGroundConnection.size(); i++) + if (checkElm(0, nodesWithGroundConnection.get(i))) + return true; + } + return false; + } + + boolean checkElm(int n1, CircuitElm ce) { + if (ce == firstElm) + return false; + if (type == INDUCT) { + // inductors need a path free of current sources + if (ce instanceof CurrentElm) + return false; + } + if (type == VOLTAGE) { + // when checking for voltage loops, we only care about voltage sources/wires/ground + if (!(ce.isWireEquivalent() || ce instanceof VoltageElm || ce instanceof GroundElm)) + return false; + } + // when checking for shorts, just check wires + if (type == SHORT && !ce.isWireEquivalent()) + return false; + if (type == CAP_V) { + // checking for capacitor/voltage source loops + if (!(ce.isWireEquivalent() || ce instanceof CapacitorElm || ce instanceof VoltageElm)) + return false; + } + if (n1 == 0) { + // look for posts which have a ground connection; + // our path can go through ground + int j; + for (j = 0; j != ce.getConnectionNodeCount(); j++) + if (ce.hasGroundConnection(j) && findPath(ce.getConnectionNode(j))) + return true; + } + int j; + for (j = 0; j != ce.getConnectionNodeCount(); j++) { + if (ce.getConnectionNode(j) == n1) { + if (ce.hasGroundConnection(j) && findPath(0)) + return true; + if (type == INDUCT && ce instanceof InductorElm) { + // inductors can use paths with other inductors of matching current + double c = ce.getCurrent(); + if (j == 0) + c = -c; + if (Math.abs(c - firstElm.getCurrent()) > 1e-10) + continue; + } + int k; + for (k = 0; k != ce.getConnectionNodeCount(); k++) { + if (j == k) + continue; + if (ce.getConnection(j, k) && findPath(ce.getConnectionNode(k))) { + //System.out.println("got findpath " + n1); + return true; + } + } + } + } + return false; + } } void stop(String s, CircuitElm ce) { - stopMessage = Locale.LS(s); - circuitMatrix = null; // causes an exception - stopElm = ce; - setSimRunning(false); - analyzeFlag = false; + stopMessage = Locale.LS(s); + circuitMatrix = null; // causes an exception + stopElm = ce; + setSimRunning(false); + 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); + 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); + 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(), by updateVoltageSource() 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); + int vn = nodeList.size() + vs; + stampMatrix(vn, n1, -1); + stampMatrix(vn, n2, 1); + stampRightSide(vn); + stampMatrix(n1, vn, 1); + stampMatrix(n2, vn, -1); } // update voltage source in doStep() void updateVoltageSource(int n1, int n2, int vs, double v) { - int vn = nodeList.size()+vs; - stampRightSide(vn, 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); + 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); + stampMatrix(n1, n1, r0); + stampMatrix(n2, n2, r0); + stampMatrix(n1, n2, -r0); + stampMatrix(n2, n1, -r0); } // specify that 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); + 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); + stampRightSide(n1, -i); + stampRightSide(n2, i); } // stamp a current source from n1 to n2 depending on current through vs void stampCCCS(int n1, int n2, int vs, double gain) { - int vn = nodeList.size()+vs; - stampMatrix(n1, vn, gain); - stampMatrix(n2, vn, -gain); + int vn = nodeList.size() + vs; + stampMatrix(n1, vn, gain); + stampMatrix(n2, vn, -gain); } // stamp value x in row i, column j, meaning that a voltage change // of dv in node j will increase the current into node i by x dv. // (Unless i or j is a voltage source node.) void stampMatrix(int i, int j, double x) { - if (Double.isInfinite(x)) - debugger(); - if (i > 0 && j > 0) { - if (circuitNeedsMap) { - i = circuitRowInfo[i-1].mapRow; - RowInfo ri = circuitRowInfo[j-1]; - if (ri.type == RowInfo.ROW_CONST) { - //System.out.println("Stamping constant " + i + " " + j + " " + x); - circuitRightSide[i] -= x*ri.value; - return; - } - j = ri.mapCol; - //System.out.println("stamping " + i + " " + j + " " + x); - } else { - i--; - j--; - } - circuitMatrix[i][j] += x; - } + if (Double.isInfinite(x)) + debugger(); + if (i > 0 && j > 0) { + if (circuitNeedsMap) { + i = circuitRowInfo[i - 1].mapRow; + RowInfo ri = circuitRowInfo[j - 1]; + if (ri.type == RowInfo.ROW_CONST) { + //System.out.println("Stamping constant " + i + " " + j + " " + x); + circuitRightSide[i] -= x * ri.value; + return; + } + j = ri.mapCol; + //System.out.println("stamping " + i + " " + j + " " + x); + } else { + i--; + j--; + } + circuitMatrix[i][j] += x; + } } // stamp value x on the right side of row i, representing an // independent current source flowing into node i void stampRightSide(int i, double x) { - if (i > 0) { - if (circuitNeedsMap) { - i = circuitRowInfo[i-1].mapRow; - //System.out.println("stamping " + i + " " + x); - } else - i--; - circuitRightSide[i] += x; - } + if (i > 0) { + if (circuitNeedsMap) { + i = circuitRowInfo[i - 1].mapRow; + //System.out.println("stamping " + i + " " + x); + } else + i--; + circuitRightSide[i] += x; + } } // indicate that the value on the right side of row i changes in doStep() void stampRightSide(int i) { - //System.out.println("rschanges true " + (i-1)); - if (i > 0) - circuitRowInfo[i-1].rsChanges = true; + //System.out.println("rschanges true " + (i-1)); + if (i > 0) + circuitRowInfo[i - 1].rsChanges = true; } // indicate that the values on the left side of row i change in doStep() void stampNonLinear(int i) { - if (i > 0) - circuitRowInfo[i-1].lsChanges = true; + if (i > 0) + circuitRowInfo[i - 1].lsChanges = true; } double getIterCount() { - // IES - remove interaction - if (speedBar.getValue() == 0) - return 0; + // IES - remove interaction + if (speedBar.getValue() == 0) + return 0; - return .1*Math.exp((speedBar.getValue()-61)/24.); + return .1 * Math.exp((speedBar.getValue() - 61) / 24.); } // we need to calculate wire currents for every iteration if someone is viewing a wire in the // scope. Otherwise we can do it only once per frame. boolean canDelayWireProcessing() { - int i; - for (i = 0; i != scopeCount; i++) - if (scopes[i].viewingWire()) - return false; - for (i=0; i != elmList.size(); i++) - if (getElm(i) instanceof ScopeElm && ((ScopeElm)getElm(i)).elmScope.viewingWire()) - return false; - return true; + int i; + for (i = 0; i != scopeCount; i++) + if (scopes[i].viewingWire()) + return false; + for (i = 0; i != elmList.size(); i++) + if (getElm(i) instanceof ScopeElm && ((ScopeElm) getElm(i)).elmScope.viewingWire()) + return false; + return true; } boolean converged; int subIterations; void runCircuit(boolean didAnalyze) { - if (circuitMatrix == null || elmList.size() == 0) { - circuitMatrix = null; - return; - } - int iter; - //int maxIter = getIterCount(); - boolean debugprint = dumpMatrix; - dumpMatrix = false; - long steprate = (long) (160*getIterCount()); - long tm = System.currentTimeMillis(); - long lit = lastIterTime; - if (lit == 0) { - lastIterTime = tm; - return; - } + if (circuitMatrix == null || elmList.size() == 0) { + circuitMatrix = null; + return; + } + int iter; + //int maxIter = getIterCount(); + boolean debugprint = dumpMatrix; + dumpMatrix = false; + long steprate = (long) (160 * getIterCount()); + long tm = System.currentTimeMillis(); + long lit = lastIterTime; + if (lit == 0) { + lastIterTime = tm; + return; + } + + // Check if we don't need to run simulation (for very slow simulation speeds). + // If the circuit changed, do at least one iteration to make sure everything is consistent. + if (1000 >= steprate * (tm - lastIterTime) && !didAnalyze) + return; + + boolean delayWireProcessing = canDelayWireProcessing(); - // Check if we don't need to run simulation (for very slow simulation speeds). - // If the circuit changed, do at least one iteration to make sure everything is consistent. - if (1000 >= steprate*(tm-lastIterTime) && !didAnalyze) - return; - - boolean delayWireProcessing = canDelayWireProcessing(); - - int timeStepCountAtFrameStart = timeStepCount; - - // keep track of iterations completed without convergence issues - int goodIterations = 100; - - for (iter = 1; ; iter++) { - if (goodIterations >= 3 && timeStep < maxTimeStep) { - // things are going well, double the time step - timeStep = Math.min(timeStep*2, maxTimeStep); - console("timestep up = " + timeStep + " at " + t); - stampCircuit(); - goodIterations = 0; - } - - int i, j, subiter; - for (i = 0; i != elmArr.length; i++) - elmArr[i].startIteration(); - steps++; - int subiterCount = (adjustTimeStep && timeStep/2 > minTimeStep) ? 100 : 5000; - for (subiter = 0; subiter != subiterCount; subiter++) { - converged = true; - subIterations = subiter; + int timeStepCountAtFrameStart = timeStepCount; + + // keep track of iterations completed without convergence issues + int goodIterations = 100; + + for (iter = 1; ; iter++) { + if (goodIterations >= 3 && timeStep < maxTimeStep) { + // things are going well, double the time step + timeStep = Math.min(timeStep * 2, maxTimeStep); + console("timestep up = " + timeStep + " at " + t); + stampCircuit(); + goodIterations = 0; + } + + int i, j, subiter; + for (i = 0; i != elmArr.length; i++) + elmArr[i].startIteration(); + steps++; + int subiterCount = (adjustTimeStep && timeStep / 2 > minTimeStep) ? 100 : 5000; + for (subiter = 0; subiter != subiterCount; subiter++) { + converged = true; + subIterations = subiter; // if (t % .030 < .002 && timeStep > 1e-6) // force nonconvergence for debugging // converged = false; - for (i = 0; i != circuitMatrixSize; i++) - circuitRightSide[i] = origRightSide[i]; - if (circuitNonLinear) { - for (i = 0; i != circuitMatrixSize; i++) - for (j = 0; j != circuitMatrixSize; j++) - circuitMatrix[i][j] = origMatrix[i][j]; - } - for (i = 0; i != elmArr.length; i++) - elmArr[i].doStep(); - if (stopMessage != null) - return; - boolean printit = debugprint; - debugprint = false; - if (circuitMatrixSize < 8) { - // we only need this for debugging purposes, so skip it for large matrices - for (j = 0; j != circuitMatrixSize; j++) { - for (i = 0; i != circuitMatrixSize; i++) { - double x = circuitMatrix[i][j]; - if (Double.isNaN(x) || Double.isInfinite(x)) { - stop("nan/infinite matrix!", null); - console("circuitMatrix " + i + " " + j + " is " + x); - return; - } - } - } - } - if (printit) { - for (j = 0; j != circuitMatrixSize; j++) { - String x = ""; - for (i = 0; i != circuitMatrixSize; i++) - x += circuitMatrix[j][i] + ","; - x += "\n"; - console(x); - } - console("done"); - } - if (circuitNonLinear) { - // stop if converged (elements check for convergence in doStep()) - if (converged && subiter > 0) - break; - if (!lu_factor(circuitMatrix, circuitMatrixSize, - circuitPermute)) { - stop("Singular matrix!", null); - return; - } - } - lu_solve(circuitMatrix, circuitMatrixSize, circuitPermute, - circuitRightSide); - applySolvedRightSide(circuitRightSide); - if (!circuitNonLinear) - break; - } - if (subiter == subiterCount) { - // convergence failed - goodIterations = 0; - if (adjustTimeStep) { - timeStep /= 2; - console("timestep down to " + timeStep + " at " + t); - } - if (timeStep < minTimeStep || !adjustTimeStep) { - console("convergence failed after " + subiter + " iterations"); - stop("Convergence failed!", null); - break; - } - // we reduced the timestep. reset circuit state to the way it was at start of iteration - setNodeVoltages(lastNodeVoltages); - stampCircuit(); - continue; - } - if (subiter > 5 || timeStep < maxTimeStep) - console("converged after " + subiter + " iterations, timeStep = " + timeStep); - if (subiter < 3) - goodIterations++; - else - goodIterations = 0; - t += timeStep; - timeStepAccum += timeStep; - if (timeStepAccum >= maxTimeStep) { - timeStepAccum -= maxTimeStep; - timeStepCount++; - } - for (i = 0; i != elmArr.length; i++) - elmArr[i].stepFinished(); - if (!delayWireProcessing) - calcWireCurrents(); - for (i = 0; i != scopeCount; i++) - scopes[i].timeStep(); - for (i=0; i != scopeElmArr.length; i++) - scopeElmArr[i].stepScope(); - callTimeStepHook(); - // save last node voltages so we can restart the next iteration if necessary - for (i = 0; i != lastNodeVoltages.length; i++) - lastNodeVoltages[i] = nodeVoltages[i]; + for (i = 0; i != circuitMatrixSize; i++) + circuitRightSide[i] = origRightSide[i]; + if (circuitNonLinear) { + for (i = 0; i != circuitMatrixSize; i++) + for (j = 0; j != circuitMatrixSize; j++) + circuitMatrix[i][j] = origMatrix[i][j]; + } + for (i = 0; i != elmArr.length; i++) + elmArr[i].doStep(); + if (stopMessage != null) + return; + boolean printit = debugprint; + debugprint = false; + if (circuitMatrixSize < 8) { + // we only need this for debugging purposes, so skip it for large matrices + for (j = 0; j != circuitMatrixSize; j++) { + for (i = 0; i != circuitMatrixSize; i++) { + double x = circuitMatrix[i][j]; + if (Double.isNaN(x) || Double.isInfinite(x)) { + stop("nan/infinite matrix!", null); + console("circuitMatrix " + i + " " + j + " is " + x); + return; + } + } + } + } + if (printit) { + for (j = 0; j != circuitMatrixSize; j++) { + String x = ""; + for (i = 0; i != circuitMatrixSize; i++) + x += circuitMatrix[j][i] + ","; + x += "\n"; + console(x); + } + console("done"); + } + if (circuitNonLinear) { + // stop if converged (elements check for convergence in doStep()) + if (converged && subiter > 0) + break; + if (!lu_factor(circuitMatrix, circuitMatrixSize, + circuitPermute)) { + stop("Singular matrix!", null); + return; + } + } + lu_solve(circuitMatrix, circuitMatrixSize, circuitPermute, + circuitRightSide); + applySolvedRightSide(circuitRightSide); + if (!circuitNonLinear) + break; + } + if (subiter == subiterCount) { + // convergence failed + goodIterations = 0; + if (adjustTimeStep) { + timeStep /= 2; + console("timestep down to " + timeStep + " at " + t); + } + if (timeStep < minTimeStep || !adjustTimeStep) { + console("convergence failed after " + subiter + " iterations"); + stop("Convergence failed!", null); + break; + } + // we reduced the timestep. reset circuit state to the way it was at start of iteration + setNodeVoltages(lastNodeVoltages); + stampCircuit(); + continue; + } + if (subiter > 5 || timeStep < maxTimeStep) + console("converged after " + subiter + " iterations, timeStep = " + timeStep); + if (subiter < 3) + goodIterations++; + else + goodIterations = 0; + t += timeStep; + timeStepAccum += timeStep; + if (timeStepAccum >= maxTimeStep) { + timeStepAccum -= maxTimeStep; + timeStepCount++; + } + for (i = 0; i != elmArr.length; i++) + elmArr[i].stepFinished(); + if (!delayWireProcessing) + calcWireCurrents(); + for (i = 0; i != scopeCount; i++) + scopes[i].timeStep(); + for (i = 0; i != scopeElmArr.length; i++) + scopeElmArr[i].stepScope(); + callTimeStepHook(); + // save last node voltages so we can restart the next iteration if necessary + for (i = 0; i != lastNodeVoltages.length; i++) + lastNodeVoltages[i] = nodeVoltages[i]; // console("set lastrightside at " + t + " " + lastNodeVoltages); - tm = System.currentTimeMillis(); - lit = tm; - // Check whether enough time has elapsed to perform an *additional* iteration after - // those we have already completed. But limit total computation time to 500ms (2fps) - if ((timeStepCount-timeStepCountAtFrameStart)*1000 >= steprate*(tm-lastIterTime) || (tm-lastFrameTime > 500)) - break; - if (!simRunning) - break; - } // for (iter = 1; ; iter++) - lastIterTime = lit; - if (delayWireProcessing) - calcWireCurrents(); + tm = System.currentTimeMillis(); + lit = tm; + // Check whether enough time has elapsed to perform an *additional* iteration after + // those we have already completed. But limit total computation time to 500ms (2fps) + if ((timeStepCount - timeStepCountAtFrameStart) * 1000 >= steprate * (tm - lastIterTime) || (tm - lastFrameTime > 500)) + break; + if (!simRunning) + break; + } // for (iter = 1; ; iter++) + lastIterTime = lit; + if (delayWireProcessing) + calcWireCurrents(); // System.out.println((System.currentTimeMillis()-lastFrameTime)/(double) iter); } // set node voltages given right side found by solving matrix void applySolvedRightSide(double rs[]) { // console("setvoltages " + rs); - int j; - for (j = 0; j != circuitMatrixFullSize; j++) { - RowInfo ri = circuitRowInfo[j]; - double res = 0; - if (ri.type == RowInfo.ROW_CONST) - res = ri.value; - else - res = rs[ri.mapCol]; - if (Double.isNaN(res)) { - converged = false; - break; - } - if (j < nodeList.size()-1) { - nodeVoltages[j] = res; - } else { - int ji = j-(nodeList.size()-1); - voltageSources[ji].setCurrent(ji, res); - } - } + int j; + for (j = 0; j != circuitMatrixFullSize; j++) { + RowInfo ri = circuitRowInfo[j]; + double res = 0; + if (ri.type == RowInfo.ROW_CONST) + res = ri.value; + else + res = rs[ri.mapCol]; + if (Double.isNaN(res)) { + converged = false; + break; + } + if (j < nodeList.size() - 1) { + nodeVoltages[j] = res; + } else { + int ji = j - (nodeList.size() - 1); + voltageSources[ji].setCurrent(ji, res); + } + } - setNodeVoltages(nodeVoltages); + setNodeVoltages(nodeVoltages); } // set node voltages in each element given an array of node voltages void setNodeVoltages(double nv[]) { - int j, k; - for (j = 0; j != nv.length; j++) { - double res = nv[j]; - CircuitNode cn = getCircuitNode(j+1); - for (k = 0; k != cn.links.size(); k++) { - CircuitNodeLink cnl = cn.links.elementAt(k); - cnl.elm.setNodeVoltage(cnl.num, res); - } - } + int j, k; + for (j = 0; j != nv.length; j++) { + double res = nv[j]; + CircuitNode cn = getCircuitNode(j + 1); + for (k = 0; k != cn.links.size(); k++) { + CircuitNodeLink cnl = cn.links.elementAt(k); + cnl.elm.setNodeVoltage(cnl.num, res); + } + } } // we removed wires from the matrix to speed things up. in order to display wire currents, // we need to calculate them now. void calcWireCurrents() { - int i; - - // for debugging - //for (i = 0; i != wireInfoList.size(); i++) - // wireInfoList.get(i).wire.setCurrent(-1, 1.23); - - for (i = 0; i != wireInfoList.size(); i++) { - WireInfo wi = wireInfoList.get(i); - double cur = 0; - int j; - Point p = wi.wire.getPost(wi.post); - for (j = 0; j != wi.neighbors.size(); j++) { - CircuitElm ce = wi.neighbors.get(j); - int n = ce.getNodeAtPoint(p.x, p.y); - cur += ce.getCurrentIntoNode(n); - } - // get correct current polarity - // (LabeledNodes may have wi.post == 1, in which case we flip the current sign) - if (wi.post == 0 || (wi.wire instanceof LabeledNodeElm)) - wi.wire.setCurrent(-1, cur); - else - wi.wire.setCurrent(-1, -cur); - } + int i; + + // for debugging + //for (i = 0; i != wireInfoList.size(); i++) + // wireInfoList.get(i).wire.setCurrent(-1, 1.23); + + for (i = 0; i != wireInfoList.size(); i++) { + WireInfo wi = wireInfoList.get(i); + double cur = 0; + int j; + Point p = wi.wire.getPost(wi.post); + for (j = 0; j != wi.neighbors.size(); j++) { + CircuitElm ce = wi.neighbors.get(j); + int n = ce.getNodeAtPoint(p.x, p.y); + cur += ce.getCurrentIntoNode(n); + } + // get correct current polarity + // (LabeledNodes may have wi.post == 1, in which case we flip the current sign) + if (wi.post == 0 || (wi.wire instanceof LabeledNodeElm)) + wi.wire.setCurrent(-1, cur); + else + wi.wire.setCurrent(-1, -cur); + } + } + + int min(int a, int b) { + return (a < b) ? a : b; } - int min(int a, int b) { return (a < b) ? a : b; } - int max(int a, int b) { return (a > b) ? a : b; } + int max(int a, int b) { + return (a > b) ? a : b; + } - public void resetAction(){ - int i; - analyzeFlag = true; - if (t == 0) - setSimRunning(true); - t = timeStepAccum = 0; - timeStepCount = 0; - for (i = 0; i != elmList.size(); i++) - getElm(i).reset(); - for (i = 0; i != scopeCount; i++) - scopes[i].resetGraph(true); - repaint(); + public void resetAction() { + int i; + analyzeFlag = true; + if (t == 0) + setSimRunning(true); + t = timeStepAccum = 0; + timeStepCount = 0; + for (i = 0; i != elmList.size(); i++) + getElm(i).reset(); + for (i = 0; i != scopeCount; i++) + scopes[i].resetGraph(true); + repaint(); } static void electronSaveAsCallback(String s) { - s = s.substring(s.lastIndexOf('/')+1); - s = s.substring(s.lastIndexOf('\\')+1); - theSim.setCircuitTitle(s); - theSim.allowSave(true); - theSim.savedFlag = true; - theSim.repaint(); + s = s.substring(s.lastIndexOf('/') + 1); + s = s.substring(s.lastIndexOf('\\') + 1); + theSim.setCircuitTitle(s); + theSim.allowSave(true); + theSim.savedFlag = true; + theSim.repaint(); } static void electronSaveCallback() { - theSim.savedFlag = true; - theSim.repaint(); + theSim.savedFlag = true; + theSim.repaint(); } static native void electronSaveAs(String dump) /*-{ @@ -3297,8 +3321,8 @@ static native void electronSave(String dump) /*-{ }-*/; static void electronOpenFileCallback(String text, String name) { - LoadFile.doLoadCallback(text, name); - theSim.allowSave(true); + LoadFile.doLoadCallback(text, name); + theSim.allowSave(true); } static native void electronOpenFile() /*-{ @@ -3320,605 +3344,601 @@ static native String getElectronStartCircuitText() /*-{ }-*/; void allowSave(boolean b) { - if (saveFileItem != null) - saveFileItem.setEnabled(b); + if (saveFileItem != null) + saveFileItem.setEnabled(b); } public void menuPerformed(String menu, String item) { - if ((menu=="edit" || menu=="main" || menu=="scopes") && noEditCheckItem.getState()) { - Window.alert(Locale.LS("Editing disabled. Re-enable from the Options menu.")); - return; - } - if (item=="help") - helpDialog = new HelpDialog(); - if (item=="license") - licenseDialog = new LicenseDialog(); - if (item=="about") - aboutBox = new AboutBox(circuitjs1.versionString); - if (item=="importfromlocalfile") { - pushUndo(); - if (isElectron()) - electronOpenFile(); - else - loadFileInput.click(); - } - if (item=="newwindow") { - Window.open(Document.get().getURL(), "_blank", ""); - } - if (item=="save") - electronSave(dumpCircuit()); - if (item=="saveas") - electronSaveAs(dumpCircuit()); - if (item=="importfromtext") { - dialogShowing = new ImportFromTextDialog(this); - } + if ((menu == "edit" || menu == "main" || menu == "scopes") && noEditCheckItem.getState()) { + Window.alert(Locale.LS("Editing disabled. Re-enable from the Options menu.")); + return; + } + if (item == "help") + helpDialog = new HelpDialog(); + if (item == "license") + licenseDialog = new LicenseDialog(); + if (item == "about") + aboutBox = new AboutBox(circuitjs1.versionString); + if (item == "importfromlocalfile") { + pushUndo(); + if (isElectron()) + electronOpenFile(); + else + loadFileInput.click(); + } + if (item == "newwindow") { + Window.open(Document.get().getURL(), "_blank", ""); + } + if (item == "save") + electronSave(dumpCircuit()); + if (item == "saveas") + electronSaveAs(dumpCircuit()); + if (item == "importfromtext") { + dialogShowing = new ImportFromTextDialog(this); + } /*if (item=="importfromdropbox") { dialogShowing = new ImportFromDropboxDialog(this); }*/ - if (item=="exportasurl") { - doExportAsUrl(); - unsavedChanges = false; - } - if (item=="exportaslocalfile") { - doExportAsLocalFile(); - unsavedChanges = false; - } - if (item=="exportastext") { - doExportAsText(); - unsavedChanges = false; - } - if (item=="exportasimage") - doExportAsImage(); - if (item=="exportassvg") - doExportAsSVG(); - if (item=="createsubcircuit") - doCreateSubcircuit(); - if (item=="dcanalysis") - doDCAnalysis(); - if (item=="print") - doPrint(); - if (item=="recover") - doRecover(); - - if ((menu=="elm" || menu=="scopepop") && contextPanel!=null) - contextPanel.hide(); - if (menu=="options" && item=="shortcuts") { - dialogShowing = new ShortcutsDialog(this); - dialogShowing.show(); - } - if (item=="search") { - dialogShowing = new SearchDialog(this); - dialogShowing.show(); - } - if (menu=="options" && item=="other") - doEdit(new EditOptions(this)); - if (item=="devtools") - toggleDevTools(); - if (item=="undo") - doUndo(); - if (item=="redo") - doRedo(); - - // if the mouse is hovering over an element, and a shortcut key is pressed, operate on that element (treat it like a context menu item selection) - if (menu == "key" && mouseElm != null) { - menuElm = mouseElm; - menu = "elm"; - } + if (item == "exportasurl") { + doExportAsUrl(); + unsavedChanges = false; + } + if (item == "exportaslocalfile") { + doExportAsLocalFile(); + unsavedChanges = false; + } + if (item == "exportastext") { + doExportAsText(); + unsavedChanges = false; + } + if (item == "exportasimage") + doExportAsImage(); + if (item == "exportassvg") + doExportAsSVG(); + if (item == "createsubcircuit") + doCreateSubcircuit(); + if (item == "dcanalysis") + doDCAnalysis(); + if (item == "print") + doPrint(); + if (item == "recover") + doRecover(); + + if ((menu == "elm" || menu == "scopepop") && contextPanel != null) + contextPanel.hide(); + if (menu == "options" && item == "shortcuts") { + dialogShowing = new ShortcutsDialog(this); + dialogShowing.show(); + } + if (item == "search") { + dialogShowing = new SearchDialog(this); + dialogShowing.show(); + } + if (menu == "options" && item == "other") + doEdit(new EditOptions(this)); + if (item == "devtools") + toggleDevTools(); + if (item == "undo") + doUndo(); + if (item == "redo") + doRedo(); + + // if the mouse is hovering over an element, and a shortcut key is pressed, operate on that element (treat it like a context menu item selection) + if (menu == "key" && mouseElm != null) { + menuElm = mouseElm; + menu = "elm"; + } - if (item == "cut") { - if (menu!="elm") - menuElm = null; - doCut(); - } - if (item == "copy") { - if (menu!="elm") - menuElm = null; - doCopy(); - } - if (item=="paste") - doPaste(null); - if (item=="duplicate") { - if (menu!="elm") - menuElm = null; - doDuplicate(); - } - if (item=="flip") - doFlip(); - if (item=="split") - doSplit(menuElm); - if (item=="selectAll") - doSelectAll(); - // if (e.getSource() == exitItem) { - // destroyFrame(); - // return; - // } - - if (item=="centrecircuit") { - pushUndo(); - centreCircuit(); - } - if (item=="stackAll") - stackAll(); - if (item=="unstackAll") - unstackAll(); - if (item=="combineAll") - combineAll(); - if (item=="separateAll") - separateAll(); - if (item=="zoomin") - zoomCircuit(20, true); - if (item=="zoomout") - zoomCircuit(-20, true); - if (item=="zoom100") - setCircuitScale(1, true); - if (menu=="elm" && item=="edit") - doEdit(menuElm); - if (item=="delete") { - if (menu!="elm") - menuElm = null; - pushUndo(); - doDelete(true); - } - if (item=="sliders") - doSliders(menuElm); - - if (item=="viewInScope" && menuElm != null) { - int i; - for (i = 0; i != scopeCount; i++) - if (scopes[i].getElm() == null) - break; - if (i == scopeCount) { - if (scopeCount == scopes.length) - return; - scopeCount++; - scopes[i] = new Scope(this); - scopes[i].position = i; - //handleResize(); - } - scopes[i].setElm(menuElm); - if (i > 0) - scopes[i].speed = scopes[i-1].speed; - } + if (item == "cut") { + if (menu != "elm") + menuElm = null; + doCut(); + } + if (item == "copy") { + if (menu != "elm") + menuElm = null; + doCopy(); + } + if (item == "paste") + doPaste(null); + if (item == "duplicate") { + if (menu != "elm") + menuElm = null; + doDuplicate(); + } + if (item == "flip") + doFlip(); + if (item == "split") + doSplit(menuElm); + if (item == "selectAll") + doSelectAll(); + // if (e.getSource() == exitItem) { + // destroyFrame(); + // return; + // } + + if (item == "centrecircuit") { + pushUndo(); + centreCircuit(); + } + if (item == "stackAll") + stackAll(); + if (item == "unstackAll") + unstackAll(); + if (item == "combineAll") + combineAll(); + if (item == "separateAll") + separateAll(); + if (item == "zoomin") + zoomCircuit(20, true); + if (item == "zoomout") + zoomCircuit(-20, true); + if (item == "zoom100") + setCircuitScale(1, true); + if (menu == "elm" && item == "edit") + doEdit(menuElm); + if (item == "delete") { + if (menu != "elm") + menuElm = null; + pushUndo(); + doDelete(true); + } + if (item == "sliders") + doSliders(menuElm); + + if (item == "viewInScope" && menuElm != null) { + int i; + for (i = 0; i != scopeCount; i++) + if (scopes[i].getElm() == null) + break; + if (i == scopeCount) { + if (scopeCount == scopes.length) + return; + scopeCount++; + scopes[i] = new Scope(this); + scopes[i].position = i; + //handleResize(); + } + scopes[i].setElm(menuElm); + if (i > 0) + scopes[i].speed = scopes[i - 1].speed; + } - if (item=="viewInFloatScope" && menuElm != null) { - ScopeElm newScope = new ScopeElm(snapGrid(menuElm.x+50), snapGrid(menuElm.y+50)); - elmList.addElement(newScope); - newScope.setScopeElm(menuElm); + if (item == "viewInFloatScope" && menuElm != null) { + ScopeElm newScope = new ScopeElm(snapGrid(menuElm.x + 50), snapGrid(menuElm.y + 50)); + elmList.addElement(newScope); + newScope.setScopeElm(menuElm); - // need to rebuild scopeElmArr - needAnalyze(); - } + // need to rebuild scopeElmArr + needAnalyze(); + } - if (item.startsWith("addToScope") && menuElm != null) { - int n; - n = Integer.parseInt(item.substring(10)); - if (n < scopeCount + countScopeElms()) { - if (n < scopeCount ) - scopes[n].addElm(menuElm); - else - getNthScopeElm(n-scopeCount).elmScope.addElm(menuElm); - } - scopeMenuSelected = -1; - } + if (item.startsWith("addToScope") && menuElm != null) { + int n; + n = Integer.parseInt(item.substring(10)); + if (n < scopeCount + countScopeElms()) { + if (n < scopeCount) + scopes[n].addElm(menuElm); + else + getNthScopeElm(n - scopeCount).elmScope.addElm(menuElm); + } + scopeMenuSelected = -1; + } - if (menu=="scopepop") { - pushUndo(); - Scope s; - if (menuScope != -1 ) - s= scopes[menuScope]; - else - s= ((ScopeElm)mouseElm).elmScope; - - if (item=="dock") { - if (scopeCount == scopes.length) - return; - scopes[scopeCount] = ((ScopeElm)mouseElm).elmScope; - ((ScopeElm)mouseElm).clearElmScope(); - scopes[scopeCount].position = scopeCount; - scopeCount++; - doDelete(false); - } - if (item=="undock") { - ScopeElm newScope = new ScopeElm(snapGrid(menuElm.x+50), snapGrid(menuElm.y+50)); - elmList.addElement(newScope); - newScope.setElmScope(scopes[menuScope]); - - int i; - // remove scope from list. setupScopes() will fix the positions - for (i = menuScope; i < scopeCount; i++) - scopes[i] = scopes[i+1]; - scopeCount--; - - needAnalyze(); // need to rebuild scopeElmArr - } - if (item=="remove") - s.setElm(null); // setupScopes() will clean this up - if (item=="removeplot") - s.removePlot(menuPlot); - if (item=="speed2") - s.speedUp(); - if (item=="speed1/2") - s.slowDown(); + if (menu == "scopepop") { + pushUndo(); + Scope s; + if (menuScope != -1) + s = scopes[menuScope]; + else + s = ((ScopeElm) mouseElm).elmScope; + + if (item == "dock") { + if (scopeCount == scopes.length) + return; + scopes[scopeCount] = ((ScopeElm) mouseElm).elmScope; + ((ScopeElm) mouseElm).clearElmScope(); + scopes[scopeCount].position = scopeCount; + scopeCount++; + doDelete(false); + } + if (item == "undock") { + ScopeElm newScope = new ScopeElm(snapGrid(menuElm.x + 50), snapGrid(menuElm.y + 50)); + elmList.addElement(newScope); + newScope.setElmScope(scopes[menuScope]); + + int i; + // remove scope from list. setupScopes() will fix the positions + for (i = menuScope; i < scopeCount; i++) + scopes[i] = scopes[i + 1]; + scopeCount--; + + needAnalyze(); // need to rebuild scopeElmArr + } + if (item == "remove") + s.setElm(null); // setupScopes() will clean this up + if (item == "removeplot") + s.removePlot(menuPlot); + if (item == "speed2") + s.speedUp(); + if (item == "speed1/2") + s.slowDown(); // if (item=="scale") // scopes[menuScope].adjustScale(.5); - if (item=="maxscale") - s.maxScale(); - if (item=="stack") - stackScope(menuScope); - if (item=="unstack") - unstackScope(menuScope); - if (item=="combine") - combineScope(menuScope); - if (item=="selecty") - s.selectY(); - if (item=="reset") - s.resetGraph(true); - if (item=="properties") - s.properties(); - deleteUnusedScopeElms(); - } - if (menu=="circuits" && item.indexOf("setup ") ==0) { - pushUndo(); - int sp = item.indexOf(' ', 6); - readSetupFile(item.substring(6, sp), item.substring(sp+1)); - } - if (item=="newblankcircuit") { - pushUndo(); - readSetupFile("blank.txt", "Blank Circuit"); - } + if (item == "maxscale") + s.maxScale(); + if (item == "stack") + stackScope(menuScope); + if (item == "unstack") + unstackScope(menuScope); + if (item == "combine") + combineScope(menuScope); + if (item == "selecty") + s.selectY(); + if (item == "reset") + s.resetGraph(true); + if (item == "properties") + s.properties(); + deleteUnusedScopeElms(); + } + if (menu == "circuits" && item.indexOf("setup ") == 0) { + pushUndo(); + int sp = item.indexOf(' ', 6); + readSetupFile(item.substring(6, sp), item.substring(sp + 1)); + } + if (item == "newblankcircuit") { + pushUndo(); + readSetupFile("blank.txt", "Blank Circuit"); + } - // if (ac.indexOf("setup ") == 0) { - // pushUndo(); - // readSetupFile(ac.substring(6), - // ((MenuItem) e.getSource()).getLabel()); - // } - - // IES: Moved from itemStateChanged() - if (menu=="main") { - if (contextPanel!=null) - contextPanel.hide(); - // MenuItem mmi = (MenuItem) mi; - // int prevMouseMode = mouseMode; - setMouseMode(MODE_ADD_ELM); - String s = item; - if (s.length() > 0) - mouseModeStr = s; - if (s.compareTo("DragAll") == 0) - setMouseMode(MODE_DRAG_ALL); - else if (s.compareTo("DragRow") == 0) - setMouseMode(MODE_DRAG_ROW); - else if (s.compareTo("DragColumn") == 0) - setMouseMode(MODE_DRAG_COLUMN); - else if (s.compareTo("DragSelected") == 0) - setMouseMode(MODE_DRAG_SELECTED); - else if (s.compareTo("DragPost") == 0) - setMouseMode(MODE_DRAG_POST); - else if (s.compareTo("Select") == 0) - setMouseMode(MODE_SELECT); - // else if (s.length() > 0) { - // try { - // addingClass = Class.forName(s); - // } catch (Exception ee) { - // ee.printStackTrace(); - // } - // } - // else - // setMouseMode(prevMouseMode); - tempMouseMode = mouseMode; - } - if (item=="fullscreen") { - if (! Graphics.isFullScreen){ - Graphics.viewFullScreen(); - setSlidersPanelHeight(); - } - else{ - Graphics.exitFullScreen(); - centreCircuit(); - setSlidersPanelHeight(); - } - } + // if (ac.indexOf("setup ") == 0) { + // pushUndo(); + // readSetupFile(ac.substring(6), + // ((MenuItem) e.getSource()).getLabel()); + // } + + // IES: Moved from itemStateChanged() + if (menu == "main") { + if (contextPanel != null) + contextPanel.hide(); + // MenuItem mmi = (MenuItem) mi; + // int prevMouseMode = mouseMode; + setMouseMode(MODE_ADD_ELM); + String s = item; + if (s.length() > 0) + mouseModeStr = s; + if (s.compareTo("DragAll") == 0) + setMouseMode(MODE_DRAG_ALL); + else if (s.compareTo("DragRow") == 0) + setMouseMode(MODE_DRAG_ROW); + else if (s.compareTo("DragColumn") == 0) + setMouseMode(MODE_DRAG_COLUMN); + else if (s.compareTo("DragSelected") == 0) + setMouseMode(MODE_DRAG_SELECTED); + else if (s.compareTo("DragPost") == 0) + setMouseMode(MODE_DRAG_POST); + else if (s.compareTo("Select") == 0) + setMouseMode(MODE_SELECT); + // else if (s.length() > 0) { + // try { + // addingClass = Class.forName(s); + // } catch (Exception ee) { + // ee.printStackTrace(); + // } + // } + // else + // setMouseMode(prevMouseMode); + tempMouseMode = mouseMode; + } + if (item == "fullscreen") { + if (!Graphics.isFullScreen) { + Graphics.viewFullScreen(); + setSlidersPanelHeight(); + } else { + Graphics.exitFullScreen(); + centreCircuit(); + setSlidersPanelHeight(); + } + } - repaint(); + repaint(); } int countScopeElms() { - int c = 0; - for (int i = 0; i != elmList.size(); i++) { - if ( elmList.get(i) instanceof ScopeElm) - c++; - } - return c; + int c = 0; + for (int i = 0; i != elmList.size(); i++) { + if (elmList.get(i) instanceof ScopeElm) + c++; + } + return c; } ScopeElm getNthScopeElm(int n) { - for (int i = 0; i != elmList.size(); i++) { - if ( elmList.get(i) instanceof ScopeElm) { - n--; - if (n<0) - return (ScopeElm) elmList.get(i); - } - } - return (ScopeElm) null; + for (int i = 0; i != elmList.size(); i++) { + if (elmList.get(i) instanceof ScopeElm) { + n--; + if (n < 0) + return (ScopeElm) elmList.get(i); + } + } + return (ScopeElm) null; } boolean canStackScope(int s) { - if (scopeCount < 2) - return false; - if (s==0) - s=1; - if (scopes[s].position == scopes[s-1].position) - return false; - return true; + if (scopeCount < 2) + return false; + if (s == 0) + s = 1; + if (scopes[s].position == scopes[s - 1].position) + return false; + return true; } boolean canCombineScope(int s) { - return scopeCount >=2; + return scopeCount >= 2; } boolean canUnstackScope(int s) { - if (scopeCount < 2) - return false; - if (s==0) - s=1; - if (scopes[s].position != scopes[s-1].position) { - if ( s + 1 < scopeCount && scopes[s+1].position == scopes[s].position) // Allow you to unstack by selecting the top scope in the stack - return true; - else - return false; - } - return true; + if (scopeCount < 2) + return false; + if (s == 0) + s = 1; + if (scopes[s].position != scopes[s - 1].position) { + if (s + 1 < scopeCount && scopes[s + 1].position == scopes[s].position) // Allow you to unstack by selecting the top scope in the stack + return true; + else + return false; + } + return true; } void stackScope(int s) { - if (! canStackScope(s) ) - return; - if (s == 0) { - s = 1; - } - scopes[s].position = scopes[s-1].position; - for (s++; s < scopeCount; s++) - scopes[s].position--; + if (!canStackScope(s)) + return; + if (s == 0) { + s = 1; + } + scopes[s].position = scopes[s - 1].position; + for (s++; s < scopeCount; s++) + scopes[s].position--; } void unstackScope(int s) { - if (! canUnstackScope(s) ) - return; - if (s == 0) { - s = 1; - } - if (scopes[s].position != scopes[s-1].position) // Allow you to unstack by selecting the top scope in the stack - s++; - for (; s < scopeCount; s++) - scopes[s].position++; + if (!canUnstackScope(s)) + return; + if (s == 0) { + s = 1; + } + if (scopes[s].position != scopes[s - 1].position) // Allow you to unstack by selecting the top scope in the stack + s++; + for (; s < scopeCount; s++) + scopes[s].position++; } void combineScope(int s) { - if (! canCombineScope(s)) - return; - if (s == 0) { - s = 1; - } - scopes[s-1].combine(scopes[s]); - scopes[s].setElm(null); + if (!canCombineScope(s)) + return; + if (s == 0) { + s = 1; + } + scopes[s - 1].combine(scopes[s]); + scopes[s].setElm(null); } void stackAll() { - int i; - for (i = 0; i != scopeCount; i++) { - scopes[i].position = 0; - scopes[i].showMax = scopes[i].showMin = false; - } + int i; + for (i = 0; i != scopeCount; i++) { + scopes[i].position = 0; + scopes[i].showMax = scopes[i].showMin = false; + } } void unstackAll() { - int i; - for (i = 0; i != scopeCount; i++) { - scopes[i].position = i; - scopes[i].showMax = true; - } + int i; + for (i = 0; i != scopeCount; i++) { + scopes[i].position = i; + scopes[i].showMax = true; + } } void combineAll() { - int i; - for (i = scopeCount-2; i >= 0; i--) { - scopes[i].combine(scopes[i+1]); - scopes[i+1].setElm(null); - } + int i; + for (i = scopeCount - 2; i >= 0; i--) { + scopes[i].combine(scopes[i + 1]); + scopes[i + 1].setElm(null); + } } void separateAll() { - int i; - Scope newscopes[] = new Scope[20]; - int ct = 0; - for (i = 0; i < scopeCount; i++) - ct = scopes[i].separate(newscopes, ct); - scopes = newscopes; - scopeCount = ct; + int i; + Scope newscopes[] = new Scope[20]; + int ct = 0; + for (i = 0; i < scopeCount; i++) + ct = scopes[i].separate(newscopes, ct); + scopes = newscopes; + scopeCount = ct; } void doEdit(Editable eable) { - clearSelection(); - pushUndo(); - if (editDialog != null) { - // requestFocus(); - editDialog.setVisible(false); - editDialog = null; - } - editDialog = new EditDialog(eable, this); - editDialog.show(); + clearSelection(); + pushUndo(); + if (editDialog != null) { + // requestFocus(); + editDialog.setVisible(false); + editDialog = null; + } + editDialog = new EditDialog(eable, this); + editDialog.show(); } void doSliders(CircuitElm ce) { - clearSelection(); - pushUndo(); - dialogShowing = new SliderDialog(ce, this); - dialogShowing.show(); + clearSelection(); + pushUndo(); + dialogShowing = new SliderDialog(ce, this); + dialogShowing.show(); } - void doExportAsUrl() - { - String dump = dumpCircuit(); - dialogShowing = new ExportAsUrlDialog(dump); - dialogShowing.show(); + void doExportAsUrl() { + String dump = dumpCircuit(); + dialogShowing = new ExportAsUrlDialog(dump); + dialogShowing.show(); } - void doExportAsText() - { - String dump = dumpCircuit(); - dialogShowing = new ExportAsTextDialog(this, dump); - dialogShowing.show(); + void doExportAsText() { + String dump = dumpCircuit(); + dialogShowing = new ExportAsTextDialog(this, dump); + dialogShowing.show(); } - void doExportAsImage() - { - dialogShowing = new ExportAsImageDialog(CAC_IMAGE); - dialogShowing.show(); + void doExportAsImage() { + dialogShowing = new ExportAsImageDialog(CAC_IMAGE); + dialogShowing.show(); } - void doCreateSubcircuit() - { - EditCompositeModelDialog dlg = new EditCompositeModelDialog(); - if (!dlg.createModel()) - return; - dlg.createDialog(); - dialogShowing = dlg; - dialogShowing.show(); + void doCreateSubcircuit() { + EditCompositeModelDialog dlg = new EditCompositeModelDialog(); + if (!dlg.createModel()) + return; + dlg.createDialog(); + dialogShowing = dlg; + dialogShowing.show(); } void doExportAsLocalFile() { - String dump = dumpCircuit(); - dialogShowing = new ExportAsLocalFileDialog(dump); - dialogShowing.show(); + String dump = dumpCircuit(); + dialogShowing = new ExportAsLocalFileDialog(dump); + dialogShowing.show(); } public void importCircuitFromText(String circuitText, boolean subcircuitsOnly) { - int flags = subcircuitsOnly ? (CirSim.RC_SUBCIRCUITS | CirSim.RC_RETAIN) : 0; - if (circuitText != null) { - readCircuit(circuitText, flags); - allowSave(false); - } + int flags = subcircuitsOnly ? (CirSim.RC_SUBCIRCUITS | CirSim.RC_RETAIN) : 0; + if (circuitText != null) { + readCircuit(circuitText, flags); + allowSave(false); + } } String dumpOptions() { - int f = (dotsCheckItem.getState()) ? 1 : 0; - f |= (smallGridCheckItem.getState()) ? 2 : 0; - f |= (voltsCheckItem.getState()) ? 0 : 4; - f |= (powerCheckItem.getState()) ? 8 : 0; - f |= (showValuesCheckItem.getState()) ? 0 : 16; - // 32 = linear scale in afilter - f |= adjustTimeStep ? 64 : 0; - String dump = "$ " + f + " " + - maxTimeStep + " " + getIterCount() + " " + - currentBar.getValue() + " " + CircuitElm.voltageRange + " " + - powerBar.getValue() + " " + minTimeStep + "\n"; - return dump; + int f = (dotsCheckItem.getState()) ? 1 : 0; + f |= (smallGridCheckItem.getState()) ? 2 : 0; + f |= (voltsCheckItem.getState()) ? 0 : 4; + f |= (powerCheckItem.getState()) ? 8 : 0; + f |= (showValuesCheckItem.getState()) ? 0 : 16; + // 32 = linear scale in afilter + f |= adjustTimeStep ? 64 : 0; + String dump = "$ " + f + " " + + maxTimeStep + " " + getIterCount() + " " + + currentBar.getValue() + " " + CircuitElm.voltageRange + " " + + powerBar.getValue() + " " + minTimeStep + "\n"; + return dump; } String dumpCircuit() { - int i; - CustomLogicModel.clearDumpedFlags(); - CustomCompositeModel.clearDumpedFlags(); - DiodeModel.clearDumpedFlags(); - TransistorModel.clearDumpedFlags(); - - String dump = dumpOptions(); - - for (i = 0; i != elmList.size(); i++) { - CircuitElm ce = getElm(i); - String m = ce.dumpModel(); - if (m != null && !m.isEmpty()) - dump += m + "\n"; - dump += ce.dump() + "\n"; - } - for (i = 0; i != scopeCount; i++) { - String d = scopes[i].dump(); - if (d != null) - dump += d + "\n"; - } - for (i = 0; i != adjustables.size(); i++) { - Adjustable adj = adjustables.get(i); - dump += "38 " + adj.dump() + "\n"; - } - if (hintType != -1) - dump += "h " + hintType + " " + hintItem1 + " " + - hintItem2 + "\n"; - return dump; + int i; + CustomLogicModel.clearDumpedFlags(); + CustomCompositeModel.clearDumpedFlags(); + DiodeModel.clearDumpedFlags(); + TransistorModel.clearDumpedFlags(); + + String dump = dumpOptions(); + + for (i = 0; i != elmList.size(); i++) { + CircuitElm ce = getElm(i); + String m = ce.dumpModel(); + if (m != null && !m.isEmpty()) + dump += m + "\n"; + dump += ce.dump() + "\n"; + } + for (i = 0; i != scopeCount; i++) { + String d = scopes[i].dump(); + if (d != null) + dump += d + "\n"; + } + for (i = 0; i != adjustables.size(); i++) { + Adjustable adj = adjustables.get(i); + dump += "38 " + adj.dump() + "\n"; + } + if (hintType != -1) + dump += "h " + hintType + " " + hintItem1 + " " + + hintItem2 + "\n"; + return dump; } void getSetupList(final boolean openDefault) { - String url; - url = GWT.getModuleBaseURL()+"setuplist.txt"+"?v="+random.nextInt(); - RequestBuilder requestBuilder = new RequestBuilder(RequestBuilder.GET, url); - try { - requestBuilder.sendRequest(null, new RequestCallback() { - public void onError(Request request, Throwable exception) { - Window.alert(Locale.LS("Can't load circuit list!")); - GWT.log("File Error Response", exception); - } + String url; + url = GWT.getModuleBaseURL() + "setuplist.txt" + "?v=" + random.nextInt(); + RequestBuilder requestBuilder = new RequestBuilder(RequestBuilder.GET, url); + try { + requestBuilder.sendRequest(null, new RequestCallback() { + public void onError(Request request, Throwable exception) { + Window.alert(Locale.LS("Can't load circuit list!")); + GWT.log("File Error Response", exception); + } - public void onResponseReceived(Request request, Response response) { - // processing goes here - if (response.getStatusCode()==Response.SC_OK) { - String text = response.getText(); - processSetupList(text, openDefault); - // end or processing - } - else { - Window.alert(Locale.LS("Can't load circuit list!")); - GWT.log("Bad file server response:"+response.getStatusText() ); - } - } - }); - } catch (RequestException e) { - GWT.log("failed file reading", e); - } - } - - void processSetupList(String text, final boolean openDefault) { - - MenuBar currentMenuBar; - MenuBar stack[] = new MenuBar[6]; - int stackptr = 0; - currentMenuBar=new MenuBar(true); - currentMenuBar.setAutoOpen(true); - menuBar.addItem(Locale.LS("Circuits"), currentMenuBar); - - MenuBar h = new MenuBar(true); - helpItem=iconMenuItem("book-open", "User Guide", (Command)null); - h.addItem(helpItem); - helpItem.setScheduledCommand(new MyCommand("file","help")); - licenseItem=iconMenuItem("license", "License",(Command)null); - h.addItem(licenseItem); - licenseItem.setScheduledCommand(new MyCommand("file","license")); - aboutItem = iconMenuItem("info-circled", "About...", (Command)null); - h.addItem(aboutItem); - aboutItem.setScheduledCommand(new MyCommand("file","about")); - h.addSeparator(); - h.addItem(aboutCircuitsItem = iconMenuItem("link", "About Circuits", - new Command() { public void execute(){ - ScriptInjector.fromString("nw.Shell.openExternal('https://www.falstad.com/circuit/e-index.html');") - .setRemoveTag(false) - .setWindow(ScriptInjector.TOP_WINDOW) - .inject(); - } - })); - h.addItem(aboutCircuitsPLItem = iconMenuItem("link", "About Circuits (Polish ver.)", - new Command() { public void execute(){ - ScriptInjector.fromString("nw.Shell.openExternal('https://www.falstad.com/circuit/polish/e-index.html');") - .setRemoveTag(false) - .setWindow(ScriptInjector.TOP_WINDOW) - .inject(); - } - })); + public void onResponseReceived(Request request, Response response) { + // processing goes here + if (response.getStatusCode() == Response.SC_OK) { + String text = response.getText(); + processSetupList(text, openDefault); + // end or processing + } else { + Window.alert(Locale.LS("Can't load circuit list!")); + GWT.log("Bad file server response:" + response.getStatusText()); + } + } + }); + } catch (RequestException e) { + GWT.log("failed file reading", e); + } + } + + void processSetupList(String text, final boolean openDefault) { + + MenuBar currentMenuBar; + MenuBar stack[] = new MenuBar[6]; + int stackptr = 0; + currentMenuBar = new MenuBar(true); + currentMenuBar.setAutoOpen(true); + menuBar.addItem(Locale.LS("Circuits"), currentMenuBar); + + MenuBar h = new MenuBar(true); + helpItem = iconMenuItem("book-open", "User Guide", (Command) null); + h.addItem(helpItem); + helpItem.setScheduledCommand(new MyCommand("file", "help")); + licenseItem = iconMenuItem("license", "License", (Command) null); + h.addItem(licenseItem); + licenseItem.setScheduledCommand(new MyCommand("file", "license")); + aboutItem = iconMenuItem("info-circled", "About...", (Command) null); + h.addItem(aboutItem); + aboutItem.setScheduledCommand(new MyCommand("file", "about")); + h.addSeparator(); + h.addItem(aboutCircuitsItem = iconMenuItem("link", "About Circuits", + new Command() { + public void execute() { + ScriptInjector.fromString("nw.Shell.openExternal('https://www.falstad.com/circuit/e-index.html');") + .setRemoveTag(false) + .setWindow(ScriptInjector.TOP_WINDOW) + .inject(); + } + })); + h.addItem(aboutCircuitsPLItem = iconMenuItem("link", "About Circuits (Polish ver.)", + new Command() { + public void execute() { + ScriptInjector.fromString("nw.Shell.openExternal('https://www.falstad.com/circuit/polish/e-index.html');") + .setRemoveTag(false) + .setWindow(ScriptInjector.TOP_WINDOW) + .inject(); + } + })); - menuBar.addItem(Locale.LS("Help"), h); + menuBar.addItem(Locale.LS("Help"), h); - stack[stackptr++] = currentMenuBar; - String[] lines = text.split("\\r?\\n"); + stack[stackptr++] = currentMenuBar; + String[] lines = text.split("\\r?\\n"); for (String line : lines) { console(line); if (line.isEmpty()) { @@ -3959,66 +3979,65 @@ void processSetupList(String text, final boolean openDefault) { } } - } + } void readCircuit(String text, int flags) { - readCircuit(text.getBytes(), flags); - if ((flags & RC_KEEP_TITLE) == 0) - titleLabel.setText(null); - setSlidersPanelHeight(); + readCircuit(text.getBytes(), flags); + if ((flags & RC_KEEP_TITLE) == 0) + titleLabel.setText(null); + setSlidersPanelHeight(); } void readCircuit(String text) { - readCircuit(text.getBytes(), 0); - titleLabel.setText(null); - setSlidersPanelHeight(); + readCircuit(text.getBytes(), 0); + titleLabel.setText(null); + setSlidersPanelHeight(); } void setCircuitTitle(String s) { - if (s != null) - titleLabel.setText(s); - setSlidersPanelHeight(); - } - - void readSetupFile(String str, String title) { - System.out.println(str); - // TODO: Maybe think about some better approach to cache management! - String url=GWT.getModuleBaseURL()+"circuits/"+str+"?v="+random.nextInt(); - loadFileFromURL(url); - if (title != null) - titleLabel.setText(title); - setSlidersPanelHeight(); - unsavedChanges = false; - } + if (s != null) + titleLabel.setText(s); + setSlidersPanelHeight(); + } + + void readSetupFile(String str, String title) { + System.out.println(str); + // TODO: Maybe think about some better approach to cache management! + String url = GWT.getModuleBaseURL() + "circuits/" + str + "?v=" + random.nextInt(); + loadFileFromURL(url); + if (title != null) + titleLabel.setText(title); + setSlidersPanelHeight(); + unsavedChanges = false; + } + + void loadFileFromURL(String url) { + RequestBuilder requestBuilder = new RequestBuilder(RequestBuilder.GET, url); + + try { + requestBuilder.sendRequest(null, new RequestCallback() { + public void onError(Request request, Throwable exception) { + Window.alert(Locale.LS("Can't load circuit!")); + GWT.log("File Error Response", exception); + } - void loadFileFromURL(String url) { - RequestBuilder requestBuilder = new RequestBuilder(RequestBuilder.GET, url); - - try { - requestBuilder.sendRequest(null, new RequestCallback() { - public void onError(Request request, Throwable exception) { - Window.alert(Locale.LS("Can't load circuit!")); - GWT.log("File Error Response", exception); - } - - public void onResponseReceived(Request request, Response response) { - if (response.getStatusCode()==Response.SC_OK) { - String text = response.getText(); - readCircuit(text, RC_KEEP_TITLE); - allowSave(false); - unsavedChanges = false; - } - else { - Window.alert(Locale.LS("Can't load circuit!")); - GWT.log("Bad file server response:"+response.getStatusText() ); - } - } - }); - } catch (RequestException e) { - GWT.log("failed file reading", e); - } + public void onResponseReceived(Request request, Response response) { + if (response.getStatusCode() == Response.SC_OK) { + String text = response.getText(); + readCircuit(text, RC_KEEP_TITLE); + allowSave(false); + unsavedChanges = false; + } else { + Window.alert(Locale.LS("Can't load circuit!")); + GWT.log("Bad file server response:" + response.getStatusText()); + } + } + }); + } catch (RequestException e) { + GWT.log("failed file reading", e); + } - } + } static final int RC_RETAIN = 1; static final int RC_NO_CENTER = 2; @@ -4026,111 +4045,111 @@ public void onResponseReceived(Request request, Response response) { static final int RC_KEEP_TITLE = 8; void readCircuit(byte b[], int flags) { - int i; - int len = b.length; - if ((flags & RC_RETAIN) == 0) { - clearMouseElm(); - for (i = 0; i != elmList.size(); i++) { - CircuitElm ce = getElm(i); - ce.delete(); - } - t = timeStepAccum = 0; - elmList.removeAllElements(); - hintType = -1; - maxTimeStep = 5e-6; - minTimeStep = 50e-12; - dotsCheckItem.setState(false); - smallGridCheckItem.setState(false); - powerCheckItem.setState(false); - voltsCheckItem.setState(true); - showValuesCheckItem.setState(true); - setGrid(); - speedBar.setValue(117); // 57 - currentBar.setValue(50); - powerBar.setValue(50); - CircuitElm.voltageRange = 5; - scopeCount = 0; - lastIterTime = 0; - } - boolean subs = (flags & RC_SUBCIRCUITS) != 0; - //cv.repaint(); - int p; - for (p = 0; p < len; ) { - int l; - int linelen = len-p; // IES - changed to allow the last line to not end with a delim. - for (l = 0; l != len-p; l++) - if (b[l+p] == '\n' || b[l+p] == '\r') { - linelen = l++; - if (l+p < b.length && b[l+p] == '\n') - l++; - break; - } - String line = new String(b, p, linelen); - StringTokenizer st = new StringTokenizer(line, " +\t\n\r\f"); - while (st.hasMoreTokens()) { - String type = st.nextToken(); - int tint = type.charAt(0); - try { - if (subs && tint != '.') - continue; - if (tint == 'o') { - Scope sc = new Scope(this); - sc.position = scopeCount; - sc.undump(st); - scopes[scopeCount++] = sc; - break; - } - if (tint == 'h') { - readHint(st); - break; - } - if (tint == '$') { - readOptions(st, flags); - break; - } - if (tint == '!') { - CustomLogicModel.undumpModel(st); - break; - } - if (tint == '%' || tint == '?' || tint == 'B') { - // ignore afilter-specific stuff - break; - } - // do not add new symbols here without testing export as link - - // if first character is a digit then parse the type as a number - if (tint >= '0' && tint <= '9') - tint = new Integer(type).intValue(); - - if (tint == 34) { - DiodeModel.undumpModel(st); - break; - } - if (tint == 32) { - TransistorModel.undumpModel(st); - break; - } - if (tint == 38) { - Adjustable adj = new Adjustable(st, this); - if (adj.elm != null) - adjustables.add(adj); - break; - } - if (tint == '.') { - CustomCompositeModel.undumpModel(st); - break; - } - int x1 = new Integer(st.nextToken()).intValue(); - int y1 = new Integer(st.nextToken()).intValue(); - int x2 = new Integer(st.nextToken()).intValue(); - int y2 = new Integer(st.nextToken()).intValue(); - int f = new Integer(st.nextToken()).intValue(); - - CircuitElm newce = createCe(tint, x1, y1, x2, y2, f, st); - if (newce==null) { - System.out.println("unrecognized dump type: " + type); - break; - } + int i; + int len = b.length; + if ((flags & RC_RETAIN) == 0) { + clearMouseElm(); + for (i = 0; i != elmList.size(); i++) { + CircuitElm ce = getElm(i); + ce.delete(); + } + t = timeStepAccum = 0; + elmList.removeAllElements(); + hintType = -1; + maxTimeStep = 5e-6; + minTimeStep = 50e-12; + dotsCheckItem.setState(false); + smallGridCheckItem.setState(false); + powerCheckItem.setState(false); + voltsCheckItem.setState(true); + showValuesCheckItem.setState(true); + setGrid(); + speedBar.setValue(117); // 57 + currentBar.setValue(50); + powerBar.setValue(50); + CircuitElm.voltageRange = 5; + scopeCount = 0; + lastIterTime = 0; + } + boolean subs = (flags & RC_SUBCIRCUITS) != 0; + //cv.repaint(); + int p; + for (p = 0; p < len; ) { + int l; + int linelen = len - p; // IES - changed to allow the last line to not end with a delim. + for (l = 0; l != len - p; l++) + if (b[l + p] == '\n' || b[l + p] == '\r') { + linelen = l++; + if (l + p < b.length && b[l + p] == '\n') + l++; + break; + } + String line = new String(b, p, linelen); + StringTokenizer st = new StringTokenizer(line, " +\t\n\r\f"); + while (st.hasMoreTokens()) { + String type = st.nextToken(); + int tint = type.charAt(0); + try { + if (subs && tint != '.') + continue; + if (tint == 'o') { + Scope sc = new Scope(this); + sc.position = scopeCount; + sc.undump(st); + scopes[scopeCount++] = sc; + break; + } + if (tint == 'h') { + readHint(st); + break; + } + if (tint == '$') { + readOptions(st, flags); + break; + } + if (tint == '!') { + CustomLogicModel.undumpModel(st); + break; + } + if (tint == '%' || tint == '?' || tint == 'B') { + // ignore afilter-specific stuff + break; + } + // do not add new symbols here without testing export as link + + // if first character is a digit then parse the type as a number + if (tint >= '0' && tint <= '9') + tint = new Integer(type).intValue(); + + if (tint == 34) { + DiodeModel.undumpModel(st); + break; + } + if (tint == 32) { + TransistorModel.undumpModel(st); + break; + } + if (tint == 38) { + Adjustable adj = new Adjustable(st, this); + if (adj.elm != null) + adjustables.add(adj); + break; + } + if (tint == '.') { + CustomCompositeModel.undumpModel(st); + break; + } + int x1 = new Integer(st.nextToken()).intValue(); + int y1 = new Integer(st.nextToken()).intValue(); + int x2 = new Integer(st.nextToken()).intValue(); + int y2 = new Integer(st.nextToken()).intValue(); + int f = new Integer(st.nextToken()).intValue(); + + CircuitElm newce = createCe(tint, x1, y1, x2, y2, f, st); + if (newce == null) { + System.out.println("unrecognized dump type: " + type); + break; + } /* * debug code to check if allocNodes() is called in constructor. It gets called in * setPoints() but that doesn't get called for subcircuits. @@ -4139,373 +4158,373 @@ void readCircuit(byte b[], int flags) { if (vv.length != vc) console("allocnodes not called! " + tint); */ - newce.setPoints(); - elmList.addElement(newce); - } catch (Exception ee) { - ee.printStackTrace(); - console("exception while undumping " + ee); - break; - } - break; - } - p += l; + newce.setPoints(); + elmList.addElement(newce); + } catch (Exception ee) { + ee.printStackTrace(); + console("exception while undumping " + ee); + break; + } + break; + } + p += l; - } - setPowerBarEnable(); - enableItems(); - if ((flags & RC_RETAIN) == 0) { - // create sliders as needed - for (i = 0; i < adjustables.size(); i++) { - if (!adjustables.get(i).createSlider(this)) - adjustables.remove(i--); - } - } + } + setPowerBarEnable(); + enableItems(); + if ((flags & RC_RETAIN) == 0) { + // create sliders as needed + for (i = 0; i < adjustables.size(); i++) { + if (!adjustables.get(i).createSlider(this)) + adjustables.remove(i--); + } + } // if (!retain) - // handleResize(); // for scopes - needAnalyze(); - if ((flags & RC_NO_CENTER) == 0) - centreCircuit(); - if ((flags & RC_SUBCIRCUITS) != 0) - updateModels(); + // handleResize(); // for scopes + needAnalyze(); + if ((flags & RC_NO_CENTER) == 0) + centreCircuit(); + if ((flags & RC_SUBCIRCUITS) != 0) + updateModels(); - AudioInputElm.clearCache(); // to save memory - DataInputElm.clearCache(); // to save memory + AudioInputElm.clearCache(); // to save memory + DataInputElm.clearCache(); // to save memory } // delete sliders for an element void deleteSliders(CircuitElm elm) { - int i; - if (adjustables == null) - return; - for (i = adjustables.size()-1; i >= 0; i--) { - Adjustable adj = adjustables.get(i); - if (adj.elm == elm) { - adj.deleteSlider(this); - adjustables.remove(i); - } - } + int i; + if (adjustables == null) + return; + for (i = adjustables.size() - 1; i >= 0; i--) { + Adjustable adj = adjustables.get(i); + if (adj.elm == elm) { + adj.deleteSlider(this); + adjustables.remove(i); + } + } } void readHint(StringTokenizer st) { - hintType = new Integer(st.nextToken()).intValue(); - hintItem1 = new Integer(st.nextToken()).intValue(); - hintItem2 = new Integer(st.nextToken()).intValue(); + hintType = new Integer(st.nextToken()).intValue(); + hintItem1 = new Integer(st.nextToken()).intValue(); + hintItem2 = new Integer(st.nextToken()).intValue(); } void readOptions(StringTokenizer st, int importFlags) { - int flags = new Integer(st.nextToken()).intValue(); + int flags = new Integer(st.nextToken()).intValue(); - if ((importFlags & RC_RETAIN) != 0) { + if ((importFlags & RC_RETAIN) != 0) { // need to set small grid if pasted circuit uses it - if ((flags & 2) != 0) - smallGridCheckItem.setState(true); - return; - } + if ((flags & 2) != 0) + smallGridCheckItem.setState(true); + return; + } - dotsCheckItem.setState((flags & 1) != 0); - smallGridCheckItem.setState((flags & 2) != 0); - voltsCheckItem.setState((flags & 4) == 0); - powerCheckItem.setState((flags & 8) == 8); - showValuesCheckItem.setState((flags & 16) == 0); - adjustTimeStep = (flags & 64) != 0; - maxTimeStep = timeStep = new Double (st.nextToken()).doubleValue(); - double sp = new Double(st.nextToken()).doubleValue(); - int sp2 = (int) (Math.log(10*sp)*24+61.5); - //int sp2 = (int) (Math.log(sp)*24+1.5); - speedBar.setValue(sp2); - currentBar.setValue(new Integer(st.nextToken()).intValue()); - CircuitElm.voltageRange = new Double (st.nextToken()).doubleValue(); - - try { - powerBar.setValue(new Integer(st.nextToken()).intValue()); - minTimeStep = Double.parseDouble(st.nextToken()); - } catch (Exception e) { - } - setGrid(); + dotsCheckItem.setState((flags & 1) != 0); + smallGridCheckItem.setState((flags & 2) != 0); + voltsCheckItem.setState((flags & 4) == 0); + powerCheckItem.setState((flags & 8) == 8); + showValuesCheckItem.setState((flags & 16) == 0); + adjustTimeStep = (flags & 64) != 0; + maxTimeStep = timeStep = new Double(st.nextToken()).doubleValue(); + double sp = new Double(st.nextToken()).doubleValue(); + int sp2 = (int) (Math.log(10 * sp) * 24 + 61.5); + //int sp2 = (int) (Math.log(sp)*24+1.5); + speedBar.setValue(sp2); + currentBar.setValue(new Integer(st.nextToken()).intValue()); + CircuitElm.voltageRange = new Double(st.nextToken()).doubleValue(); + + try { + powerBar.setValue(new Integer(st.nextToken()).intValue()); + minTimeStep = Double.parseDouble(st.nextToken()); + } catch (Exception e) { + } + setGrid(); } int snapGrid(int x) { - return (x+gridRound) & gridMask; - } - - boolean doSwitch(int x, int y) { - if (mouseElm == null || !(mouseElm instanceof SwitchElm)) - return false; - SwitchElm se = (SwitchElm) mouseElm; - if (!se.getSwitchRect().contains(x, y)) - return false; - se.toggle(); - if (se.momentary) - heldSwitchElm = se; - if (!(se instanceof LogicInputElm)) - needAnalyze(); - return true; - } + return (x + gridRound) & gridMask; + } + + boolean doSwitch(int x, int y) { + if (mouseElm == null || !(mouseElm instanceof SwitchElm)) + return false; + SwitchElm se = (SwitchElm) mouseElm; + if (!se.getSwitchRect().contains(x, y)) + return false; + se.toggle(); + if (se.momentary) + heldSwitchElm = se; + if (!(se instanceof LogicInputElm)) + needAnalyze(); + return true; + } int locateElm(CircuitElm elm) { - int i; - for (i = 0; i != elmList.size(); i++) - if (elm == elmList.elementAt(i)) - return i; - return -1; + int i; + for (i = 0; i != elmList.size(); i++) + if (elm == elmList.elementAt(i)) + return i; + return -1; } public void mouseDragged(MouseMoveEvent e) { - // ignore right mouse button with no modifiers (needed on PC) - if (e.getNativeButton()==NativeEvent.BUTTON_RIGHT) { - if (!(e.isMetaKeyDown() || - e.isShiftKeyDown() || - e.isControlKeyDown() || - e.isAltKeyDown())) - return; - } + // ignore right mouse button with no modifiers (needed on PC) + if (e.getNativeButton() == NativeEvent.BUTTON_RIGHT) { + if (!(e.isMetaKeyDown() || + e.isShiftKeyDown() || + e.isControlKeyDown() || + e.isAltKeyDown())) + return; + } - if (tempMouseMode==MODE_DRAG_SPLITTER) { - dragSplitter(e.getX(), e.getY()); - return; - } - int gx = inverseTransformX(e.getX()); - int gy = inverseTransformY(e.getY()); - if (!circuitArea.contains(e.getX(), e.getY())) - return; - boolean changed = false; - if (dragElm != null) - dragElm.drag(gx, gy); - boolean success = true; - switch (tempMouseMode) { - case MODE_DRAG_ALL: - dragAll(e.getX(), e.getY()); - break; - case MODE_DRAG_ROW: - dragRow(snapGrid(gx), snapGrid(gy)); - changed = true; - break; - case MODE_DRAG_COLUMN: - dragColumn(snapGrid(gx), snapGrid(gy)); - changed = true; - break; - case MODE_DRAG_POST: - if (mouseElm != null) { - dragPost(snapGrid(gx), snapGrid(gy), e.isShiftKeyDown()); - changed = true; - } - break; - case MODE_SELECT: - if (mouseElm == null) - selectArea(gx, gy, e.isShiftKeyDown()); - else if (!noEditCheckItem.getState()) { - // wait short delay before dragging. This is to fix problem where switches were accidentally getting - // dragged when tapped on mobile devices - if (System.currentTimeMillis()-mouseDownTime < 150) - return; - - tempMouseMode = MODE_DRAG_SELECTED; - changed = success = dragSelected(gx, gy); - } - break; - case MODE_DRAG_SELECTED: - changed = success = dragSelected(gx, gy); - break; + if (tempMouseMode == MODE_DRAG_SPLITTER) { + dragSplitter(e.getX(), e.getY()); + return; + } + int gx = inverseTransformX(e.getX()); + int gy = inverseTransformY(e.getY()); + if (!circuitArea.contains(e.getX(), e.getY())) + return; + boolean changed = false; + if (dragElm != null) + dragElm.drag(gx, gy); + boolean success = true; + switch (tempMouseMode) { + case MODE_DRAG_ALL: + dragAll(e.getX(), e.getY()); + break; + case MODE_DRAG_ROW: + dragRow(snapGrid(gx), snapGrid(gy)); + changed = true; + break; + case MODE_DRAG_COLUMN: + dragColumn(snapGrid(gx), snapGrid(gy)); + changed = true; + break; + case MODE_DRAG_POST: + if (mouseElm != null) { + dragPost(snapGrid(gx), snapGrid(gy), e.isShiftKeyDown()); + changed = true; + } + break; + case MODE_SELECT: + if (mouseElm == null) + selectArea(gx, gy, e.isShiftKeyDown()); + else if (!noEditCheckItem.getState()) { + // wait short delay before dragging. This is to fix problem where switches were accidentally getting + // dragged when tapped on mobile devices + if (System.currentTimeMillis() - mouseDownTime < 150) + return; + + tempMouseMode = MODE_DRAG_SELECTED; + changed = success = dragSelected(gx, gy); + } + break; + case MODE_DRAG_SELECTED: + changed = success = dragSelected(gx, gy); + break; - } - dragging = true; - if (success) { - dragScreenX = e.getX(); - dragScreenY = e.getY(); - // console("setting dragGridx in mousedragged"); - dragGridX = inverseTransformX(dragScreenX); - dragGridY = inverseTransformY(dragScreenY); - if (!(tempMouseMode == MODE_DRAG_SELECTED && onlyGraphicsElmsSelected())) { - dragGridX = snapGrid(dragGridX); - dragGridY = snapGrid(dragGridY); - } - } - if (changed) - writeRecoveryToStorage(); - repaint(); + } + dragging = true; + if (success) { + dragScreenX = e.getX(); + dragScreenY = e.getY(); + // console("setting dragGridx in mousedragged"); + dragGridX = inverseTransformX(dragScreenX); + dragGridY = inverseTransformY(dragScreenY); + if (!(tempMouseMode == MODE_DRAG_SELECTED && onlyGraphicsElmsSelected())) { + dragGridX = snapGrid(dragGridX); + dragGridY = snapGrid(dragGridY); + } + } + if (changed) + writeRecoveryToStorage(); + repaint(); } void dragSplitter(int x, int y) { - double h = (double) canvasHeight; - if (h<1) - h=1; - scopeHeightFraction=1.0-(((double)y)/h); - if (scopeHeightFraction<0.1) - scopeHeightFraction=0.1; - if (scopeHeightFraction>0.9) - scopeHeightFraction=0.9; - setCircuitArea(); - repaint(); + double h = (double) canvasHeight; + if (h < 1) + h = 1; + scopeHeightFraction = 1.0 - (((double) y) / h); + if (scopeHeightFraction < 0.1) + scopeHeightFraction = 0.1; + if (scopeHeightFraction > 0.9) + scopeHeightFraction = 0.9; + setCircuitArea(); + repaint(); } void dragAll(int x, int y) { - int dx = x-dragScreenX; - int dy = y-dragScreenY; - if (dx == 0 && dy == 0) - return; - transform[4] += dx; - transform[5] += dy; - dragScreenX = x; - dragScreenY = y; + int dx = x - dragScreenX; + int dy = y - dragScreenY; + if (dx == 0 && dy == 0) + return; + transform[4] += dx; + transform[5] += dy; + dragScreenX = x; + dragScreenY = y; } void dragRow(int x, int y) { - int dy = y-dragGridY; - if (dy == 0) - return; - int i; - for (i = 0; i != elmList.size(); i++) { - CircuitElm ce = getElm(i); - if (ce.y == dragGridY) - ce.movePoint(0, 0, dy); - if (ce.y2 == dragGridY) - ce.movePoint(1, 0, dy); - } - removeZeroLengthElements(); + int dy = y - dragGridY; + if (dy == 0) + return; + int i; + for (i = 0; i != elmList.size(); i++) { + CircuitElm ce = getElm(i); + if (ce.y == dragGridY) + ce.movePoint(0, 0, dy); + if (ce.y2 == dragGridY) + ce.movePoint(1, 0, dy); + } + removeZeroLengthElements(); } void dragColumn(int x, int y) { - int dx = x-dragGridX; - if (dx == 0) - return; - int i; - for (i = 0; i != elmList.size(); i++) { - CircuitElm ce = getElm(i); - if (ce.x == dragGridX) - ce.movePoint(0, dx, 0); - if (ce.x2 == dragGridX) - ce.movePoint(1, dx, 0); - } - removeZeroLengthElements(); + int dx = x - dragGridX; + if (dx == 0) + return; + int i; + for (i = 0; i != elmList.size(); i++) { + CircuitElm ce = getElm(i); + if (ce.x == dragGridX) + ce.movePoint(0, dx, 0); + if (ce.x2 == dragGridX) + ce.movePoint(1, dx, 0); + } + removeZeroLengthElements(); } boolean onlyGraphicsElmsSelected() { - if (mouseElm!=null && !(mouseElm instanceof GraphicElm)) - return false; - int i; - for (i = 0; i != elmList.size(); i++) { - CircuitElm ce = getElm(i); - if ( ce.isSelected() && !(ce instanceof GraphicElm) ) - return false; - } - return true; + if (mouseElm != null && !(mouseElm instanceof GraphicElm)) + return false; + int i; + for (i = 0; i != elmList.size(); i++) { + CircuitElm ce = getElm(i); + if (ce.isSelected() && !(ce instanceof GraphicElm)) + return false; + } + return true; } boolean dragSelected(int x, int y) { - boolean me = false; - int i; - if (mouseElm != null && !mouseElm.isSelected()) - mouseElm.setSelected(me = true); - - if (! onlyGraphicsElmsSelected()) { - // console("Snapping x and y"); - x = snapGrid(x); - y = snapGrid(y); - } + boolean me = false; + int i; + if (mouseElm != null && !mouseElm.isSelected()) + mouseElm.setSelected(me = true); - int dx = x-dragGridX; - // console("dx="+dx+"dragGridx="+dragGridX); - int dy = y-dragGridY; - if (dx == 0 && dy == 0) { - // don't leave mouseElm selected if we selected it above - if (me) - mouseElm.setSelected(false); - return false; - } - boolean allowed = true; + if (!onlyGraphicsElmsSelected()) { + // console("Snapping x and y"); + x = snapGrid(x); + y = snapGrid(y); + } - // check if moves are allowed - for (i = 0; allowed && i != elmList.size(); i++) { - CircuitElm ce = getElm(i); - if (ce.isSelected() && !ce.allowMove(dx, dy)) - allowed = false; - } + int dx = x - dragGridX; + // console("dx="+dx+"dragGridx="+dragGridX); + int dy = y - dragGridY; + if (dx == 0 && dy == 0) { + // don't leave mouseElm selected if we selected it above + if (me) + mouseElm.setSelected(false); + return false; + } + boolean allowed = true; - if (allowed) { - for (i = 0; i != elmList.size(); i++) { - CircuitElm ce = getElm(i); - if (ce.isSelected()) - ce.move(dx, dy); - } - needAnalyze(); - } + // check if moves are allowed + for (i = 0; allowed && i != elmList.size(); i++) { + CircuitElm ce = getElm(i); + if (ce.isSelected() && !ce.allowMove(dx, dy)) + allowed = false; + } - // don't leave mouseElm selected if we selected it above - if (me) - mouseElm.setSelected(false); + if (allowed) { + for (i = 0; i != elmList.size(); i++) { + CircuitElm ce = getElm(i); + if (ce.isSelected()) + ce.move(dx, dy); + } + needAnalyze(); + } - return allowed; + // don't leave mouseElm selected if we selected it above + if (me) + mouseElm.setSelected(false); + + return allowed; } void dragPost(int x, int y, boolean all) { - if (draggingPost == -1) { - draggingPost = - (Graphics.distanceSq(mouseElm.x , mouseElm.y , x, y) > - Graphics.distanceSq(mouseElm.x2, mouseElm.y2, x, y)) ? 1 : 0; - } - int dx = x-dragGridX; - int dy = y-dragGridY; - if (dx == 0 && dy == 0) - return; - - if (all) { - // go through all elms - int i; - for (i = 0; i != elmList.size(); i++) { - CircuitElm e = elmList.get(i); - - // which post do we move? - int p = 0; - if (e.x == dragGridX && e.y == dragGridY) - p = 0; - else if (e.x2 == dragGridX && e.y2 == dragGridY) - p = 1; - else - continue; - e.movePoint(p, dx, dy); - } - } else - mouseElm.movePoint(draggingPost, dx, dy); - needAnalyze(); + if (draggingPost == -1) { + draggingPost = + (Graphics.distanceSq(mouseElm.x, mouseElm.y, x, y) > + Graphics.distanceSq(mouseElm.x2, mouseElm.y2, x, y)) ? 1 : 0; + } + int dx = x - dragGridX; + int dy = y - dragGridY; + if (dx == 0 && dy == 0) + return; + + if (all) { + // go through all elms + int i; + for (i = 0; i != elmList.size(); i++) { + CircuitElm e = elmList.get(i); + + // which post do we move? + int p = 0; + if (e.x == dragGridX && e.y == dragGridY) + p = 0; + else if (e.x2 == dragGridX && e.y2 == dragGridY) + p = 1; + else + continue; + e.movePoint(p, dx, dy); + } + } else + mouseElm.movePoint(draggingPost, dx, dy); + needAnalyze(); } void doFlip() { - menuElm.flipPosts(); - needAnalyze(); + menuElm.flipPosts(); + needAnalyze(); } void doSplit(CircuitElm ce) { - int x = snapGrid(inverseTransformX(menuX)); - int y = snapGrid(inverseTransformY(menuY)); - if (ce == null || !(ce instanceof WireElm)) - return; - if (ce.x == ce.x2) - x = ce.x; - else - y = ce.y; - - // don't create zero-length wire - if (x == ce.x && y == ce.y || x == ce.x2 && y == ce.y2) - return; - - WireElm newWire = new WireElm(x, y); - newWire.drag(ce.x2, ce.y2); - ce.drag(x, y); - elmList.addElement(newWire); - needAnalyze(); + int x = snapGrid(inverseTransformX(menuX)); + int y = snapGrid(inverseTransformY(menuY)); + if (ce == null || !(ce instanceof WireElm)) + return; + if (ce.x == ce.x2) + x = ce.x; + else + y = ce.y; + + // don't create zero-length wire + if (x == ce.x && y == ce.y || x == ce.x2 && y == ce.y2) + return; + + WireElm newWire = new WireElm(x, y); + newWire.drag(ce.x2, ce.y2); + ce.drag(x, y); + elmList.addElement(newWire); + needAnalyze(); } void selectArea(int x, int y, boolean add) { - int x1 = min(x, initDragGridX); - int x2 = max(x, initDragGridX); - int y1 = min(y, initDragGridY); - int y2 = max(y, initDragGridY); - selectedArea = new Rectangle(x1, y1, x2-x1, y2-y1); - int i; - for (i = 0; i != elmList.size(); i++) { - CircuitElm ce = getElm(i); - ce.selectRect(selectedArea, add); - } + int x1 = min(x, initDragGridX); + int x2 = max(x, initDragGridX); + int y1 = min(y, initDragGridY); + int y2 = max(y, initDragGridY); + selectedArea = new Rectangle(x1, y1, x2 - x1, y2 - y1); + int i; + for (i = 0; i != elmList.size(); i++) { + CircuitElm ce = getElm(i); + ce.selectRect(selectedArea, add); + } } // void setSelectedElm(CircuitElm cs) { @@ -4518,312 +4537,308 @@ void selectArea(int x, int y, boolean add) { // } void setMouseElm(CircuitElm ce) { - if (ce!=mouseElm) { - if (mouseElm!=null) - mouseElm.setMouseElm(false); - if (ce!=null) - ce.setMouseElm(true); - mouseElm=ce; - int i; - for (i = 0; i < adjustables.size(); i++) - adjustables.get(i).setMouseElm(ce); - } + if (ce != mouseElm) { + if (mouseElm != null) + mouseElm.setMouseElm(false); + if (ce != null) + ce.setMouseElm(true); + mouseElm = ce; + int i; + for (i = 0; i < adjustables.size(); i++) + adjustables.get(i).setMouseElm(ce); + } } void removeZeroLengthElements() { - int i; - boolean changed = false; - for (i = elmList.size()-1; i >= 0; i--) { - CircuitElm ce = getElm(i); - if (ce.x == ce.x2 && ce.y == ce.y2) { - elmList.removeElementAt(i); - ce.delete(); - changed = true; - } - } - needAnalyze(); + int i; + boolean changed = false; + for (i = elmList.size() - 1; i >= 0; i--) { + CircuitElm ce = getElm(i); + if (ce.x == ce.x2 && ce.y == ce.y2) { + elmList.removeElementAt(i); + ce.delete(); + changed = true; + } + } + needAnalyze(); } boolean mouseIsOverSplitter(int x, int y) { - boolean isOverSplitter; - if (scopeCount == 0) - return false; - isOverSplitter =((x>=0) && (x=circuitArea.height-5) && (y= 0) && (x < circuitArea.width) && + (y >= circuitArea.height - 5) && (y < circuitArea.height)); + if (isOverSplitter != mouseWasOverSplitter) { + if (isOverSplitter) + setCursorStyle("cursorSplitter"); + else + setMouseMode(mouseMode); + } + mouseWasOverSplitter = isOverSplitter; + return isOverSplitter; } public void onMouseMove(MouseMoveEvent e) { - e.preventDefault(); - mouseCursorX=e.getX(); - mouseCursorY=e.getY(); - if (mouseDragging) { - mouseDragged(e); - return; - } - mouseSelect(e); - scopeMenuSelected = -1; + e.preventDefault(); + mouseCursorX = e.getX(); + mouseCursorY = e.getY(); + if (mouseDragging) { + mouseDragged(e); + return; + } + mouseSelect(e); + scopeMenuSelected = -1; } // convert screen coordinates to grid coordinates by inverting circuit transform int inverseTransformX(double x) { - return (int) ((x-transform[4])/transform[0]); + return (int) ((x - transform[4]) / transform[0]); } int inverseTransformY(double y) { - return (int) ((y-transform[5])/transform[3]); + return (int) ((y - transform[5]) / transform[3]); } // convert grid coordinates to screen coordinates int transformX(double x) { - return (int) ((x*transform[0]) + transform[4]); + return (int) ((x * transform[0]) + transform[4]); } int transformY(double y) { - return (int) ((y*transform[3]) + transform[5]); + return (int) ((y * transform[3]) + transform[5]); } - // need to break this out into a separate routine to handle selection, // since we don't get mouse move events on mobile public void mouseSelect(MouseEvent e) { - // The following is in the original, but seems not to work/be needed for GWT - // if (e.getNativeButton()==NativeEvent.BUTTON_LEFT) - // return; - CircuitElm newMouseElm=null; - mouseCursorX=e.getX(); - mouseCursorY=e.getY(); - int sx = e.getX(); - int sy = e.getY(); - int gx = inverseTransformX(sx); - int gy = inverseTransformY(sy); - // console("Settingd draggridx in mouseEvent"); - dragGridX = snapGrid(gx); - dragGridY = snapGrid(gy); - dragScreenX = sx; - dragScreenY = sy; - draggingPost = -1; - int i; - // CircuitElm origMouse = mouseElm; - - mousePost = -1; - plotXElm = plotYElm = null; - - if (mouseIsOverSplitter(sx, sy)) { - setMouseElm(null); - return; - } + // The following is in the original, but seems not to work/be needed for GWT + // if (e.getNativeButton()==NativeEvent.BUTTON_LEFT) + // return; + CircuitElm newMouseElm = null; + mouseCursorX = e.getX(); + mouseCursorY = e.getY(); + int sx = e.getX(); + int sy = e.getY(); + int gx = inverseTransformX(sx); + int gy = inverseTransformY(sy); + // console("Settingd draggridx in mouseEvent"); + dragGridX = snapGrid(gx); + dragGridY = snapGrid(gy); + dragScreenX = sx; + dragScreenY = sy; + draggingPost = -1; + int i; + // CircuitElm origMouse = mouseElm; - if (circuitArea.contains(sx, sy)) { - if (mouseElm!=null && ( mouseElm.getHandleGrabbedClose(gx, gy, POSTGRABSQ, MINPOSTGRABSIZE)>=0)) { - newMouseElm=mouseElm; - } else { - int bestDist = 100000000; - int bestArea = 100000000; - for (i = 0; i != elmList.size(); i++) { - CircuitElm ce = getElm(i); - if (ce.boundingBox.contains(gx, gy)) { - int j; - int area = ce.boundingBox.width * ce.boundingBox.height; - int jn = ce.getPostCount(); - if (jn > 2) - jn = 2; - for (j = 0; j != jn; j++) { - Point pt = ce.getPost(j); - int dist = Graphics.distanceSq(gx, gy, pt.x, pt.y); - - // if multiple elements have overlapping bounding boxes, - // we prefer selecting elements that have posts close - // to the mouse pointer and that have a small bounding - // box area. - if (dist <= bestDist && area <= bestArea) { - bestDist = dist; - bestArea = area; - newMouseElm = ce; - } - } - // prefer selecting elements that have small bounding box area (for - // elements with no posts) - if (ce.getPostCount() == 0 && area <= bestArea) { - newMouseElm = ce; - bestArea = area; - } - } - } // for - } - } - scopeSelected = -1; - if (newMouseElm == null) { - for (i = 0; i != scopeCount; i++) { - Scope s = scopes[i]; - if (s.rect.contains(sx, sy)) { - newMouseElm=s.getElm(); - if (s.plotXY) { - plotXElm = s.getXElm(); - plotYElm = s.getYElm(); - } - scopeSelected = i; - } - } - // // the mouse pointer was not in any of the bounding boxes, but we - // // might still be close to a post - for (i = 0; i != elmList.size(); i++) { - CircuitElm ce = getElm(i); - if (mouseMode==MODE_DRAG_POST ) { - if (ce.getHandleGrabbedClose(gx, gy, POSTGRABSQ, 0)> 0) - { - newMouseElm = ce; - break; - } - } - int j; - int jn = ce.getPostCount(); - for (j = 0; j != jn; j++) { - Point pt = ce.getPost(j); - // int dist = Graphics.distanceSq(x, y, pt.x, pt.y); - if (Graphics.distanceSq(pt.x, pt.y, gx, gy) < 26) { - newMouseElm = ce; - mousePost = j; - break; - } - } - } - } else { - mousePost = -1; - // look for post close to the mouse pointer - for (i = 0; i != newMouseElm.getPostCount(); i++) { - Point pt = newMouseElm.getPost(i); - if (Graphics.distanceSq(pt.x, pt.y, gx, gy) < 26) - mousePost = i; - } - } - repaint(); - setMouseElm(newMouseElm); - } + mousePost = -1; + plotXElm = plotYElm = null; + if (mouseIsOverSplitter(sx, sy)) { + setMouseElm(null); + return; + } + + if (circuitArea.contains(sx, sy)) { + if (mouseElm != null && (mouseElm.getHandleGrabbedClose(gx, gy, POSTGRABSQ, MINPOSTGRABSIZE) >= 0)) { + newMouseElm = mouseElm; + } else { + int bestDist = 100000000; + int bestArea = 100000000; + for (i = 0; i != elmList.size(); i++) { + CircuitElm ce = getElm(i); + if (ce.boundingBox.contains(gx, gy)) { + int j; + int area = ce.boundingBox.width * ce.boundingBox.height; + int jn = ce.getPostCount(); + if (jn > 2) + jn = 2; + for (j = 0; j != jn; j++) { + Point pt = ce.getPost(j); + int dist = Graphics.distanceSq(gx, gy, pt.x, pt.y); + + // if multiple elements have overlapping bounding boxes, + // we prefer selecting elements that have posts close + // to the mouse pointer and that have a small bounding + // box area. + if (dist <= bestDist && area <= bestArea) { + bestDist = dist; + bestArea = area; + newMouseElm = ce; + } + } + // prefer selecting elements that have small bounding box area (for + // elements with no posts) + if (ce.getPostCount() == 0 && area <= bestArea) { + newMouseElm = ce; + bestArea = area; + } + } + } // for + } + } + scopeSelected = -1; + if (newMouseElm == null) { + for (i = 0; i != scopeCount; i++) { + Scope s = scopes[i]; + if (s.rect.contains(sx, sy)) { + newMouseElm = s.getElm(); + if (s.plotXY) { + plotXElm = s.getXElm(); + plotYElm = s.getYElm(); + } + scopeSelected = i; + } + } + // // the mouse pointer was not in any of the bounding boxes, but we + // // might still be close to a post + for (i = 0; i != elmList.size(); i++) { + CircuitElm ce = getElm(i); + if (mouseMode == MODE_DRAG_POST) { + if (ce.getHandleGrabbedClose(gx, gy, POSTGRABSQ, 0) > 0) { + newMouseElm = ce; + break; + } + } + int j; + int jn = ce.getPostCount(); + for (j = 0; j != jn; j++) { + Point pt = ce.getPost(j); + // int dist = Graphics.distanceSq(x, y, pt.x, pt.y); + if (Graphics.distanceSq(pt.x, pt.y, gx, gy) < 26) { + newMouseElm = ce; + mousePost = j; + break; + } + } + } + } else { + mousePost = -1; + // look for post close to the mouse pointer + for (i = 0; i != newMouseElm.getPostCount(); i++) { + Point pt = newMouseElm.getPost(i); + if (Graphics.distanceSq(pt.x, pt.y, gx, gy) < 26) + mousePost = i; + } + } + repaint(); + setMouseElm(newMouseElm); + } public void onContextMenu(ContextMenuEvent e) { - e.preventDefault(); - if (!dialogIsShowing()) { - menuClientX = e.getNativeEvent().getClientX(); - menuClientY = e.getNativeEvent().getClientY(); - doPopupMenu(); - } + e.preventDefault(); + if (!dialogIsShowing()) { + menuClientX = e.getNativeEvent().getClientX(); + menuClientY = e.getNativeEvent().getClientY(); + doPopupMenu(); + } } @SuppressWarnings("deprecation") void doPopupMenu() { - if (noEditCheckItem.getState() || dialogIsShowing()) - return; - menuElm = mouseElm; - menuScope=-1; - menuPlot=-1; - int x, y; - if (scopeSelected!=-1) { - if (scopes[scopeSelected].canMenu()) { - menuScope=scopeSelected; - menuPlot=scopes[scopeSelected].selectedPlot; - scopePopupMenu.doScopePopupChecks(false, canStackScope(scopeSelected), canCombineScope(scopeSelected), - canUnstackScope(scopeSelected), scopes[scopeSelected]); - contextPanel=new PopupPanel(true); - contextPanel.add(scopePopupMenu.getMenuBar()); - y=Math.max(0, Math.min(menuClientY,canvasHeight-160)); - contextPanel.setPopupPosition(menuClientX, y); - contextPanel.show(); - } - } else if (mouseElm != null) { - if (! (mouseElm instanceof ScopeElm)) { - elmScopeMenuItem.setEnabled(mouseElm.canViewInScope()); - elmFloatScopeMenuItem.setEnabled(mouseElm.canViewInScope()); - if ((scopeCount + countScopeElms()) <= 1) { - elmAddScopeMenuItem.setCommand(new MyCommand("elm", "addToScope0")); - elmAddScopeMenuItem.setSubMenu(null); - elmAddScopeMenuItem.setEnabled(mouseElm.canViewInScope() && (scopeCount + countScopeElms())> 0); - } - else { - composeSelectScopeMenu(selectScopeMenuBar); - elmAddScopeMenuItem.setCommand(null); - elmAddScopeMenuItem.setSubMenu(selectScopeMenuBar); - elmAddScopeMenuItem.setEnabled(mouseElm.canViewInScope() ); - } - elmEditMenuItem .setEnabled(mouseElm.getEditInfo(0) != null); - elmFlipMenuItem .setEnabled(mouseElm.getPostCount() == 2); - elmSplitMenuItem.setEnabled(canSplit(mouseElm)); - elmSliderMenuItem.setEnabled(sliderItemEnabled(mouseElm)); - contextPanel=new PopupPanel(true); - contextPanel.add(elmMenuBar); - contextPanel.setPopupPosition(menuClientX, menuClientY); - contextPanel.show(); - } else { - ScopeElm s = (ScopeElm) mouseElm; - if (s.elmScope.canMenu()) { - menuPlot = s.elmScope.selectedPlot; - scopePopupMenu.doScopePopupChecks(true, false, false, false, s.elmScope); - contextPanel=new PopupPanel(true); - contextPanel.add(scopePopupMenu.getMenuBar()); - contextPanel.setPopupPosition(menuClientX, menuClientY); - contextPanel.show(); - } - } - } else { - doMainMenuChecks(); - contextPanel=new PopupPanel(true); - contextPanel.add(mainMenuBar); - x=Math.max(0, Math.min(menuClientX, canvasWidth-400)); - y=Math.max(0, Math.min(menuClientY, canvasHeight-450)); - contextPanel.setPopupPosition(x,y); - contextPanel.show(); - } + if (noEditCheckItem.getState() || dialogIsShowing()) + return; + menuElm = mouseElm; + menuScope = -1; + menuPlot = -1; + int x, y; + if (scopeSelected != -1) { + if (scopes[scopeSelected].canMenu()) { + menuScope = scopeSelected; + menuPlot = scopes[scopeSelected].selectedPlot; + scopePopupMenu.doScopePopupChecks(false, canStackScope(scopeSelected), canCombineScope(scopeSelected), + canUnstackScope(scopeSelected), scopes[scopeSelected]); + contextPanel = new PopupPanel(true); + contextPanel.add(scopePopupMenu.getMenuBar()); + y = Math.max(0, Math.min(menuClientY, canvasHeight - 160)); + contextPanel.setPopupPosition(menuClientX, y); + contextPanel.show(); + } + } else if (mouseElm != null) { + if (!(mouseElm instanceof ScopeElm)) { + elmScopeMenuItem.setEnabled(mouseElm.canViewInScope()); + elmFloatScopeMenuItem.setEnabled(mouseElm.canViewInScope()); + if ((scopeCount + countScopeElms()) <= 1) { + elmAddScopeMenuItem.setCommand(new MyCommand("elm", "addToScope0")); + elmAddScopeMenuItem.setSubMenu(null); + elmAddScopeMenuItem.setEnabled(mouseElm.canViewInScope() && (scopeCount + countScopeElms()) > 0); + } else { + composeSelectScopeMenu(selectScopeMenuBar); + elmAddScopeMenuItem.setCommand(null); + elmAddScopeMenuItem.setSubMenu(selectScopeMenuBar); + elmAddScopeMenuItem.setEnabled(mouseElm.canViewInScope()); + } + elmEditMenuItem.setEnabled(mouseElm.getEditInfo(0) != null); + elmFlipMenuItem.setEnabled(mouseElm.getPostCount() == 2); + elmSplitMenuItem.setEnabled(canSplit(mouseElm)); + elmSliderMenuItem.setEnabled(sliderItemEnabled(mouseElm)); + contextPanel = new PopupPanel(true); + contextPanel.add(elmMenuBar); + contextPanel.setPopupPosition(menuClientX, menuClientY); + contextPanel.show(); + } else { + ScopeElm s = (ScopeElm) mouseElm; + if (s.elmScope.canMenu()) { + menuPlot = s.elmScope.selectedPlot; + scopePopupMenu.doScopePopupChecks(true, false, false, false, s.elmScope); + contextPanel = new PopupPanel(true); + contextPanel.add(scopePopupMenu.getMenuBar()); + contextPanel.setPopupPosition(menuClientX, menuClientY); + contextPanel.show(); + } + } + } else { + doMainMenuChecks(); + contextPanel = new PopupPanel(true); + contextPanel.add(mainMenuBar); + x = Math.max(0, Math.min(menuClientX, canvasWidth - 400)); + y = Math.max(0, Math.min(menuClientY, canvasHeight - 450)); + contextPanel.setPopupPosition(x, y); + contextPanel.show(); + } } boolean canSplit(CircuitElm ce) { - if (!(ce instanceof WireElm)) - return false; - WireElm we = (WireElm) ce; - if (we.x == we.x2 || we.y == we.y2) - return true; - return false; + if (!(ce instanceof WireElm)) + return false; + WireElm we = (WireElm) ce; + if (we.x == we.x2 || we.y == we.y2) + return true; + return false; } // check if the user can create sliders for this element boolean sliderItemEnabled(CircuitElm elm) { - int i; + int i; - // prevent confusion - if (elm instanceof VarRailElm || elm instanceof PotElm) - return false; + // prevent confusion + if (elm instanceof VarRailElm || elm instanceof PotElm) + return false; - for (i = 0; ; i++) { - EditInfo ei = elm.getEditInfo(i); - if (ei == null) - return false; - if (ei.canCreateAdjustable()) - return true; - } + for (i = 0; ; i++) { + EditInfo ei = elm.getEditInfo(i); + if (ei == null) + return false; + if (ei.canCreateAdjustable()) + return true; + } } void longPress() { - doPopupMenu(); + doPopupMenu(); } void twoFingerTouch(int x, int y) { - tempMouseMode = MODE_DRAG_ALL; - dragScreenX = x; - dragScreenY = y; + tempMouseMode = MODE_DRAG_ALL; + dragScreenX = x; + dragScreenY = y; } -// public void mouseClicked(MouseEvent e) { + // public void mouseClicked(MouseEvent e) { public void onClick(ClickEvent e) { - e.preventDefault(); + e.preventDefault(); // //IES - remove inteaction //// if ( e.getClickCount() == 2 && !didSwitch ) //// doEditMenu(e); @@ -4831,28 +4846,28 @@ public void onClick(ClickEvent e) { // if (mouseMode == MODE_SELECT || mouseMode == MODE_DRAG_SELECTED) // clearSelection(); // } - if ((e.getNativeButton() == NativeEvent.BUTTON_MIDDLE)) - scrollValues(e.getNativeEvent().getClientX(), e.getNativeEvent().getClientY(), 0); + if ((e.getNativeButton() == NativeEvent.BUTTON_MIDDLE)) + scrollValues(e.getNativeEvent().getClientX(), e.getNativeEvent().getClientY(), 0); } - public void onDoubleClick(DoubleClickEvent e){ - e.preventDefault(); - // if (!didSwitch && mouseElm != null) - if (mouseElm != null && !(mouseElm instanceof SwitchElm) && !noEditCheckItem.getState()) - doEdit(mouseElm); + public void onDoubleClick(DoubleClickEvent e) { + e.preventDefault(); + // if (!didSwitch && mouseElm != null) + if (mouseElm != null && !(mouseElm instanceof SwitchElm) && !noEditCheckItem.getState()) + doEdit(mouseElm); } // public void mouseEntered(MouseEvent e) { // } public void onMouseOut(MouseOutEvent e) { - mouseCursorX=-1; + mouseCursorX = -1; } void clearMouseElm() { - scopeSelected = -1; - setMouseElm(null); - plotXElm = plotYElm = null; + scopeSelected = -1; + setMouseElm(null); + plotXElm = plotYElm = null; } int menuClientX, menuClientY; @@ -4860,103 +4875,103 @@ void clearMouseElm() { public void onMouseDown(MouseDownEvent e) { // public void mousePressed(MouseEvent e) { - e.preventDefault(); + e.preventDefault(); - // make sure canvas has focus, not stop button or something else, so all shortcuts work - cv.setFocus(true); + // make sure canvas has focus, not stop button or something else, so all shortcuts work + cv.setFocus(true); - stopElm = null; // if stopped, allow user to select other elements to fix circuit - menuX = menuClientX = e.getX(); - menuY = menuClientY = e.getY(); - mouseDownTime = System.currentTimeMillis(); + stopElm = null; // if stopped, allow user to select other elements to fix circuit + menuX = menuClientX = e.getX(); + menuY = menuClientY = e.getY(); + mouseDownTime = System.currentTimeMillis(); - // maybe someone did copy in another window? should really do this when - // window receives focus - enablePaste(); + // maybe someone did copy in another window? should really do this when + // window receives focus + enablePaste(); - if (e.getNativeButton() != NativeEvent.BUTTON_LEFT && e.getNativeButton() != NativeEvent.BUTTON_MIDDLE) - return; + if (e.getNativeButton() != NativeEvent.BUTTON_LEFT && e.getNativeButton() != NativeEvent.BUTTON_MIDDLE) + return; - // set mouseElm in case we are on mobile - mouseSelect(e); + // set mouseElm in case we are on mobile + mouseSelect(e); - mouseDragging=true; - didSwitch = false; + mouseDragging = true; + didSwitch = false; - if (mouseWasOverSplitter) { - tempMouseMode = MODE_DRAG_SPLITTER; - return; - } - if (e.getNativeButton() == NativeEvent.BUTTON_LEFT) { + if (mouseWasOverSplitter) { + tempMouseMode = MODE_DRAG_SPLITTER; + return; + } + if (e.getNativeButton() == NativeEvent.BUTTON_LEFT) { // // left mouse - tempMouseMode = mouseMode; - if (e.isAltKeyDown() && e.isMetaKeyDown()) - tempMouseMode = MODE_DRAG_COLUMN; - else if (e.isAltKeyDown() && e.isShiftKeyDown()) - tempMouseMode = MODE_DRAG_ROW; - else if (e.isShiftKeyDown()) - tempMouseMode = MODE_SELECT; - else if (e.isAltKeyDown()) - tempMouseMode = MODE_DRAG_ALL; - else if (e.isControlKeyDown() || e.isMetaKeyDown()) - tempMouseMode = MODE_DRAG_POST; - } else - tempMouseMode = MODE_DRAG_ALL; - - - if (noEditCheckItem.getState()) - tempMouseMode = MODE_SELECT; - - if (!(dialogIsShowing()) && ((scopeSelected != -1 && scopes[scopeSelected].cursorInSettingsWheel()) || - ( scopeSelected == -1 && mouseElm instanceof ScopeElm && ((ScopeElm)mouseElm).elmScope.cursorInSettingsWheel()))){ - if (noEditCheckItem.getState()) - return; - Scope s; - if (scopeSelected != -1) - s=scopes[scopeSelected]; - else - s=((ScopeElm)mouseElm).elmScope; - s.properties(); - clearSelection(); - mouseDragging=false; - return; - } + tempMouseMode = mouseMode; + if (e.isAltKeyDown() && e.isMetaKeyDown()) + tempMouseMode = MODE_DRAG_COLUMN; + else if (e.isAltKeyDown() && e.isShiftKeyDown()) + tempMouseMode = MODE_DRAG_ROW; + else if (e.isShiftKeyDown()) + tempMouseMode = MODE_SELECT; + else if (e.isAltKeyDown()) + tempMouseMode = MODE_DRAG_ALL; + else if (e.isControlKeyDown() || e.isMetaKeyDown()) + tempMouseMode = MODE_DRAG_POST; + } else + tempMouseMode = MODE_DRAG_ALL; - int gx = inverseTransformX(e.getX()); - int gy = inverseTransformY(e.getY()); - if (doSwitch(gx, gy)) { - // do this BEFORE we change the mouse mode to MODE_DRAG_POST! Or else logic inputs - // will add dots to the whole circuit when we click on them! + + if (noEditCheckItem.getState()) + tempMouseMode = MODE_SELECT; + + if (!(dialogIsShowing()) && ((scopeSelected != -1 && scopes[scopeSelected].cursorInSettingsWheel()) || + (scopeSelected == -1 && mouseElm instanceof ScopeElm && ((ScopeElm) mouseElm).elmScope.cursorInSettingsWheel()))) { + if (noEditCheckItem.getState()) + return; + Scope s; + if (scopeSelected != -1) + s = scopes[scopeSelected]; + else + s = ((ScopeElm) mouseElm).elmScope; + s.properties(); + clearSelection(); + mouseDragging = false; + return; + } + + int gx = inverseTransformX(e.getX()); + int gy = inverseTransformY(e.getY()); + if (doSwitch(gx, gy)) { + // do this BEFORE we change the mouse mode to MODE_DRAG_POST! Or else logic inputs + // will add dots to the whole circuit when we click on them! didSwitch = true; - return; - } + return; + } + + // IES - Grab resize handles in select mode if they are far enough apart and you are on top of them + if (tempMouseMode == MODE_SELECT && mouseElm != null && !noEditCheckItem.getState() && + mouseElm.getHandleGrabbedClose(gx, gy, POSTGRABSQ, MINPOSTGRABSIZE) >= 0 && + !anySelectedButMouse()) + tempMouseMode = MODE_DRAG_POST; - // IES - Grab resize handles in select mode if they are far enough apart and you are on top of them - if (tempMouseMode == MODE_SELECT && mouseElm!=null && !noEditCheckItem.getState() && - mouseElm.getHandleGrabbedClose(gx, gy, POSTGRABSQ, MINPOSTGRABSIZE) >=0 && - !anySelectedButMouse()) - tempMouseMode = MODE_DRAG_POST; - - if (tempMouseMode != MODE_SELECT && tempMouseMode != MODE_DRAG_SELECTED) - clearSelection(); - - pushUndo(); - initDragGridX = gx; - initDragGridY = gy; - dragging = true; - if (tempMouseMode !=MODE_ADD_ELM) - return; + if (tempMouseMode != MODE_SELECT && tempMouseMode != MODE_DRAG_SELECTED) + clearSelection(); + + pushUndo(); + initDragGridX = gx; + initDragGridY = gy; + dragging = true; + if (tempMouseMode != MODE_ADD_ELM) + return; // - int x0 = snapGrid(gx); - int y0 = snapGrid(gy); - if (!circuitArea.contains(e.getX(), e.getY())) - return; - - try { - dragElm = constructElement(mouseModeStr, x0, y0); - } catch (Exception ex) { - debugger(); - } + int x0 = snapGrid(gx); + int y0 = snapGrid(gy); + if (!circuitArea.contains(e.getX(), e.getY())) + return; + + try { + dragElm = constructElement(mouseModeStr, x0, y0); + } catch (Exception ex) { + debugger(); + } } static int lastSubcircuitMenuUpdate; @@ -4964,655 +4979,654 @@ else if (e.isControlKeyDown() || e.isMetaKeyDown()) // check/uncheck/enable/disable menu items as appropriate when menu bar clicked on, or when // right mouse menu accessed. also displays shortcuts as a side effect void doMainMenuChecks() { - int c = mainMenuItems.size(); - int i; - for (i=0; i 3 && s.substring(s.length()-3)=="Elm") - //mainMenuItems.get(i).setEnabled(!noEditCheckItem.getState()); - } - stackAllItem.setEnabled(scopeCount > 1 && scopes[scopeCount-1].position > 0); - unstackAllItem.setEnabled(scopeCount > 1 && scopes[scopeCount-1].position != scopeCount -1); - combineAllItem.setEnabled(scopeCount > 1); - separateAllItem.setEnabled(scopeCount > 0); + int c = mainMenuItems.size(); + int i; + for (i = 0; i < c; i++) { + String s = mainMenuItemNames.get(i); + mainMenuItems.get(i).setState(s == mouseModeStr); + + // Code to disable draw menu items when cct is not editable, but no used in this version as it + // puts up a dialog box instead (see menuPerformed). + //if (s.length() > 3 && s.substring(s.length()-3)=="Elm") + //mainMenuItems.get(i).setEnabled(!noEditCheckItem.getState()); + } + stackAllItem.setEnabled(scopeCount > 1 && scopes[scopeCount - 1].position > 0); + unstackAllItem.setEnabled(scopeCount > 1 && scopes[scopeCount - 1].position != scopeCount - 1); + combineAllItem.setEnabled(scopeCount > 1); + separateAllItem.setEnabled(scopeCount > 0); - // also update the subcircuit menu if necessary - if (lastSubcircuitMenuUpdate != CustomCompositeModel.sequenceNumber) - composeSubcircuitMenu(); + // also update the subcircuit menu if necessary + if (lastSubcircuitMenuUpdate != CustomCompositeModel.sequenceNumber) + composeSubcircuitMenu(); } public void onMouseUp(MouseUpEvent e) { - e.preventDefault(); - mouseDragging=false; - - // click to clear selection - if (tempMouseMode == MODE_SELECT && selectedArea == null) - clearSelection(); - - // cmd-click = split wire - if (tempMouseMode == MODE_DRAG_POST && draggingPost == -1) - doSplit(mouseElm); - - tempMouseMode = mouseMode; - selectedArea = null; - dragging = false; - boolean circuitChanged = false; - if (heldSwitchElm != null) { - heldSwitchElm.mouseUp(); - heldSwitchElm = null; - circuitChanged = true; - } - if (dragElm != null) { - // if the element is zero size then don't create it - // IES - and disable any previous selection - if (dragElm.creationFailed()) { - dragElm.delete(); - if (mouseMode == MODE_SELECT || mouseMode == MODE_DRAG_SELECTED) - clearSelection(); - } - else { - elmList.addElement(dragElm); - dragElm.draggingDone(); - circuitChanged = true; - writeRecoveryToStorage(); - unsavedChanges = true; - } - dragElm = null; - } - if (circuitChanged) { - needAnalyze(); - pushUndo(); - } - if (dragElm != null) - dragElm.delete(); - dragElm = null; - repaint(); + e.preventDefault(); + mouseDragging = false; + + // click to clear selection + if (tempMouseMode == MODE_SELECT && selectedArea == null) + clearSelection(); + + // cmd-click = split wire + if (tempMouseMode == MODE_DRAG_POST && draggingPost == -1) + doSplit(mouseElm); + + tempMouseMode = mouseMode; + selectedArea = null; + dragging = false; + boolean circuitChanged = false; + if (heldSwitchElm != null) { + heldSwitchElm.mouseUp(); + heldSwitchElm = null; + circuitChanged = true; + } + if (dragElm != null) { + // if the element is zero size then don't create it + // IES - and disable any previous selection + if (dragElm.creationFailed()) { + dragElm.delete(); + if (mouseMode == MODE_SELECT || mouseMode == MODE_DRAG_SELECTED) + clearSelection(); + } else { + elmList.addElement(dragElm); + dragElm.draggingDone(); + circuitChanged = true; + writeRecoveryToStorage(); + unsavedChanges = true; + } + dragElm = null; + } + if (circuitChanged) { + needAnalyze(); + pushUndo(); + } + if (dragElm != null) + dragElm.delete(); + dragElm = null; + repaint(); } public void onMouseWheel(MouseWheelEvent e) { - e.preventDefault(); - - // once we start zooming, don't allow other uses of mouse wheel for a while - // so we don't accidentally edit a resistor value while zooming - boolean zoomOnly = System.currentTimeMillis() < zoomTime+1000; - - if (noEditCheckItem.getState() || !mouseWheelEditCheckItem.getState()) - zoomOnly = true; - - if (!zoomOnly) - scrollValues(e.getNativeEvent().getClientX(), e.getNativeEvent().getClientY(), e.getDeltaY()); - - if (mouseElm instanceof MouseWheelHandler && !zoomOnly) - ((MouseWheelHandler) mouseElm).onMouseWheel(e); - else if (scopeSelected != -1 && !zoomOnly) - scopes[scopeSelected].onMouseWheel(e); - else if (!dialogIsShowing()) { - mouseCursorX=e.getX(); - mouseCursorY=e.getY(); - zoomCircuit(-e.getDeltaY(), false); - zoomTime = System.currentTimeMillis(); - } - repaint(); + e.preventDefault(); + + // once we start zooming, don't allow other uses of mouse wheel for a while + // so we don't accidentally edit a resistor value while zooming + boolean zoomOnly = System.currentTimeMillis() < zoomTime + 1000; + + if (noEditCheckItem.getState() || !mouseWheelEditCheckItem.getState()) + zoomOnly = true; + + if (!zoomOnly) + scrollValues(e.getNativeEvent().getClientX(), e.getNativeEvent().getClientY(), e.getDeltaY()); + + if (mouseElm instanceof MouseWheelHandler && !zoomOnly) + ((MouseWheelHandler) mouseElm).onMouseWheel(e); + else if (scopeSelected != -1 && !zoomOnly) + scopes[scopeSelected].onMouseWheel(e); + else if (!dialogIsShowing()) { + mouseCursorX = e.getX(); + mouseCursorY = e.getY(); + zoomCircuit(-e.getDeltaY(), false); + zoomTime = System.currentTimeMillis(); + } + repaint(); } - void zoomCircuit(double dy) { zoomCircuit(dy, false); } + void zoomCircuit(double dy) { + zoomCircuit(dy, false); + } void zoomCircuit(double dy, boolean menu) { - double newScale; - double oldScale = transform[0]; - double val = dy*.01; - newScale = Math.max(oldScale+val, .2); - newScale = Math.min(newScale, 2.5); - setCircuitScale(newScale, menu); + double newScale; + double oldScale = transform[0]; + double val = dy * .01; + newScale = Math.max(oldScale + val, .2); + newScale = Math.min(newScale, 2.5); + setCircuitScale(newScale, menu); } void setCircuitScale(double newScale, boolean menu) { - int constX = !menu ? mouseCursorX : circuitArea.width/2; - int constY = !menu ? mouseCursorY : circuitArea.height/2; - int cx = inverseTransformX(constX); - int cy = inverseTransformY(constY); - transform[0] = transform[3] = newScale; + int constX = !menu ? mouseCursorX : circuitArea.width / 2; + int constY = !menu ? mouseCursorY : circuitArea.height / 2; + int cx = inverseTransformX(constX); + int cy = inverseTransformY(constY); + transform[0] = transform[3] = newScale; - // adjust translation to keep center of screen constant - // inverse transform = (x-t4)/t0 - transform[4] = constX - cx*newScale; - transform[5] = constY - cy*newScale; + // adjust translation to keep center of screen constant + // inverse transform = (x-t4)/t0 + transform[4] = constX - cx * newScale; + transform[5] = constY - cy * newScale; } void setPowerBarEnable() { - if (powerCheckItem.getState()) { - powerLabel.setStyleName("disabled", false); - powerBar.enable(); - } else { - powerLabel.setStyleName("disabled", true); - powerBar.disable(); - } + if (powerCheckItem.getState()) { + powerLabel.setStyleName("disabled", false); + powerBar.enable(); + } else { + powerLabel.setStyleName("disabled", true); + powerBar.disable(); + } } void scrollValues(int x, int y, int deltay) { - if (mouseElm!=null && !dialogIsShowing() && scopeSelected == -1) - if (mouseElm instanceof ResistorElm || mouseElm instanceof CapacitorElm || mouseElm instanceof InductorElm) { - scrollValuePopup = new ScrollValuePopup(x, y, deltay, mouseElm, this); - } + if (mouseElm != null && !dialogIsShowing() && scopeSelected == -1) + if (mouseElm instanceof ResistorElm || mouseElm instanceof CapacitorElm || mouseElm instanceof InductorElm) { + scrollValuePopup = new ScrollValuePopup(x, y, deltay, mouseElm, this); + } } void enableItems() { } void setGrid() { - gridSize = (smallGridCheckItem.getState()) ? 8 : 16; - gridMask = ~(gridSize-1); - gridRound = gridSize/2-1; + gridSize = (smallGridCheckItem.getState()) ? 8 : 16; + gridMask = ~(gridSize - 1); + gridRound = gridSize / 2 - 1; } void pushUndo() { - redoStack.removeAllElements(); - String s = dumpCircuit(); - if (undoStack.size() > 0 && - s.compareTo(undoStack.lastElement().dump) == 0) - return; - undoStack.add(new UndoItem(s)); - enableUndoRedo(); - savedFlag = false; + redoStack.removeAllElements(); + String s = dumpCircuit(); + if (undoStack.size() > 0 && + s.compareTo(undoStack.lastElement().dump) == 0) + return; + undoStack.add(new UndoItem(s)); + enableUndoRedo(); + savedFlag = false; } void doUndo() { - if (undoStack.size() == 0) - return; - redoStack.add(new UndoItem(dumpCircuit())); - UndoItem ui = undoStack.remove(undoStack.size()-1); - loadUndoItem(ui); - enableUndoRedo(); + if (undoStack.size() == 0) + return; + redoStack.add(new UndoItem(dumpCircuit())); + UndoItem ui = undoStack.remove(undoStack.size() - 1); + loadUndoItem(ui); + enableUndoRedo(); } void doRedo() { - if (redoStack.size() == 0) - return; - undoStack.add(new UndoItem(dumpCircuit())); - UndoItem ui = redoStack.remove(redoStack.size()-1); - loadUndoItem(ui); - enableUndoRedo(); + if (redoStack.size() == 0) + return; + undoStack.add(new UndoItem(dumpCircuit())); + UndoItem ui = redoStack.remove(redoStack.size() - 1); + loadUndoItem(ui); + enableUndoRedo(); } void loadUndoItem(UndoItem ui) { - readCircuit(ui.dump, RC_NO_CENTER); - transform[0] = transform[3] = ui.scale; - transform[4] = ui.transform4; - transform[5] = ui.transform5; + readCircuit(ui.dump, RC_NO_CENTER); + transform[0] = transform[3] = ui.scale; + transform[4] = ui.transform4; + transform[5] = ui.transform5; } void doRecover() { - pushUndo(); - readCircuit(recovery); - allowSave(false); - recoverItem.setEnabled(false); + pushUndo(); + readCircuit(recovery); + allowSave(false); + recoverItem.setEnabled(false); } void enableUndoRedo() { - redoItem.setEnabled(redoStack.size() > 0); - undoItem.setEnabled(undoStack.size() > 0); + redoItem.setEnabled(redoStack.size() > 0); + undoItem.setEnabled(undoStack.size() > 0); } - void setMouseMode(int mode) - { - mouseMode = mode; - if ( mode == MODE_ADD_ELM ) { - setCursorStyle("cursorCross"); - } else { - setCursorStyle("cursorPointer"); - } + void setMouseMode(int mode) { + mouseMode = mode; + if (mode == MODE_ADD_ELM) { + setCursorStyle("cursorCross"); + } else { + setCursorStyle("cursorPointer"); + } } void setCursorStyle(String s) { - if (lastCursorStyle!=null) - cv.removeStyleName(lastCursorStyle); - cv.addStyleName(s); - lastCursorStyle=s; + if (lastCursorStyle != null) + cv.removeStyleName(lastCursorStyle); + cv.addStyleName(s); + lastCursorStyle = s; } - void setMenuSelection() { - if (menuElm != null) { - if (menuElm.selected) - return; - clearSelection(); - menuElm.setSelected(true); - } + if (menuElm != null) { + if (menuElm.selected) + return; + clearSelection(); + menuElm.setSelected(true); + } } void doCut() { - int i; - pushUndo(); - setMenuSelection(); - clipboard = ""; - for (i = elmList.size()-1; i >= 0; i--) { - CircuitElm ce = getElm(i); - // ScopeElms don't cut-paste well because their reference to a parent - // elm by number get's messed up in the dump. For now we will just ignore them - // until I can be bothered to come up with something better - if (willDelete(ce) && !(ce instanceof ScopeElm) ) { - clipboard += ce.dump() + "\n"; - } - } - writeClipboardToStorage(); - doDelete(true); - enablePaste(); + int i; + pushUndo(); + setMenuSelection(); + clipboard = ""; + for (i = elmList.size() - 1; i >= 0; i--) { + CircuitElm ce = getElm(i); + // ScopeElms don't cut-paste well because their reference to a parent + // elm by number get's messed up in the dump. For now we will just ignore them + // until I can be bothered to come up with something better + if (willDelete(ce) && !(ce instanceof ScopeElm)) { + clipboard += ce.dump() + "\n"; + } + } + writeClipboardToStorage(); + doDelete(true); + enablePaste(); } void writeClipboardToStorage() { - Storage stor = Storage.getLocalStorageIfSupported(); - if (stor == null) - return; - stor.setItem("circuitClipboard", clipboard); + Storage stor = Storage.getLocalStorageIfSupported(); + if (stor == null) + return; + stor.setItem("circuitClipboard", clipboard); } void readClipboardFromStorage() { - Storage stor = Storage.getLocalStorageIfSupported(); - if (stor == null) - return; - clipboard = stor.getItem("circuitClipboard"); + Storage stor = Storage.getLocalStorageIfSupported(); + if (stor == null) + return; + clipboard = stor.getItem("circuitClipboard"); } void writeRecoveryToStorage() { - console("write recovery"); - Storage stor = Storage.getLocalStorageIfSupported(); - if (stor == null) - return; - String s = dumpCircuit(); - stor.setItem("circuitRecovery", s); + console("write recovery"); + Storage stor = Storage.getLocalStorageIfSupported(); + if (stor == null) + return; + String s = dumpCircuit(); + stor.setItem("circuitRecovery", s); } void readRecovery() { - Storage stor = Storage.getLocalStorageIfSupported(); - if (stor == null) - return; - recovery = stor.getItem("circuitRecovery"); + Storage stor = Storage.getLocalStorageIfSupported(); + if (stor == null) + return; + recovery = stor.getItem("circuitRecovery"); } void deleteUnusedScopeElms() { - // Remove any scopeElms for elements that no longer exist - for (int i = elmList.size()-1; i >= 0; i--) { - CircuitElm ce = getElm(i); - if (ce instanceof ScopeElm && (((ScopeElm) ce).elmScope.needToRemove() )) { - ce.delete(); - elmList.removeElementAt(i); - - // need to rebuild scopeElmArr - needAnalyze(); - } - } + // Remove any scopeElms for elements that no longer exist + for (int i = elmList.size() - 1; i >= 0; i--) { + CircuitElm ce = getElm(i); + if (ce instanceof ScopeElm && (((ScopeElm) ce).elmScope.needToRemove())) { + ce.delete(); + elmList.removeElementAt(i); + + // need to rebuild scopeElmArr + needAnalyze(); + } + } } void doDelete(boolean pushUndoFlag) { - int i; - if (pushUndoFlag) - pushUndo(); - boolean hasDeleted = false; - - for (i = elmList.size()-1; i >= 0; i--) { - CircuitElm ce = getElm(i); - if (willDelete(ce)) { - if (ce.isMouseElm()) - setMouseElm(null); - ce.delete(); - elmList.removeElementAt(i); - hasDeleted = true; - } - } - if ( hasDeleted ) { - deleteUnusedScopeElms(); - needAnalyze(); - writeRecoveryToStorage(); - } + int i; + if (pushUndoFlag) + pushUndo(); + boolean hasDeleted = false; + + for (i = elmList.size() - 1; i >= 0; i--) { + CircuitElm ce = getElm(i); + if (willDelete(ce)) { + if (ce.isMouseElm()) + setMouseElm(null); + ce.delete(); + elmList.removeElementAt(i); + hasDeleted = true; + } + } + if (hasDeleted) { + deleteUnusedScopeElms(); + needAnalyze(); + writeRecoveryToStorage(); + } } - boolean willDelete( CircuitElm ce ) { - // Is this element in the list to be deleted. - // This changes the logic from the previous version which would initially only - // delete selected elements (which could include the mouseElm) and then delete the - // mouseElm if there were no selected elements. Not really sure this added anything useful - // to the user experience. - // - // BTW, the old logic could also leave mouseElm pointing to a deleted element. - return ce.isSelected() || ce.isMouseElm(); + boolean willDelete(CircuitElm ce) { + // Is this element in the list to be deleted. + // This changes the logic from the previous version which would initially only + // delete selected elements (which could include the mouseElm) and then delete the + // mouseElm if there were no selected elements. Not really sure this added anything useful + // to the user experience. + // + // BTW, the old logic could also leave mouseElm pointing to a deleted element. + return ce.isSelected() || ce.isMouseElm(); } String copyOfSelectedElms() { - String r = dumpOptions(); - CustomLogicModel.clearDumpedFlags(); - CustomCompositeModel.clearDumpedFlags(); - DiodeModel.clearDumpedFlags(); - TransistorModel.clearDumpedFlags(); - for (int i = elmList.size()-1; i >= 0; i--) { - CircuitElm ce = getElm(i); - String m = ce.dumpModel(); - if (m != null && !m.isEmpty()) - r += m + "\n"; - // See notes on do cut why we don't copy ScopeElms. - if (ce.isSelected() && !(ce instanceof ScopeElm)) - r += ce.dump() + "\n"; - } - return r; + String r = dumpOptions(); + CustomLogicModel.clearDumpedFlags(); + CustomCompositeModel.clearDumpedFlags(); + DiodeModel.clearDumpedFlags(); + TransistorModel.clearDumpedFlags(); + for (int i = elmList.size() - 1; i >= 0; i--) { + CircuitElm ce = getElm(i); + String m = ce.dumpModel(); + if (m != null && !m.isEmpty()) + r += m + "\n"; + // See notes on do cut why we don't copy ScopeElms. + if (ce.isSelected() && !(ce instanceof ScopeElm)) + r += ce.dump() + "\n"; + } + return r; } void doCopy() { - // clear selection when we're done if we're copying a single element using the context menu - boolean clearSel = (menuElm != null && !menuElm.selected); + // clear selection when we're done if we're copying a single element using the context menu + boolean clearSel = (menuElm != null && !menuElm.selected); - setMenuSelection(); - clipboard=copyOfSelectedElms(); + setMenuSelection(); + clipboard = copyOfSelectedElms(); - if (clearSel) - clearSelection(); + if (clearSel) + clearSelection(); - writeClipboardToStorage(); - enablePaste(); + writeClipboardToStorage(); + enablePaste(); } void enablePaste() { - if (clipboard == null || clipboard.length() == 0) - readClipboardFromStorage(); - pasteItem.setEnabled(clipboard != null && clipboard.length() > 0); + if (clipboard == null || clipboard.length() == 0) + readClipboardFromStorage(); + pasteItem.setEnabled(clipboard != null && clipboard.length() > 0); } void doDuplicate() { - String s; - setMenuSelection(); - s=copyOfSelectedElms(); - doPaste(s); + String s; + setMenuSelection(); + s = copyOfSelectedElms(); + doPaste(s); } void doPaste(String dump) { - pushUndo(); - clearSelection(); - int i; - Rectangle oldbb = null; - - // get old bounding box - for (i = 0; i != elmList.size(); i++) { - CircuitElm ce = getElm(i); - Rectangle bb = ce.getBoundingBox(); - if (oldbb != null) - oldbb = oldbb.union(bb); - else - oldbb = bb; - } + pushUndo(); + clearSelection(); + int i; + Rectangle oldbb = null; + + // get old bounding box + for (i = 0; i != elmList.size(); i++) { + CircuitElm ce = getElm(i); + Rectangle bb = ce.getBoundingBox(); + if (oldbb != null) + oldbb = oldbb.union(bb); + else + oldbb = bb; + } - // add new items - int oldsz = elmList.size(); - int flags = RC_RETAIN; + // add new items + int oldsz = elmList.size(); + int flags = RC_RETAIN; - // don't recenter circuit if we're going to paste in place because that will change the transform + // don't recenter circuit if we're going to paste in place because that will change the transform // if (mouseCursorX > 0 && circuitArea.contains(mouseCursorX, mouseCursorY)) - // in fact, don't ever recenter circuit, unless old circuit was empty - if (oldsz > 0) - flags |= RC_NO_CENTER; + // in fact, don't ever recenter circuit, unless old circuit was empty + if (oldsz > 0) + flags |= RC_NO_CENTER; - if (dump != null) - readCircuit(dump, flags); - else { - readClipboardFromStorage(); - readCircuit(clipboard, flags); - } + if (dump != null) + readCircuit(dump, flags); + else { + readClipboardFromStorage(); + readCircuit(clipboard, flags); + } - // select new items and get their bounding box - Rectangle newbb = null; - for (i = oldsz; i != elmList.size(); i++) { - CircuitElm ce = getElm(i); - ce.setSelected(true); - Rectangle bb = ce.getBoundingBox(); - if (newbb != null) - newbb = newbb.union(bb); - else - newbb = bb; - } + // select new items and get their bounding box + Rectangle newbb = null; + for (i = oldsz; i != elmList.size(); i++) { + CircuitElm ce = getElm(i); + ce.setSelected(true); + Rectangle bb = ce.getBoundingBox(); + if (newbb != null) + newbb = newbb.union(bb); + else + newbb = bb; + } - if (oldbb != null && newbb != null /*&& oldbb.intersects(newbb)*/) { - // find a place on the edge for new items - int dx = 0, dy = 0; - int spacew = circuitArea.width - oldbb.width - newbb.width; - int spaceh = circuitArea.height - oldbb.height - newbb.height; - - if (!oldbb.intersects(newbb)) { - // old coordinates may be really far away so move them to same origin as current circuit - dx = snapGrid(oldbb.x - newbb.x); - dy = snapGrid(oldbb.y - newbb.y); - } - - if (spacew > spaceh) { - dx = snapGrid(oldbb.x + oldbb.width - newbb.x + gridSize); - } else { - dy = snapGrid(oldbb.y + oldbb.height - newbb.y + gridSize); - } - - // move new items near the mouse if possible - if (mouseCursorX > 0 && circuitArea.contains(mouseCursorX, mouseCursorY)) { - int gx = inverseTransformX(mouseCursorX); - int gy = inverseTransformY(mouseCursorY); - int mdx = snapGrid(gx-(newbb.x+newbb.width/2)); - int mdy = snapGrid(gy-(newbb.y+newbb.height/2)); - for (i = oldsz; i != elmList.size(); i++) { - if (!getElm(i).allowMove(mdx, mdy)) - break; - } - if (i == elmList.size()) { - dx = mdx; - dy = mdy; - } - } - - // move the new items - for (i = oldsz; i != elmList.size(); i++) { - CircuitElm ce = getElm(i); - ce.move(dx, dy); - } - - // center circuit - // handleResize(); - } - needAnalyze(); - writeRecoveryToStorage(); + if (oldbb != null && newbb != null /*&& oldbb.intersects(newbb)*/) { + // find a place on the edge for new items + int dx = 0, dy = 0; + int spacew = circuitArea.width - oldbb.width - newbb.width; + int spaceh = circuitArea.height - oldbb.height - newbb.height; + + if (!oldbb.intersects(newbb)) { + // old coordinates may be really far away so move them to same origin as current circuit + dx = snapGrid(oldbb.x - newbb.x); + dy = snapGrid(oldbb.y - newbb.y); + } + + if (spacew > spaceh) { + dx = snapGrid(oldbb.x + oldbb.width - newbb.x + gridSize); + } else { + dy = snapGrid(oldbb.y + oldbb.height - newbb.y + gridSize); + } + + // move new items near the mouse if possible + if (mouseCursorX > 0 && circuitArea.contains(mouseCursorX, mouseCursorY)) { + int gx = inverseTransformX(mouseCursorX); + int gy = inverseTransformY(mouseCursorY); + int mdx = snapGrid(gx - (newbb.x + newbb.width / 2)); + int mdy = snapGrid(gy - (newbb.y + newbb.height / 2)); + for (i = oldsz; i != elmList.size(); i++) { + if (!getElm(i).allowMove(mdx, mdy)) + break; + } + if (i == elmList.size()) { + dx = mdx; + dy = mdy; + } + } + + // move the new items + for (i = oldsz; i != elmList.size(); i++) { + CircuitElm ce = getElm(i); + ce.move(dx, dy); + } + + // center circuit + // handleResize(); + } + needAnalyze(); + writeRecoveryToStorage(); } void clearSelection() { - int i; - for (i = 0; i != elmList.size(); i++) { - CircuitElm ce = getElm(i); - ce.setSelected(false); - } + int i; + for (i = 0; i != elmList.size(); i++) { + CircuitElm ce = getElm(i); + ce.setSelected(false); + } } void doSelectAll() { - int i; - for (i = 0; i != elmList.size(); i++) { - CircuitElm ce = getElm(i); - ce.setSelected(true); - } + int i; + for (i = 0; i != elmList.size(); i++) { + CircuitElm ce = getElm(i); + ce.setSelected(true); + } } boolean anySelectedButMouse() { - for (int i=0; i != elmList.size(); i++) - if (getElm(i)!= mouseElm && getElm(i).selected) - return true; - return false; + for (int i = 0; i != elmList.size(); i++) + if (getElm(i) != mouseElm && getElm(i).selected) + return true; + return false; } // public void keyPressed(KeyEvent e) {} // public void keyReleased(KeyEvent e) {} boolean dialogIsShowing() { - if (editDialog!=null && editDialog.isShowing()) - return true; - if (customLogicEditDialog!=null && customLogicEditDialog.isShowing()) - return true; - if (diodeModelEditDialog!=null && diodeModelEditDialog.isShowing()) - return true; - if (dialogShowing != null && dialogShowing.isShowing()) - return true; - if (contextPanel!=null && contextPanel.isShowing()) - return true; - if (scrollValuePopup != null && scrollValuePopup.isShowing()) - return true; - if (aboutBox !=null && aboutBox.isShowing()) - return true; - if (helpDialog !=null && helpDialog.isShowing()) - return true; - if (licenseDialog !=null && licenseDialog.isShowing()) - return true; - return false; + if (editDialog != null && editDialog.isShowing()) + return true; + if (customLogicEditDialog != null && customLogicEditDialog.isShowing()) + return true; + if (diodeModelEditDialog != null && diodeModelEditDialog.isShowing()) + return true; + if (dialogShowing != null && dialogShowing.isShowing()) + return true; + if (contextPanel != null && contextPanel.isShowing()) + return true; + if (scrollValuePopup != null && scrollValuePopup.isShowing()) + return true; + if (aboutBox != null && aboutBox.isShowing()) + return true; + if (helpDialog != null && helpDialog.isShowing()) + return true; + if (licenseDialog != null && licenseDialog.isShowing()) + return true; + return false; } public void onPreviewNativeEvent(NativePreviewEvent e) { - int cc=e.getNativeEvent().getCharCode(); - int t=e.getTypeInt(); - int code=e.getNativeEvent().getKeyCode(); - if (dialogIsShowing()) { - if (scrollValuePopup != null && scrollValuePopup.isShowing() && - (t & Event.ONKEYDOWN)!=0) { - if (code==KEY_ESCAPE || code==KEY_SPACE) - scrollValuePopup.close(false); - if (code==KEY_ENTER) - scrollValuePopup.close(true); - } - - // process escape/enter for dialogs - // multiple edit dialogs could be displayed at once, pick the one in front - Dialog dlg = editDialog; - if (diodeModelEditDialog != null) - dlg = diodeModelEditDialog; - if (customLogicEditDialog != null) - dlg = customLogicEditDialog; - if (dialogShowing != null) - dlg = dialogShowing; - if (dlg!=null && dlg.isShowing() && - (t & Event.ONKEYDOWN)!=0) { - if (code==KEY_ESCAPE) - dlg.closeDialog(); - if (code==KEY_ENTER) - dlg.enterPressed(); - } - return; - } + int cc = e.getNativeEvent().getCharCode(); + int t = e.getTypeInt(); + int code = e.getNativeEvent().getKeyCode(); + if (dialogIsShowing()) { + if (scrollValuePopup != null && scrollValuePopup.isShowing() && + (t & Event.ONKEYDOWN) != 0) { + if (code == KEY_ESCAPE || code == KEY_SPACE) + scrollValuePopup.close(false); + if (code == KEY_ENTER) + scrollValuePopup.close(true); + } - if ((t&Event.ONKEYPRESS)!=0) { - if (cc=='-') { - menuPerformed("key", "zoomout"); - e.cancel(); - } - if (cc=='+' || cc == '=') { - menuPerformed("key", "zoomin"); - e.cancel(); - } - if (cc=='0') { - menuPerformed("key", "zoom100"); - e.cancel(); - } - if (cc=='/' && shortcuts['/'] == null) { - menuPerformed("key", "search"); - e.cancel(); - } - } + // process escape/enter for dialogs + // multiple edit dialogs could be displayed at once, pick the one in front + Dialog dlg = editDialog; + if (diodeModelEditDialog != null) + dlg = diodeModelEditDialog; + if (customLogicEditDialog != null) + dlg = customLogicEditDialog; + if (dialogShowing != null) + dlg = dialogShowing; + if (dlg != null && dlg.isShowing() && + (t & Event.ONKEYDOWN) != 0) { + if (code == KEY_ESCAPE) + dlg.closeDialog(); + if (code == KEY_ENTER) + dlg.enterPressed(); + } + return; + } - // all other shortcuts are ignored when editing disabled - if (noEditCheckItem.getState()) - return; - - if ((t & Event.ONKEYDOWN)!=0) { - if (code==KEY_BACKSPACE || code==KEY_DELETE) { - if (scopeSelected != -1) { - // Treat DELETE key with scope selected as "remove scope", not delete - scopes[scopeSelected].setElm(null); - scopeSelected = -1; - } else { - menuElm = null; - pushUndo(); - doDelete(true); - e.cancel(); - } - } - if (code==KEY_ESCAPE){ - setMouseMode(MODE_SELECT); - mouseModeStr = "Select"; - tempMouseMode = mouseMode; - e.cancel(); - } - - if (e.getNativeEvent().getCtrlKey() || e.getNativeEvent().getMetaKey()) { - if (code==KEY_C) { - menuPerformed("key", "copy"); - e.cancel(); - } - if (code==KEY_X) { - menuPerformed("key", "cut"); - e.cancel(); - } - if (code==KEY_V) { - menuPerformed("key", "paste"); - e.cancel(); - } - if (code==KEY_Z) { - menuPerformed("key", "undo"); - e.cancel(); - } - if (code==KEY_Y) { - menuPerformed("key", "redo"); - e.cancel(); - } - if (code==KEY_D) { - menuPerformed("key", "duplicate"); - e.cancel(); - } - if (code==KEY_A) { - menuPerformed("key", "selectAll"); - e.cancel(); - } - if (code==KEY_P) { - menuPerformed("key", "print"); - e.cancel(); - } - if (code==KEY_N && isElectron()) { - menuPerformed("key", "newwindow"); - e.cancel(); - } - if (code==KEY_S) { - String cmd = "exportaslocalfile"; - if (isElectron()) - cmd = saveFileItem.isEnabled() ? "save" : "saveas"; - menuPerformed("key", cmd); - e.cancel(); - } - if (code==KEY_O) { - menuPerformed("key", "importfromlocalfile"); - e.cancel(); - } - } - } - if ((t&Event.ONKEYPRESS)!=0) { - if (cc>32 && cc<127){ - String c=shortcuts[cc]; - e.cancel(); - if (c==null) - return; - setMouseMode(MODE_ADD_ELM); - mouseModeStr=c; - tempMouseMode = mouseMode; - } - if (cc==32) { - setMouseMode(MODE_SELECT); - mouseModeStr = "Select"; - tempMouseMode = mouseMode; - e.cancel(); - } - } + if ((t & Event.ONKEYPRESS) != 0) { + if (cc == '-') { + menuPerformed("key", "zoomout"); + e.cancel(); + } + if (cc == '+' || cc == '=') { + menuPerformed("key", "zoomin"); + e.cancel(); + } + if (cc == '0') { + menuPerformed("key", "zoom100"); + e.cancel(); + } + if (cc == '/' && shortcuts['/'] == null) { + menuPerformed("key", "search"); + e.cancel(); + } + } + + // all other shortcuts are ignored when editing disabled + if (noEditCheckItem.getState()) + return; + + if ((t & Event.ONKEYDOWN) != 0) { + if (code == KEY_BACKSPACE || code == KEY_DELETE) { + if (scopeSelected != -1) { + // Treat DELETE key with scope selected as "remove scope", not delete + scopes[scopeSelected].setElm(null); + scopeSelected = -1; + } else { + menuElm = null; + pushUndo(); + doDelete(true); + e.cancel(); + } + } + if (code == KEY_ESCAPE) { + setMouseMode(MODE_SELECT); + mouseModeStr = "Select"; + tempMouseMode = mouseMode; + e.cancel(); + } + + if (e.getNativeEvent().getCtrlKey() || e.getNativeEvent().getMetaKey()) { + if (code == KEY_C) { + menuPerformed("key", "copy"); + e.cancel(); + } + if (code == KEY_X) { + menuPerformed("key", "cut"); + e.cancel(); + } + if (code == KEY_V) { + menuPerformed("key", "paste"); + e.cancel(); + } + if (code == KEY_Z) { + menuPerformed("key", "undo"); + e.cancel(); + } + if (code == KEY_Y) { + menuPerformed("key", "redo"); + e.cancel(); + } + if (code == KEY_D) { + menuPerformed("key", "duplicate"); + e.cancel(); + } + if (code == KEY_A) { + menuPerformed("key", "selectAll"); + e.cancel(); + } + if (code == KEY_P) { + menuPerformed("key", "print"); + e.cancel(); + } + if (code == KEY_N && isElectron()) { + menuPerformed("key", "newwindow"); + e.cancel(); + } + if (code == KEY_S) { + String cmd = "exportaslocalfile"; + if (isElectron()) + cmd = saveFileItem.isEnabled() ? "save" : "saveas"; + menuPerformed("key", cmd); + e.cancel(); + } + if (code == KEY_O) { + menuPerformed("key", "importfromlocalfile"); + e.cancel(); + } + } + } + if ((t & Event.ONKEYPRESS) != 0) { + if (cc > 32 && cc < 127) { + String c = shortcuts[cc]; + e.cancel(); + if (c == null) + return; + setMouseMode(MODE_ADD_ELM); + mouseModeStr = c; + tempMouseMode = mouseMode; + } + if (cc == 32) { + setMouseMode(MODE_SELECT); + mouseModeStr = "Select"; + tempMouseMode = mouseMode; + e.cancel(); + } + } } // factors a matrix into upper and lower triangular matrices by @@ -5620,446 +5634,560 @@ public void onPreviewNativeEvent(NativePreviewEvent e) { // matrix to be factored. ipvt[] returns an integer vector of pivot // indices, used in the lu_solve() routine. static boolean lu_factor(double a[][], int n, int ipvt[]) { - DMatrixSparseCSC dMatrixRMaj = DMatrixSparseCSC.convert(a,DMatrixSparseCSC.EPS); - return sparseLU.setA(dMatrixRMaj); + DMatrixSparseCSC dMatrixRMaj = DMatrixSparseCSC.convert(a, DMatrixSparseCSC.EPS); + return sparseLU.setA(dMatrixRMaj); } - final static SparseLU sparseLU = new SparseLU(); + final static SparseLU sparseLU = new SparseLU(); + // Solves the set of n linear equations using a LU factorization // previously performed by lu_factor. On input, b[0..n-1] is the right // hand side of the equations, and on output, contains the solution. static void lu_solve(double a[][], int n, int ipvt[], double b[]) { - sparseLU.solve(b); + sparseLU.solve(b); } void createNewLoadFile() { - // This is a hack to fix what IMHO is a bug in the () { - public void onFailure(Exception reason) { - Window.alert("Can't load canvas2svg.js."); - } - public void onSuccess(Void result) { - loadedCanvas2SVG = true; - if (followupAction.equals("doExportAsSVG")) { - doExportAsSVG(); - } else if (followupAction.equals("doExportAsSVGFromAPI")) { - doExportAsSVGFromAPI(); - } - } - }).inject(); - return false; - } - return true; - } + boolean initializeSVGScriptIfNecessary(final String followupAction) { + // load canvas2svg if we haven't already + if (!loadedCanvas2SVG) { + ScriptInjector.fromUrl("canvas2svg.js").setCallback(new Callback() { + public void onFailure(Exception reason) { + Window.alert("Can't load canvas2svg.js."); + } - void doExportAsSVG() { - if (!initializeSVGScriptIfNecessary("doExportAsSVG")) { - return; - } - dialogShowing = new ExportAsImageDialog(CAC_SVG); - dialogShowing.show(); - } + public void onSuccess(Void result) { + loadedCanvas2SVG = true; + if (followupAction.equals("doExportAsSVG")) { + doExportAsSVG(); + } else if (followupAction.equals("doExportAsSVGFromAPI")) { + doExportAsSVGFromAPI(); + } + } + }).inject(); + return false; + } + return true; + } - public void doExportAsSVGFromAPI() { - if (!initializeSVGScriptIfNecessary("doExportAsSVGFromAPI")) { - return; - } - String svg = getCircuitAsSVG(); - callSVGRenderedHook(svg); - } + void doExportAsSVG() { + if (!initializeSVGScriptIfNecessary("doExportAsSVG")) { + return; + } + dialogShowing = new ExportAsImageDialog(CAC_SVG); + dialogShowing.show(); + } - static final int CAC_PRINT = 0; - static final int CAC_IMAGE = 1; - static final int CAC_SVG = 2; - - public Canvas getCircuitAsCanvas(int type) { - // create canvas to draw circuit into - Canvas cv = Canvas.createIfSupported(); - Rectangle bounds = getCircuitBounds(); - - // add some space on edges because bounds calculation is not perfect - int wmargin = 140; - int hmargin = 100; - int w = (bounds.width*2+wmargin) ; - int h = (bounds.height*2+hmargin) ; - cv.setCoordinateSpaceWidth(w); - cv.setCoordinateSpaceHeight(h); - - Context2d context = cv.getContext2d(); - drawCircuitInContext(context, type, bounds, w, h); - return cv; - } + public void doExportAsSVGFromAPI() { + if (!initializeSVGScriptIfNecessary("doExportAsSVGFromAPI")) { + return; + } + String svg = getCircuitAsSVG(); + callSVGRenderedHook(svg); + } + + static final int CAC_PRINT = 0; + static final int CAC_IMAGE = 1; + static final int CAC_SVG = 2; + + public Canvas getCircuitAsCanvas(int type) { + // create canvas to draw circuit into + Canvas cv = Canvas.createIfSupported(); + Rectangle bounds = getCircuitBounds(); + + // add some space on edges because bounds calculation is not perfect + int wmargin = 140; + int hmargin = 100; + int w = (bounds.width * 2 + wmargin); + int h = (bounds.height * 2 + hmargin); + cv.setCoordinateSpaceWidth(w); + cv.setCoordinateSpaceHeight(h); - // create SVG context using canvas2svg - native static Context2d createSVGContext(int w, int h) /*-{ + Context2d context = cv.getContext2d(); + drawCircuitInContext(context, type, bounds, w, h); + return cv; + } + + // create SVG context using canvas2svg + native static Context2d createSVGContext(int w, int h) /*-{ return new C2S(w, h); }-*/; - native static String getSerializedSVG(Context2d context) /*-{ + native static String getSerializedSVG(Context2d context) /*-{ return context.getSerializedSvg(); }-*/; - public String getCircuitAsSVG() { - Rectangle bounds = getCircuitBounds(); - - // add some space on edges because bounds calculation is not perfect - int wmargin = 140; - int hmargin = 100; - int w = (bounds.width+wmargin) ; - int h = (bounds.height+hmargin) ; - Context2d context = createSVGContext(w, h); - drawCircuitInContext(context, CAC_SVG, bounds, w, h); - return getSerializedSVG(context); - } + public String getCircuitAsSVG() { + Rectangle bounds = getCircuitBounds(); - void drawCircuitInContext(Context2d context, int type, Rectangle bounds, int w, int h) { - Graphics g = new Graphics(context); - context.setTransform(1, 0, 0, 1, 0, 0); - double oldTransform[] = Arrays.copyOf(transform, 6); - - double scale = 1; - - // turn on white background, turn off current display - boolean p = printableCheckItem.getState(); - boolean c = dotsCheckItem.getState(); - boolean print = (type == CAC_PRINT); - if (print) - printableCheckItem.setState(true); - 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, w, h); - } - dotsCheckItem.setState(false); - - int wmargin = 140; - int hmargin = 100; - if (bounds != null) - scale = Math.min(w /(double)(bounds.width+wmargin), - h/(double)(bounds.height+hmargin)); - - // ScopeElms need the transform array to be updated - transform[0] = transform[3] = scale; - transform[4] = -(bounds.x-wmargin/2); - transform[5] = -(bounds.y-hmargin/2); - context.scale(scale, scale); - context.translate(transform[4], transform[5]); - context.setLineCap(Context2d.LineCap.ROUND); - - // draw elements - int i; - for (i = 0; i != elmList.size(); i++) { - getElm(i).draw(g); - } - for (i = 0; i != postDrawList.size(); i++) { - CircuitElm.drawPost(g, postDrawList.get(i)); - } - - // restore everything - printableCheckItem.setState(p); - dotsCheckItem.setState(c); - transform = oldTransform; - } + // add some space on edges because bounds calculation is not perfect + int wmargin = 140; + int hmargin = 100; + int w = (bounds.width + wmargin); + int h = (bounds.height + hmargin); + Context2d context = createSVGContext(w, h); + drawCircuitInContext(context, CAC_SVG, bounds, w, h); + return getSerializedSVG(context); + } - boolean isSelection() { - for (int i = 0; i != elmList.size(); i++) - if (getElm(i).isSelected()) - return true; - return false; - } + void drawCircuitInContext(Context2d context, int type, Rectangle bounds, int w, int h) { + Graphics g = new Graphics(context); + context.setTransform(1, 0, 0, 1, 0, 0); + double oldTransform[] = Arrays.copyOf(transform, 6); + + double scale = 1; + + // turn on white background, turn off current display + boolean p = printableCheckItem.getState(); + boolean c = dotsCheckItem.getState(); + boolean print = (type == CAC_PRINT); + if (print) + printableCheckItem.setState(true); + 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, w, h); + } + dotsCheckItem.setState(false); + + int wmargin = 140; + int hmargin = 100; + if (bounds != null) + scale = Math.min(w / (double) (bounds.width + wmargin), + h / (double) (bounds.height + hmargin)); + + // ScopeElms need the transform array to be updated + transform[0] = transform[3] = scale; + transform[4] = -(bounds.x - wmargin / 2); + transform[5] = -(bounds.y - hmargin / 2); + context.scale(scale, scale); + context.translate(transform[4], transform[5]); + context.setLineCap(Context2d.LineCap.ROUND); + + // draw elements + int i; + for (i = 0; i != elmList.size(); i++) { + getElm(i).draw(g); + } + for (i = 0; i != postDrawList.size(); i++) { + CircuitElm.drawPost(g, postDrawList.get(i)); + } - public CustomCompositeModel getCircuitAsComposite() { - int i; - String nodeDump = ""; - String dump = ""; + // restore everything + printableCheckItem.setState(p); + dotsCheckItem.setState(c); + transform = oldTransform; + } + + boolean isSelection() { + for (int i = 0; i != elmList.size(); i++) + if (getElm(i).isSelected()) + return true; + return false; + } + + public CustomCompositeModel getCircuitAsComposite() { + int i; + String nodeDump = ""; + String dump = ""; // String models = ""; - CustomLogicModel.clearDumpedFlags(); - DiodeModel.clearDumpedFlags(); - TransistorModel.clearDumpedFlags(); - Vector sideLabels[] = new Vector[] { + CustomLogicModel.clearDumpedFlags(); + DiodeModel.clearDumpedFlags(); + TransistorModel.clearDumpedFlags(); + Vector sideLabels[] = new Vector[]{ new Vector(), new Vector(), new Vector(), new Vector() - }; - Vector extList = new Vector(); - boolean sel = isSelection(); - - boolean used[] = new boolean[nodeList.size()]; - boolean extnodes[] = new boolean[nodeList.size()]; - - // find all the labeled nodes, get a list of them, and create a node number map - for (i = 0; i != elmList.size(); i++) { - CircuitElm ce = getElm(i); - if (sel && !ce.isSelected()) - continue; - if (ce instanceof LabeledNodeElm) { - LabeledNodeElm lne = (LabeledNodeElm) ce; - String label = lne.text; - if (lne.isInternal()) - continue; - - // already added to list? - if (extnodes[ce.getNode(0)]) - continue; - - int side = ChipElm.SIDE_W; - if (Math.abs(ce.dx) >= Math.abs(ce.dy) && ce.dx > 0) side = ChipElm.SIDE_E; - if (Math.abs(ce.dx) <= Math.abs(ce.dy) && ce.dy < 0) side = ChipElm.SIDE_N; - if (Math.abs(ce.dx) <= Math.abs(ce.dy) && ce.dy > 0) side = ChipElm.SIDE_S; - - // create ext list entry for external nodes - sideLabels[side].add(lne); - extnodes[ce.getNode(0)] = true; - } - } - - Collections.sort(sideLabels[ChipElm.SIDE_W], (LabeledNodeElm a, LabeledNodeElm b) -> Integer.signum(a.y - b.y)); - Collections.sort(sideLabels[ChipElm.SIDE_E], (LabeledNodeElm a, LabeledNodeElm b) -> Integer.signum(a.y - b.y)); - Collections.sort(sideLabels[ChipElm.SIDE_N], (LabeledNodeElm a, LabeledNodeElm b) -> Integer.signum(a.x - b.x)); - Collections.sort(sideLabels[ChipElm.SIDE_S], (LabeledNodeElm a, LabeledNodeElm b) -> Integer.signum(a.x - b.x)); - - for (int side = 0; side < sideLabels.length; side++) { - for (int pos = 0; pos < sideLabels[side].size(); pos++) { - LabeledNodeElm lne = sideLabels[side].get(pos); - ExtListEntry ent = new ExtListEntry(lne.text, lne.getNode(0), pos, side); - extList.add(ent); + }; + Vector extList = new Vector(); + boolean sel = isSelection(); + + boolean used[] = new boolean[nodeList.size()]; + boolean extnodes[] = new boolean[nodeList.size()]; + + // find all the labeled nodes, get a list of them, and create a node number map + for (i = 0; i != elmList.size(); i++) { + CircuitElm ce = getElm(i); + if (sel && !ce.isSelected()) + continue; + if (ce instanceof LabeledNodeElm) { + LabeledNodeElm lne = (LabeledNodeElm) ce; + String label = lne.text; + if (lne.isInternal()) + continue; + + // already added to list? + if (extnodes[ce.getNode(0)]) + continue; + + int side = ChipElm.SIDE_W; + if (Math.abs(ce.dx) >= Math.abs(ce.dy) && ce.dx > 0) side = ChipElm.SIDE_E; + if (Math.abs(ce.dx) <= Math.abs(ce.dy) && ce.dy < 0) side = ChipElm.SIDE_N; + if (Math.abs(ce.dx) <= Math.abs(ce.dy) && ce.dy > 0) side = ChipElm.SIDE_S; + + // create ext list entry for external nodes + sideLabels[side].add(lne); + extnodes[ce.getNode(0)] = true; + } + } + + Collections.sort(sideLabels[ChipElm.SIDE_W], (LabeledNodeElm a, LabeledNodeElm b) -> Integer.signum(a.y - b.y)); + Collections.sort(sideLabels[ChipElm.SIDE_E], (LabeledNodeElm a, LabeledNodeElm b) -> Integer.signum(a.y - b.y)); + Collections.sort(sideLabels[ChipElm.SIDE_N], (LabeledNodeElm a, LabeledNodeElm b) -> Integer.signum(a.x - b.x)); + Collections.sort(sideLabels[ChipElm.SIDE_S], (LabeledNodeElm a, LabeledNodeElm b) -> Integer.signum(a.x - b.x)); + + for (int side = 0; side < sideLabels.length; side++) { + for (int pos = 0; pos < sideLabels[side].size(); pos++) { + LabeledNodeElm lne = sideLabels[side].get(pos); + ExtListEntry ent = new ExtListEntry(lne.text, lne.getNode(0), pos, side); + extList.add(ent); + } + } + + // output all the elements + for (i = 0; i != elmList.size(); i++) { + CircuitElm ce = getElm(i); + if (sel && !ce.isSelected()) + continue; + // don't need these elements dumped + if (ce instanceof WireElm || ce instanceof LabeledNodeElm || ce instanceof ScopeElm) + continue; + if (ce instanceof GraphicElm || ce instanceof GroundElm) + continue; + int j; + if (nodeDump.length() > 0) + nodeDump += "\r"; + nodeDump += ce.getClass().getSimpleName(); + for (j = 0; j != ce.getPostCount(); j++) { + int n = ce.getNode(j); + used[n] = true; + nodeDump += " " + n; + } + + // save positions + int x1 = ce.x; + int y1 = ce.y; + int x2 = ce.x2; + int y2 = ce.y2; + + // set them to 0 so they're easy to remove + ce.x = ce.y = ce.x2 = ce.y2 = 0; + + String tstring = ce.dump(); + tstring = tstring.replaceFirst("[A-Za-z0-9]+ 0 0 0 0 ", ""); // remove unused tint_x1 y1 x2 y2 coords for internal components + + // restore positions + ce.x = x1; + ce.y = y1; + ce.x2 = x2; + ce.y2 = y2; + if (dump.length() > 0) + dump += " "; + dump += CustomLogicModel.escape(tstring); + } + + for (i = 0; i != extList.size(); i++) { + ExtListEntry ent = extList.get(i); + if (!used[ent.node]) { + Window.alert("Node \"" + ent.name + "\" is not used!"); + return null; + } + } + + boolean first = true; + for (i = 0; i != unconnectedNodes.size(); i++) { + int q = unconnectedNodes.get(i); + if (!extnodes[q] && used[q]) { + if (nodesWithGroundConnectionCount == 0 && first) { + first = false; + continue; } + Window.alert("Some nodes are unconnected!"); + return null; } + } - // output all the elements - for (i = 0; i != elmList.size(); i++) { - CircuitElm ce = getElm(i); - if (sel && !ce.isSelected()) - continue; - // don't need these elements dumped - if (ce instanceof WireElm || ce instanceof LabeledNodeElm || ce instanceof ScopeElm) - continue; - if (ce instanceof GraphicElm || ce instanceof GroundElm) - continue; - int j; - if (nodeDump.length() > 0) - nodeDump += "\r"; - nodeDump += ce.getClass().getSimpleName(); - for (j = 0; j != ce.getPostCount(); j++) { - int n = ce.getNode(j); - used[n] = true; - nodeDump += " " + n; - } - - // save positions - int x1 = ce.x; int y1 = ce.y; - int x2 = ce.x2; int y2 = ce.y2; - - // set them to 0 so they're easy to remove - ce.x = ce.y = ce.x2 = ce.y2 = 0; - - String tstring = ce.dump(); - tstring = tstring.replaceFirst("[A-Za-z0-9]+ 0 0 0 0 ", ""); // remove unused tint_x1 y1 x2 y2 coords for internal components - - // restore positions - ce.x = x1; ce.y = y1; ce.x2 = x2; ce.y2 = y2; - if (dump.length() > 0) - dump += " "; - dump += CustomLogicModel.escape(tstring); - } - - for (i = 0; i != extList.size(); i++) { - ExtListEntry ent = extList.get(i); - if (!used[ent.node]) { - Window.alert("Node \"" + ent.name + "\" is not used!"); - return null; - } - } - - boolean first = true; - for (i = 0; i != unconnectedNodes.size(); i++) { - int q = unconnectedNodes.get(i); - if (!extnodes[q] && used[q]) { - if (nodesWithGroundConnectionCount == 0 && first) { - first = false; - continue; - } - Window.alert("Some nodes are unconnected!"); - return null; - } - } - - CustomCompositeModel ccm = new CustomCompositeModel(); - ccm.nodeList = nodeDump; - ccm.elmDump = dump; - ccm.extList = extList; - return ccm; - } + CustomCompositeModel ccm = new CustomCompositeModel(); + ccm.nodeList = nodeDump; + ccm.elmDump = dump; + ccm.extList = extList; + return ccm; + } + + static void invertMatrix(double a[][], int n) { + int ipvt[] = new int[n]; + lu_factor(a, n, ipvt); + int i, j; + double b[] = new double[n]; + double inva[][] = new double[n][n]; + + // solve for each column of identity matrix + for (i = 0; i != n; i++) { + for (j = 0; j != n; j++) + b[j] = 0; + b[i] = 1; + lu_solve(a, n, ipvt, b); + for (j = 0; j != n; j++) + inva[j][i] = b[j]; + } - static void invertMatrix(double a[][], int n) { - int ipvt[] = new int[n]; - lu_factor(a, n, ipvt); - int i, j; - double b[] = new double[n]; - double inva[][] = new double[n][n]; - - // solve for each column of identity matrix - for (i = 0; i != n; i++) { - for (j = 0; j != n; j++) - b[j] = 0; - b[i] = 1; - lu_solve(a, n, ipvt, b); - for (j = 0; j != n; j++) - inva[j][i] = b[j]; - } - - // return in original matrix - for (i = 0; i != n; i++) - for (j = 0; j != n; j++) - a[i][j] = inva[i][j]; - } + // return in original matrix + for (i = 0; i != n; i++) + for (j = 0; j != n; j++) + a[i][j] = inva[i][j]; + } - double getLabeledNodeVoltage(String name) { - Integer node = LabeledNodeElm.getByName(name); - if (node == null || node == 0) - return 0; - // subtract one because ground is not included in nodeVoltages[] - return nodeVoltages[node.intValue()-1]; - } + double getLabeledNodeVoltage(String name) { + Integer node = LabeledNodeElm.getByName(name); + if (node == null || node == 0) + return 0; + // subtract one because ground is not included in nodeVoltages[] + return nodeVoltages[node.intValue() - 1]; + } - void setExtVoltage(String name, double v) { - int i; - for (i = 0; i != elmList.size(); i++) { - CircuitElm ce = getElm(i); - if (ce instanceof ExtVoltageElm) { - ExtVoltageElm eve = (ExtVoltageElm) ce; - if (eve.getName().equals(name)) - eve.setVoltage(v); - } - } - } + void setExtVoltage(String name, double v) { + int i; + for (i = 0; i != elmList.size(); i++) { + CircuitElm ce = getElm(i); + if (ce instanceof ExtVoltageElm) { + ExtVoltageElm eve = (ExtVoltageElm) ce; + if (eve.getName().equals(name)) + eve.setVoltage(v); + } + } + } - native JsArray getJSArray() /*-{ return []; }-*/; - - JsArray getJSElements() { - int i; - JsArray arr = getJSArray(); - for (i = 0; i != elmList.size(); i++) { - CircuitElm ce = getElm(i); - ce.addJSMethods(); - arr.push(ce.getJavaScriptObject()); - } - return arr; - } + native JsArray getJSArray() /*-{ return []; }-*/; - native void setupJSInterface() /*-{ + JsArray getJSElements() { + int i; + JsArray arr = getJSArray(); + for (i = 0; i != elmList.size(); i++) { + CircuitElm ce = getElm(i); + ce.addJSMethods(); + arr.push(ce.getJavaScriptObject()); + } + return arr; + } + + native void setupJSInterface() /*-{ var that = this; $wnd.CircuitJS1 = { setSimRunning: $entry(function(run) { that.@com.lushprojects.circuitjs1.client.CirSim::setSimRunning(Z)(run); } ), @@ -6490,41 +6624,42 @@ native void setupJSInterface() /*-{ hook($wnd.CircuitJS1); }-*/; - native void callUpdateHook() /*-{ + native void callUpdateHook() /*-{ var hook = $wnd.CircuitJS1.onupdate; if (hook) hook($wnd.CircuitJS1); }-*/; - native void callAnalyzeHook() /*-{ + native void callAnalyzeHook() /*-{ var hook = $wnd.CircuitJS1.onanalyze; if (hook) hook($wnd.CircuitJS1); }-*/; - native void callTimeStepHook() /*-{ + native void callTimeStepHook() /*-{ var hook = $wnd.CircuitJS1.ontimestep; if (hook) hook($wnd.CircuitJS1); }-*/; - native void callSVGRenderedHook(String svgData) /*-{ + native void callSVGRenderedHook(String svgData) /*-{ var hook = $wnd.CircuitJS1.onsvgrendered; if (hook) hook($wnd.CircuitJS1, svgData); }-*/; - class UndoItem { - public String dump; - public double scale, transform4, transform5; - UndoItem(String d) { - dump = d; - scale = transform[0]; - transform4 = transform[4]; - transform5 = transform[5]; - } - } + class UndoItem { + public String dump; + public double scale, transform4, transform5; + + UndoItem(String d) { + dump = d; + scale = transform[0]; + transform4 = transform[4]; + transform5 = transform[5]; + } + } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/CircuitElm.java b/src/main/java/com/lushprojects/circuitjs1/client/CircuitElm.java index c6bb39b..d16e6c1 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/CircuitElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/CircuitElm.java @@ -37,10 +37,10 @@ public abstract class CircuitElm implements Editable { static int colorScaleCount = 201; // odd so ground = gray static Color colorScale[]; static double currentMult, powerMult; - + // scratch points for convenience static Point ps1, ps2; - + static CirSim sim; static public Color whiteColor, lightGrayColor, selectColor; static public Color positiveColor, negativeColor, neutralColor, currentColor; @@ -54,275 +54,302 @@ public abstract class CircuitElm implements Editable { static final int SCALE_1 = 1; static final int SCALE_M = 2; static final int SCALE_MU = 3; - + static int decimalDigits, shortDecimalDigits; - + // initial point where user created element. For simple two-terminal elements, this is the first node/post. int x, y; - + // point to which user dragged out element. For simple two-terminal elements, this is the second node/post int x2, y2; - + int flags, nodes[], voltSource; - + // length along x and y axes, and sign of difference int dx, dy, dsign; - int lastHandleGrabbed=-1; - + int lastHandleGrabbed = -1; + // length of element double dn; - + double dpx1, dpy1; - + // (x,y) and (x2,y2) as Point objects Point point1, point2; - + // lead points (ends of wire stubs for simple two-terminal elements) Point lead1, lead2; - + // voltages at each node double volts[]; - + double current, curcount; Rectangle boundingBox; - + // if subclasses set this to true, element will be horizontal or vertical only boolean noDiagonal; - + public boolean selected; - + boolean hasWireInfo; // used in calcWireInfo() - -// abstract int getDumpType(); + + // abstract int getDumpType(); int getDumpType() { - - throw new IllegalStateException(); // Seems necessary to work-around what appears to be a compiler - // bug affecting OTAElm to make sure this method (which should really be abstract) throws - // an exception. If you're getting this, try making small update to CompositeElm.java and try again - } - + + throw new IllegalStateException(); // Seems necessary to work-around what appears to be a compiler + // bug affecting OTAElm to make sure this method (which should really be abstract) throws + // an exception. If you're getting this, try making small update to CompositeElm.java and try again + } + // leftover from java, doesn't do anything anymore. - Class getDumpClass() { return getClass(); } - - int getDefaultFlags() { return 0; } + Class getDumpClass() { + return getClass(); + } + + int getDefaultFlags() { + return 0; + } + + boolean hasFlag(int f) { + return (flags & f) != 0; + } - boolean hasFlag(int f) { return (flags & f) != 0; } - static void initClass(CirSim s) { - unitsFont = new Font("SansSerif", 0, 12); - sim = s; - - colorScale = new Color[colorScaleCount]; - - - ps1 = new Point(); - ps2 = new Point(); - - Storage stor = Storage.getLocalStorageIfSupported(); - decimalDigits = 3; - shortDecimalDigits = 1; - if (stor != null) { - String s1 = stor.getItem("decimalDigits"); - String s2 = stor.getItem("decimalDigitsShort"); - if (s1 != null) - decimalDigits = Integer.parseInt(s1); - if (s2 != null) - shortDecimalDigits = Integer.parseInt(s2); - } - setDecimalDigits(decimalDigits, false, false); - setDecimalDigits(shortDecimalDigits, true, false); + unitsFont = new Font("SansSerif", 0, 12); + sim = s; + + colorScale = new Color[colorScaleCount]; + + + ps1 = new Point(); + ps2 = new Point(); + + Storage stor = Storage.getLocalStorageIfSupported(); + decimalDigits = 3; + shortDecimalDigits = 1; + if (stor != null) { + String s1 = stor.getItem("decimalDigits"); + String s2 = stor.getItem("decimalDigitsShort"); + if (s1 != null) + decimalDigits = Integer.parseInt(s1); + if (s2 != null) + shortDecimalDigits = Integer.parseInt(s2); + } + setDecimalDigits(decimalDigits, false, false); + setDecimalDigits(shortDecimalDigits, true, false); } static void setDecimalDigits(int num, boolean sf, boolean save) { - if (sf) - shortDecimalDigits = num; - else - decimalDigits = num; - - String s = "####."; - int ct = num; - for (; ct > 0; ct--) - s += '#'; - NumberFormat nf = NumberFormat.getFormat(s); - if (sf) - shortFormat = nf; - else - showFormat = nf; - - if (save) { - Storage stor = Storage.getLocalStorageIfSupported(); - if (stor != null) - stor.setItem(sf ? "decimalDigitsShort" : "decimalDigits", Integer.toString(num)); - } - - if (!sf) { - s = "####."; - ct = num; - for (; ct > 0; ct--) - s += '0'; - fixedFormat = NumberFormat.getFormat(s); - } + if (sf) + shortDecimalDigits = num; + else + decimalDigits = num; + + String s = "####."; + int ct = num; + for (; ct > 0; ct--) + s += '#'; + NumberFormat nf = NumberFormat.getFormat(s); + if (sf) + shortFormat = nf; + else + showFormat = nf; + + if (save) { + Storage stor = Storage.getLocalStorageIfSupported(); + if (stor != null) + stor.setItem(sf ? "decimalDigitsShort" : "decimalDigits", Integer.toString(num)); + } + + if (!sf) { + s = "####."; + ct = num; + for (; ct > 0; ct--) + s += '0'; + fixedFormat = NumberFormat.getFormat(s); + } } - + static void setColorScale() { - int i; - - if (positiveColor == null) - positiveColor = Color.green; - if (negativeColor == null) - negativeColor = Color.red; - if (neutralColor == null) - neutralColor = Color.gray; - - for (i = 0; i != colorScaleCount; i++) { - double v = i * 2. / colorScaleCount - 1; - if (v < 0) { - colorScale[i] = new Color(neutralColor, negativeColor, -v); - } else { - colorScale[i] = new Color(neutralColor, positiveColor, v); - } - } + int i; + + if (positiveColor == null) + positiveColor = Color.green; + if (negativeColor == null) + negativeColor = Color.red; + if (neutralColor == null) + neutralColor = Color.gray; + + for (i = 0; i != colorScaleCount; i++) { + double v = i * 2. / colorScaleCount - 1; + if (v < 0) { + colorScale[i] = new Color(neutralColor, negativeColor, -v); + } else { + colorScale[i] = new Color(neutralColor, positiveColor, v); + } + } } - + // create new element with one post at xx,yy, to be dragged out by user CircuitElm(int xx, int yy) { - x = x2 = xx; - y = y2 = yy; - flags = getDefaultFlags(); - allocNodes(); - initBoundingBox(); + x = x2 = xx; + y = y2 = yy; + flags = getDefaultFlags(); + allocNodes(); + initBoundingBox(); } - + // create element between xa,ya and xb,yb from undump CircuitElm(int xa, int ya, int xb, int yb, int f) { - x = xa; y = ya; x2 = xb; y2 = yb; flags = f; - allocNodes(); - initBoundingBox(); + x = xa; + y = ya; + x2 = xb; + y2 = yb; + flags = f; + allocNodes(); + initBoundingBox(); } - + void initBoundingBox() { - boundingBox = new Rectangle(); - boundingBox.setBounds(min(x, x2), min(y, y2), - abs(x2-x)+1, abs(y2-y)+1); + boundingBox = new Rectangle(); + boundingBox.setBounds(min(x, x2), min(y, y2), + abs(x2 - x) + 1, abs(y2 - y) + 1); } - + // allocate nodes/volts arrays we need void allocNodes() { - int n = getPostCount() + getInternalNodeCount(); - // preserve voltages if possible - if (nodes == null || nodes.length != n) { - nodes = new int[n]; - volts = new double[n]; - } + int n = getPostCount() + getInternalNodeCount(); + // preserve voltages if possible + if (nodes == null || nodes.length != n) { + nodes = new int[n]; + volts = new double[n]; + } } - + // dump component state for export/undo String dump() { - int t = getDumpType(); - return (t < 127 ? ((char)t)+" " : t+" ") + x + " " + y + " " + - x2 + " " + y2 + " " + flags; + int t = getDumpType(); + return (t < 127 ? ((char) t) + " " : t + " ") + x + " " + y + " " + + x2 + " " + y2 + " " + flags; } - + // handle reset button void reset() { - int i; - for (i = 0; i != getPostCount()+getInternalNodeCount(); i++) - volts[i] = 0; - curcount = 0; + int i; + for (i = 0; i != getPostCount() + getInternalNodeCount(); i++) + volts[i] = 0; + curcount = 0; } - void draw(Graphics g) {} - + + void draw(Graphics g) { + } + // set current for voltage source vn to c. vn will be the same value as in a previous call to setVoltageSource(n, vn) - void setCurrent(int vn, double c) { current = c; } - + void setCurrent(int vn, double c) { + current = c; + } + // get current for one- or two-terminal elements - double getCurrent() { return current; } + double getCurrent() { + return current; + } + + void setParentList(Vector elmList) { + } - void setParentList(Vector elmList) {} - // stamp matrix values for linear elements. // for non-linear elements, use this to stamp values that don't change each iteration, and call stampRightSide() or stampNonLinear() as needed - void stamp() {} - + void stamp() { + } + // stamp matrix values for non-linear elements - void doStep() {} - + void doStep() { + } + void delete() { - if (mouseElmRef==this) - mouseElmRef=null; - sim.deleteSliders(this); + if (mouseElmRef == this) + mouseElmRef = null; + sim.deleteSliders(this); } - void startIteration() {} - + + void startIteration() { + } + // get voltage of x'th node - double getPostVoltage(int x) { return volts[x]; } - + double getPostVoltage(int x) { + return volts[x]; + } + // set voltage of x'th node, called by simulator logic void setNodeVoltage(int n, double c) { - volts[n] = c; - calculateCurrent(); + volts[n] = c; + calculateCurrent(); } - + // calculate current in response to node voltages changing - void calculateCurrent() {} - + void calculateCurrent() { + } + // calculate post locations and other convenience values used for drawing. Called when element is moved void setPoints() { - dx = x2-x; dy = y2-y; - dn = Math.sqrt(dx*dx+dy*dy); - dpx1 = dy/dn; - dpy1 = -dx/dn; - dsign = (dy == 0) ? sign(dx) : sign(dy); - point1 = new Point(x , y ); - point2 = new Point(x2, y2); + dx = x2 - x; + dy = y2 - y; + dn = Math.sqrt(dx * dx + dy * dy); + dpx1 = dy / dn; + dpy1 = -dx / dn; + dsign = (dy == 0) ? sign(dx) : sign(dy); + point1 = new Point(x, y); + point2 = new Point(x2, y2); } - + // calculate lead points for an element of length len. Handy for simple two-terminal elements. // Posts are where the user connects wires; leads are ends of wire stubs drawn inside the element. void calcLeads(int len) { - if (dn < len || len == 0) { - lead1 = point1; - lead2 = point2; - return; - } - lead1 = interpPoint(point1, point2, (dn-len)/(2*dn)); - lead2 = interpPoint(point1, point2, (dn+len)/(2*dn)); + if (dn < len || len == 0) { + lead1 = point1; + lead2 = point2; + return; + } + lead1 = interpPoint(point1, point2, (dn - len) / (2 * dn)); + lead2 = interpPoint(point1, point2, (dn + len) / (2 * dn)); } // calculate point fraction f between a and b, linearly interpolated Point interpPoint(Point a, Point b, double f) { - Point p = new Point(); - interpPoint(a, b, p, f); - return p; + Point p = new Point(); + interpPoint(a, b, p, f); + return p; } - + // calculate point fraction f between a and b, linearly interpolated, return it in c void interpPoint(Point a, Point b, Point c, double f) { - c.x = (int) Math.floor(a.x*(1-f)+b.x*f+.48); - c.y = (int) Math.floor(a.y*(1-f)+b.y*f+.48); + c.x = (int) Math.floor(a.x * (1 - f) + b.x * f + .48); + c.y = (int) Math.floor(a.y * (1 - f) + b.y * f + .48); } - + /** * Returns a point fraction f along the line between a and b and offset perpendicular by g + * * @param a 1st Point * @param b 2nd Point * @param f Fraction along line * @param g Fraction perpendicular to line - * Returns interpolated point in c + * Returns interpolated point in c */ void interpPoint(Point a, Point b, Point c, double f, double g) { - int gx = b.y-a.y; - int gy = a.x-b.x; - g /= Math.sqrt(gx*gx+gy*gy); - c.x = (int) Math.floor(a.x*(1-f)+b.x*f+g*gx+.48); - c.y = (int) Math.floor(a.y*(1-f)+b.y*f+g*gy+.48); + int gx = b.y - a.y; + int gy = a.x - b.x; + g /= Math.sqrt(gx * gx + gy * gy); + c.x = (int) Math.floor(a.x * (1 - f) + b.x * f + g * gx + .48); + c.y = (int) Math.floor(a.y * (1 - f) + b.y * f + g * gy + .48); } - + /** * Returns a point fraction f along the line between a and b and offset perpendicular by g + * * @param a 1st Point * @param b 2nd Point * @param f Fraction along line @@ -330,14 +357,15 @@ void interpPoint(Point a, Point b, Point c, double f, double g) { * @return Interpolated point */ Point interpPoint(Point a, Point b, double f, double g) { - Point p = new Point(); - interpPoint(a, b, p, f, g); - return p; + Point p = new Point(); + interpPoint(a, b, p, f, g); + return p; } - - + + /** * Calculates two points fraction f along the line between a and b and offest perpendicular by +/-g + * * @param a 1st point (In) * @param b 2nd point (In) * @param c 1st point (Out) @@ -348,279 +376,303 @@ Point interpPoint(Point a, Point b, double f, double g) { void interpPoint2(Point a, Point b, Point c, Point d, double f, double g) { // int xpd = b.x-a.x; // int ypd = b.y-a.y; - int gx = b.y-a.y; - int gy = a.x-b.x; - g /= Math.sqrt(gx*gx+gy*gy); - c.x = (int) Math.floor(a.x*(1-f)+b.x*f+g*gx+.48); - c.y = (int) Math.floor(a.y*(1-f)+b.y*f+g*gy+.48); - d.x = (int) Math.floor(a.x*(1-f)+b.x*f-g*gx+.48); - d.y = (int) Math.floor(a.y*(1-f)+b.y*f-g*gy+.48); + int gx = b.y - a.y; + int gy = a.x - b.x; + g /= Math.sqrt(gx * gx + gy * gy); + c.x = (int) Math.floor(a.x * (1 - f) + b.x * f + g * gx + .48); + c.y = (int) Math.floor(a.y * (1 - f) + b.y * f + g * gy + .48); + d.x = (int) Math.floor(a.x * (1 - f) + b.x * f - g * gx + .48); + d.y = (int) Math.floor(a.y * (1 - f) + b.y * f - g * gy + .48); } - + void draw2Leads(Graphics g) { - // draw first lead - setVoltageColor(g, volts[0]); - drawThickLine(g, point1, lead1); + // draw first lead + setVoltageColor(g, volts[0]); + drawThickLine(g, point1, lead1); - // draw second lead - setVoltageColor(g, volts[1]); - drawThickLine(g, lead2, point2); + // draw second lead + setVoltageColor(g, volts[1]); + drawThickLine(g, lead2, point2); } - Point [] newPointArray(int n) { - Point a[] = new Point[n]; - while (n > 0) - a[--n] = new Point(); - return a; + + Point[] newPointArray(int n) { + Point a[] = new Point[n]; + while (n > 0) + a[--n] = new Point(); + return a; } final int CURRENT_TOO_FAST = 100; // draw current dots from point a to b void drawDots(Graphics g, Point pa, Point pb, double pos) { - if ((!sim.simIsRunning()) || pos == 0 || !sim.dotsCheckItem.getState()) - return; - int dx = pb.x-pa.x; - int dy = pb.y-pa.y; - double dn = Math.sqrt(dx*dx+dy*dy); - g.setColor(currentColor); - int ds = 16; - if (pos == CURRENT_TOO_FAST || pos == -CURRENT_TOO_FAST) { - // current is moving too fast, avoid aliasing by drawing dots at - // random position with transparent yellow line underneath - g.save(); - Context2d ctx = g.context; - ctx.setLineWidth(4); - ctx.setGlobalAlpha(.5); - ctx.beginPath(); - ctx.moveTo(pa.x, pa.y); - ctx.lineTo(pb.x, pb.y); - ctx.stroke(); - g.restore(); - pos = Random.nextDouble()*ds; - } - pos %= ds; - if (pos < 0) - pos += ds; - double di = 0; - for (di = pos; di < dn; di += ds) { - int x0 = (int) (pa.x+di*dx/dn); - int y0 = (int) (pa.y+di*dy/dn); - g.fillRect(x0-2, y0-2, 4, 4); - } + if ((!sim.simIsRunning()) || pos == 0 || !sim.dotsCheckItem.getState()) + return; + int dx = pb.x - pa.x; + int dy = pb.y - pa.y; + double dn = Math.sqrt(dx * dx + dy * dy); + g.setColor(currentColor); + int ds = 16; + if (pos == CURRENT_TOO_FAST || pos == -CURRENT_TOO_FAST) { + // current is moving too fast, avoid aliasing by drawing dots at + // random position with transparent yellow line underneath + g.save(); + Context2d ctx = g.context; + ctx.setLineWidth(4); + ctx.setGlobalAlpha(.5); + ctx.beginPath(); + ctx.moveTo(pa.x, pa.y); + ctx.lineTo(pb.x, pb.y); + ctx.stroke(); + g.restore(); + pos = Random.nextDouble() * ds; + } + pos %= ds; + if (pos < 0) + pos += ds; + double di = 0; + for (di = pos; di < dn; di += ds) { + int x0 = (int) (pa.x + di * dx / dn); + int y0 = (int) (pa.y + di * dy / dn); + g.fillRect(x0 - 2, y0 - 2, 4, 4); + } } double addCurCount(double c, double a) { - if (c == CURRENT_TOO_FAST || c == -CURRENT_TOO_FAST) - return c; - return c+a; + if (c == CURRENT_TOO_FAST || c == -CURRENT_TOO_FAST) + return c; + return c + a; } - + Polygon calcArrow(Point a, Point b, double al, double aw) { - Polygon poly = new Polygon(); - Point p1 = new Point(); - Point p2 = new Point(); - int adx = b.x-a.x; - int ady = b.y-a.y; - double l = Math.sqrt(adx*adx+ady*ady); - poly.addPoint(b.x, b.y); - interpPoint2(a, b, p1, p2, 1-al/l, aw); - poly.addPoint(p1.x, p1.y); - poly.addPoint(p2.x, p2.y); - return poly; + Polygon poly = new Polygon(); + Point p1 = new Point(); + Point p2 = new Point(); + int adx = b.x - a.x; + int ady = b.y - a.y; + double l = Math.sqrt(adx * adx + ady * ady); + poly.addPoint(b.x, b.y); + interpPoint2(a, b, p1, p2, 1 - al / l, aw); + poly.addPoint(p1.x, p1.y); + poly.addPoint(p2.x, p2.y); + return poly; } + Polygon createPolygon(Point a, Point b, Point c) { - Polygon p = new Polygon(); - p.addPoint(a.x, a.y); - p.addPoint(b.x, b.y); - p.addPoint(c.x, c.y); - return p; + Polygon p = new Polygon(); + p.addPoint(a.x, a.y); + p.addPoint(b.x, b.y); + p.addPoint(c.x, c.y); + return p; } + Polygon createPolygon(Point a, Point b, Point c, Point d) { - Polygon p = new Polygon(); - p.addPoint(a.x, a.y); - p.addPoint(b.x, b.y); - p.addPoint(c.x, c.y); - p.addPoint(d.x, d.y); - return p; + Polygon p = new Polygon(); + p.addPoint(a.x, a.y); + p.addPoint(b.x, b.y); + p.addPoint(c.x, c.y); + p.addPoint(d.x, d.y); + return p; } + Polygon createPolygon(Point a[]) { - Polygon p = new Polygon(); - int i; - for (i = 0; i != a.length; i++) - p.addPoint(a[i].x, a[i].y); - return p; + Polygon p = new Polygon(); + int i; + for (i = 0; i != a.length; i++) + p.addPoint(a[i].x, a[i].y); + return p; } - + // draw second point to xx, yy void drag(int xx, int yy) { - xx = sim.snapGrid(xx); - yy = sim.snapGrid(yy); - if (noDiagonal) { - if (Math.abs(x-xx) < Math.abs(y-yy)) { - xx = x; - } else { - yy = y; - } - } - x2 = xx; y2 = yy; - setPoints(); + xx = sim.snapGrid(xx); + yy = sim.snapGrid(yy); + if (noDiagonal) { + if (Math.abs(x - xx) < Math.abs(y - yy)) { + xx = x; + } else { + yy = y; + } + } + x2 = xx; + y2 = yy; + setPoints(); } - + void move(int dx, int dy) { - x += dx; y += dy; x2 += dx; y2 += dy; - boundingBox.translate(dx, dy); - setPoints(); + x += dx; + y += dy; + x2 += dx; + y2 += dy; + boundingBox.translate(dx, dy); + setPoints(); } // called when an element is done being dragged out; returns true if it's zero size and should be deleted boolean creationFailed() { - return (x == x2 && y == y2); + return (x == x2 && y == y2); } // this is used to set the position of an internal element so we can draw it inside the parent void setPosition(int x_, int y_, int x2_, int y2_) { - x = x_; - y = y_; - x2 = x2_; - y2 = y2_; - setPoints(); + x = x_; + y = y_; + x2 = x2_; + y2 = y2_; + setPoints(); } - + // determine if moving this element by (dx,dy) will put it on top of another element boolean allowMove(int dx, int dy) { - int nx = x+dx; - int ny = y+dy; - int nx2 = x2+dx; - int ny2 = y2+dy; - int i; - for (i = 0; i != sim.elmList.size(); i++) { - CircuitElm ce = sim.getElm(i); - if (ce.x == nx && ce.y == ny && ce.x2 == nx2 && ce.y2 == ny2) - return false; - if (ce.x == nx2 && ce.y == ny2 && ce.x2 == nx && ce.y2 == ny) - return false; - } - return true; + int nx = x + dx; + int ny = y + dy; + int nx2 = x2 + dx; + int ny2 = y2 + dy; + int i; + for (i = 0; i != sim.elmList.size(); i++) { + CircuitElm ce = sim.getElm(i); + if (ce.x == nx && ce.y == ny && ce.x2 == nx2 && ce.y2 == ny2) + return false; + if (ce.x == nx2 && ce.y == ny2 && ce.x2 == nx && ce.y2 == ny) + return false; + } + return true; } - + void movePoint(int n, int dx, int dy) { - // modified by IES to prevent the user dragging points to create zero sized nodes - // that then render improperly - int oldx=x; - int oldy=y; - int oldx2=x2; - int oldy2=y2; - if (noDiagonal) { - if (x == x2) - dx = 0; - else - dy = 0; - } - if (n == 0) { - x += dx; y += dy; - } else { - x2 += dx; y2 += dy; - } - if (x==x2 && y==y2) { - x=oldx; - y=oldy; - x2=oldx2; - y2=oldy2; - } - setPoints(); + // modified by IES to prevent the user dragging points to create zero sized nodes + // that then render improperly + int oldx = x; + int oldy = y; + int oldx2 = x2; + int oldy2 = y2; + if (noDiagonal) { + if (x == x2) + dx = 0; + else + dy = 0; + } + if (n == 0) { + x += dx; + y += dy; + } else { + x2 += dx; + y2 += dy; + } + if (x == x2 && y == y2) { + x = oldx; + y = oldy; + x2 = oldx2; + y2 = oldy2; + } + setPoints(); } - + void drawPosts(Graphics g) { - // we normally do this in updateCircuit() now because the logic is more complicated. - // we only handle the case where we have to draw all the posts. That happens when - // this element is selected or is being created - if (sim.dragElm == null && !needsHighlight()) - return; - if (sim.mouseMode == CirSim.MODE_DRAG_ROW || - sim.mouseMode == CirSim.MODE_DRAG_COLUMN) - return; - int i; - for (i = 0; i != getPostCount(); i++) { - Point p = getPost(i); - drawPost(g, p); - } + // we normally do this in updateCircuit() now because the logic is more complicated. + // we only handle the case where we have to draw all the posts. That happens when + // this element is selected or is being created + if (sim.dragElm == null && !needsHighlight()) + return; + if (sim.mouseMode == CirSim.MODE_DRAG_ROW || + sim.mouseMode == CirSim.MODE_DRAG_COLUMN) + return; + int i; + for (i = 0; i != getPostCount(); i++) { + Point p = getPost(i); + drawPost(g, p); + } } - + int getNumHandles() { - return getPostCount(); + return getPostCount(); } - + void drawHandles(Graphics g, Color c) { - g.setColor(c); - if (lastHandleGrabbed==-1) - g.fillRect(x-3, y-3, 7, 7); - else if (lastHandleGrabbed==0) - g.fillRect(x-4, y-4, 9, 9); - if (getNumHandles() > 1) { - if (lastHandleGrabbed==-1) - g.fillRect(x2-3, y2-3, 7, 7); - else if (lastHandleGrabbed==1) - g.fillRect(x2-4, y2-4, 9, 9); - } + g.setColor(c); + if (lastHandleGrabbed == -1) + g.fillRect(x - 3, y - 3, 7, 7); + else if (lastHandleGrabbed == 0) + g.fillRect(x - 4, y - 4, 9, 9); + if (getNumHandles() > 1) { + if (lastHandleGrabbed == -1) + g.fillRect(x2 - 3, y2 - 3, 7, 7); + else if (lastHandleGrabbed == 1) + g.fillRect(x2 - 4, y2 - 4, 9, 9); + } } - + int getHandleGrabbedClose(int xtest, int ytest, int deltaSq, int minSize) { - lastHandleGrabbed=-1; - if ( Graphics.distanceSq(x , y , x2, y2)>=minSize) { - if (Graphics.distanceSq(x, y, xtest,ytest) <= deltaSq) - lastHandleGrabbed=0; - else if (getNumHandles() > 1 && Graphics.distanceSq(x2, y2, xtest,ytest) <= deltaSq) - lastHandleGrabbed=1; - } - return lastHandleGrabbed; + lastHandleGrabbed = -1; + if (Graphics.distanceSq(x, y, x2, y2) >= minSize) { + if (Graphics.distanceSq(x, y, xtest, ytest) <= deltaSq) + lastHandleGrabbed = 0; + else if (getNumHandles() > 1 && Graphics.distanceSq(x2, y2, xtest, ytest) <= deltaSq) + lastHandleGrabbed = 1; + } + return lastHandleGrabbed; } - + // number of voltage sources this element needs - int getVoltageSourceCount() { return 0; } - + int getVoltageSourceCount() { + return 0; + } + // number of internal nodes (nodes not visible in UI that are needed for implementation) - int getInternalNodeCount() { return 0; } - + int getInternalNodeCount() { + return 0; + } + // notify this element that its pth node is n. This value n can be passed to stampMatrix() - void setNode(int p, int n) { nodes[p] = n; } - + void setNode(int p, int n) { + nodes[p] = n; + } + // notify this element that its nth voltage source is v. This value v can be passed to stampVoltageSource(), etc and will be passed back in calls to setCurrent() void setVoltageSource(int n, int v) { - // default implementation only makes sense for subclasses with one voltage source. If we have 0 this isn't used, if we have >1 this won't work - voltSource = v; + // default implementation only makes sense for subclasses with one voltage source. If we have 0 this isn't used, if we have >1 this won't work + voltSource = v; } - + // int getVoltageSource() { return voltSource; } // Never used except for debug code which is commented out - + double getVoltageDiff() { - return volts[0] - volts[1]; + return volts[0] - volts[1]; } - boolean nonLinear() { return false; } - int getPostCount() { return 2; } - + + boolean nonLinear() { + return false; + } + + int getPostCount() { + return 2; + } + // get (global) node number of nth node - int getNode(int n) { return nodes[n]; } - + int getNode(int n) { + return nodes[n]; + } + // get position of nth node Point getPost(int n) { - return (n == 0) ? point1 : (n == 1) ? point2 : null; + return (n == 0) ? point1 : (n == 1) ? point2 : null; } - + // return post we're connected to (for wires, so we can optimize them out in calculateWireClosure()) Point getConnectedPost() { - return point2; + return point2; } - + int getNodeAtPoint(int xp, int yp) { - int i; - for (i = 0; i != getPostCount(); i++) { - Point p = getPost(i); - if (p.x == xp && p.y == yp) - return i; - } - return 0; + int i; + for (i = 0; i != getPostCount(); i++) { + Point p = getPost(i); + if (p.x == xp && p.y == yp) + return i; + } + return 0; } - + /* void drawPost(Graphics g, int x0, int y0, int n) { if (sim.dragElm == null && !needsHighlight() && @@ -633,168 +685,187 @@ void drawPost(Graphics g, int x0, int y0, int n) { } */ static void drawPost(Graphics g, Point pt) { - g.setColor(whiteColor); - g.fillOval(pt.x-3, pt.y-3, 7, 7); + g.setColor(whiteColor); + g.fillOval(pt.x - 3, pt.y - 3, 7, 7); } - + // set/adjust bounding box used for selecting elements. getCircuitBounds() does not use this! void setBbox(int x1, int y1, int x2, int y2) { - if (x1 > x2) { int q = x1; x1 = x2; x2 = q; } - if (y1 > y2) { int q = y1; y1 = y2; y2 = q; } - boundingBox.setBounds(x1, y1, x2-x1+1, y2-y1+1); + if (x1 > x2) { + int q = x1; + x1 = x2; + x2 = q; + } + if (y1 > y2) { + int q = y1; + y1 = y2; + y2 = q; + } + boundingBox.setBounds(x1, y1, x2 - x1 + 1, y2 - y1 + 1); } - + // set bounding box for an element from p1 to p2 with width w void setBbox(Point p1, Point p2, double w) { - setBbox(p1.x, p1.y, p2.x, p2.y); - int dpx = (int) (dpx1*w); - int dpy = (int) (dpy1*w); - adjustBbox(p1.x+dpx, p1.y+dpy, p1.x-dpx, p1.y-dpy); + setBbox(p1.x, p1.y, p2.x, p2.y); + int dpx = (int) (dpx1 * w); + int dpy = (int) (dpy1 * w); + adjustBbox(p1.x + dpx, p1.y + dpy, p1.x - dpx, p1.y - dpy); } // enlarge bbox to contain an additional rectangle void adjustBbox(int x1, int y1, int x2, int y2) { - if (x1 > x2) { int q = x1; x1 = x2; x2 = q; } - if (y1 > y2) { int q = y1; y1 = y2; y2 = q; } - x1 = min(boundingBox.x, x1); - y1 = min(boundingBox.y, y1); - x2 = max(boundingBox.x+boundingBox.width, x2); - y2 = max(boundingBox.y+boundingBox.height, y2); - boundingBox.setBounds(x1, y1, x2-x1, y2-y1); + if (x1 > x2) { + int q = x1; + x1 = x2; + x2 = q; + } + if (y1 > y2) { + int q = y1; + y1 = y2; + y2 = q; + } + x1 = min(boundingBox.x, x1); + y1 = min(boundingBox.y, y1); + x2 = max(boundingBox.x + boundingBox.width, x2); + y2 = max(boundingBox.y + boundingBox.height, y2); + boundingBox.setBounds(x1, y1, x2 - x1, y2 - y1); } + void adjustBbox(Point p1, Point p2) { - adjustBbox(p1.x, p1.y, p2.x, p2.y); + adjustBbox(p1.x, p1.y, p2.x, p2.y); } - + // needed for calculating circuit bounds (need to special-case centered text elements) - boolean isCenteredText() { return false; } - + boolean isCenteredText() { + return false; + } + void drawCenteredText(Graphics g, String s, int x, int y, boolean cx) { - // FontMetrics fm = g.getFontMetrics(); - //int w = fm.stringWidth(s); + // FontMetrics fm = g.getFontMetrics(); + //int w = fm.stringWidth(s); // int w=0; // if (cx) // x -= w/2; // g.drawString(s, x, y+fm.getAscent()/2); // adjustBbox(x, y-fm.getAscent()/2, // x+w, y+fm.getAscent()/2+fm.getDescent()); - int w=(int)g.context.measureText(s).getWidth(); - int h2=(int)g.currentFontSize/2; - g.save(); - g.context.setTextBaseline("middle"); - if (cx) { - g.context.setTextAlign("center"); - adjustBbox(x-w/2,y-h2,x+w/2,y+h2); - } else { - adjustBbox(x,y-h2,x+w,y+h2); - } - - if (cx) - g.context.setTextAlign("center"); - g.drawString(s, x, y); - g.restore(); + int w = (int) g.context.measureText(s).getWidth(); + int h2 = (int) g.currentFontSize / 2; + g.save(); + g.context.setTextBaseline("middle"); + if (cx) { + g.context.setTextAlign("center"); + adjustBbox(x - w / 2, y - h2, x + w / 2, y + h2); + } else { + adjustBbox(x, y - h2, x + w, y + h2); + } + + if (cx) + g.context.setTextAlign("center"); + g.drawString(s, x, y); + g.restore(); } - + // draw component values (number of resistor ohms, etc). hs = offset void drawValues(Graphics g, String s, double hs) { - if (s == null) - return; - g.setFont(unitsFont); - //FontMetrics fm = g.getFontMetrics(); - int w = (int)g.context.measureText(s).getWidth(); - g.setColor(whiteColor); - int ya = (int)g.currentFontSize/2; - int xc, yc; - if (this instanceof RailElm || this instanceof SweepElm) { - xc = x2; - yc = y2; - } else { - xc = (x2+x)/2; - yc = (y2+y)/2; - } - int dpx = (int) (dpx1*hs); - int dpy = (int) (dpy1*hs); - if (dpx == 0) - g.drawString(s, xc-w/2, yc-abs(dpy)-2); - else { - int xx = xc+abs(dpx)+2; - if (this instanceof VoltageElm || (x < x2 && y > y2)) - xx = xc-(w+abs(dpx)+2); - g.drawString(s, xx, yc+dpy+ya); - } + if (s == null) + return; + g.setFont(unitsFont); + //FontMetrics fm = g.getFontMetrics(); + int w = (int) g.context.measureText(s).getWidth(); + g.setColor(whiteColor); + int ya = (int) g.currentFontSize / 2; + int xc, yc; + if (this instanceof RailElm || this instanceof SweepElm) { + xc = x2; + yc = y2; + } else { + xc = (x2 + x) / 2; + yc = (y2 + y) / 2; + } + int dpx = (int) (dpx1 * hs); + int dpy = (int) (dpy1 * hs); + if (dpx == 0) + g.drawString(s, xc - w / 2, yc - abs(dpy) - 2); + else { + int xx = xc + abs(dpx) + 2; + if (this instanceof VoltageElm || (x < x2 && y > y2)) + xx = xc - (w + abs(dpx) + 2); + g.drawString(s, xx, yc + dpy + ya); + } } - + void drawLabeledNode(Graphics g, String str, Point pt1, Point pt2) { - boolean lineOver = false; - if (str.startsWith("/")) { - lineOver = true; - str = str.substring(1); - } - int w=(int)g.context.measureText(str).getWidth(); - int h=(int)g.currentFontSize; + boolean lineOver = false; + if (str.startsWith("/")) { + lineOver = true; + str = str.substring(1); + } + int w = (int) g.context.measureText(str).getWidth(); + int h = (int) g.currentFontSize; g.save(); g.context.setTextBaseline("middle"); int x = pt2.x, y = pt2.y; if (pt1.y != pt2.y) { - x -= w/2; - y += sign(pt2.y-pt1.y)*h; + x -= w / 2; + y += sign(pt2.y - pt1.y) * h; } else { if (pt2.x > pt1.x) - x += 4; + x += 4; else - x -= 4+w; + x -= 4 + w; } g.drawString(str, x, y); - adjustBbox(x, y-h/2, x+w, y+h/2); + adjustBbox(x, y - h / 2, x + w, y + h / 2); g.restore(); - if (lineOver) { - int ya = y-h/2-1; - g.drawLine(x, ya, x+w, ya); - } - } - + if (lineOver) { + int ya = y - h / 2 - 1; + g.drawLine(x, ya, x + w, ya); + } + } + void drawCoil(Graphics g, int hs, Point p1, Point p2, - double v1, double v2) { - double len = distance(p1, p2); - - g.save(); - g.context.setLineWidth(3.0); - g.context.transform(((double)(p2.x-p1.x))/len, ((double)(p2.y-p1.y))/len, - -((double)(p2.y-p1.y))/len,((double)(p2.x-p1.x))/len,p1.x,p1.y); - if (sim.voltsCheckItem.getState() ) { - CanvasGradient grad = g.context.createLinearGradient(0,0,len,0); - grad.addColorStop(0, getVoltageColor(g,v1).getHexValue()); - grad.addColorStop(1.0, getVoltageColor(g,v2).getHexValue()); - g.context.setStrokeStyle(grad); - } - g.context.setLineCap(LineCap.ROUND); - g.context.scale(1, hs > 0 ? 1 : -1); - - int loop; - // draw more loops for a longer coil - int loopCt = (int)Math.ceil(len/11); - for (loop = 0; loop != loopCt; loop++) { - g.context.beginPath(); - double start = len*loop/loopCt; - g.context.moveTo(start,0); - g.context.arc(len*(loop+.5)/loopCt, 0, len/(2*loopCt), Math.PI, Math.PI*2); - g.context.lineTo(len*(loop+1)/loopCt, 0); - g.context.stroke(); - } - - g.restore(); + double v1, double v2) { + double len = distance(p1, p2); + + g.save(); + g.context.setLineWidth(3.0); + g.context.transform(((double) (p2.x - p1.x)) / len, ((double) (p2.y - p1.y)) / len, + -((double) (p2.y - p1.y)) / len, ((double) (p2.x - p1.x)) / len, p1.x, p1.y); + if (sim.voltsCheckItem.getState()) { + CanvasGradient grad = g.context.createLinearGradient(0, 0, len, 0); + grad.addColorStop(0, getVoltageColor(g, v1).getHexValue()); + grad.addColorStop(1.0, getVoltageColor(g, v2).getHexValue()); + g.context.setStrokeStyle(grad); + } + g.context.setLineCap(LineCap.ROUND); + g.context.scale(1, hs > 0 ? 1 : -1); + + int loop; + // draw more loops for a longer coil + int loopCt = (int) Math.ceil(len / 11); + for (loop = 0; loop != loopCt; loop++) { + g.context.beginPath(); + double start = len * loop / loopCt; + g.context.moveTo(start, 0); + g.context.arc(len * (loop + .5) / loopCt, 0, len / (2 * loopCt), Math.PI, Math.PI * 2); + g.context.lineTo(len * (loop + 1) / loopCt, 0); + g.context.stroke(); + } + + g.restore(); } - + static void drawThickLine(Graphics g, int x, int y, int x2, int y2) { - g.setLineWidth(3.0); - g.drawLine(x,y,x2,y2); - g.setLineWidth(1.0); + g.setLineWidth(3.0); + g.drawLine(x, y, x2, y2); + g.setLineWidth(1.0); } static void drawThickLine(Graphics g, Point pa, Point pb) { - g.setLineWidth(3.0); - g.drawLine(pa.x, pa.y, pb.x, pb.y); - g.setLineWidth(1.0); + g.setLineWidth(3.0); + g.drawLine(pa.x, pa.y, pb.x, pb.y); + g.setLineWidth(1.0); } static void drawThickPolygon(Graphics g, int xs[], int ys[], int c) { @@ -802,17 +873,17 @@ static void drawThickPolygon(Graphics g, int xs[], int ys[], int c) { // for (i = 0; i != c-1; i++) // drawThickLine(g, xs[i], ys[i], xs[i+1], ys[i+1]); // drawThickLine(g, xs[i], ys[i], xs[0], ys[0]); - g.setLineWidth(3.0); - g.drawPolyline(xs, ys, c); - g.setLineWidth(1.0); + g.setLineWidth(3.0); + g.drawPolyline(xs, ys, c); + g.setLineWidth(1.0); } - + static void drawThickPolygon(Graphics g, Polygon p) { - drawThickPolygon(g, p.xpoints, p.ypoints, p.npoints); + drawThickPolygon(g, p.xpoints, p.ypoints, p.npoints); } - + static void drawPolygon(Graphics g, Polygon p) { - g.drawPolyline(p.xpoints, p.ypoints, p.npoints); + g.drawPolyline(p.xpoints, p.ypoints, p.npoints); /* int i; int xs[] = p.xpoints; int ys[] = p.ypoints; @@ -822,174 +893,183 @@ static void drawPolygon(Graphics g, Polygon p) { g.drawLine(xs[i], ys[i], xs[i+1], ys[i+1]); g.drawLine(xs[i], ys[i], xs[0], ys[0]);*/ } - + static void drawThickCircle(Graphics g, int cx, int cy, int ri) { - g.setLineWidth(3.0); - g.context.beginPath(); - g.context.arc(cx, cy, ri*.98, 0, 2*Math.PI); - g.context.stroke(); - g.setLineWidth(1.0); + g.setLineWidth(3.0); + g.context.beginPath(); + g.context.arc(cx, cy, ri * .98, 0, 2 * Math.PI); + g.context.stroke(); + g.setLineWidth(1.0); } - + Polygon getSchmittPolygon(float gsize, float ctr) { - Point pts[] = newPointArray(6); - float hs = 3*gsize; - float h1 = 3*gsize; - float h2 = h1*2; - double len = distance(lead1, lead2); - pts[0] = interpPoint(lead1, lead2, ctr-h2/len, hs); - pts[1] = interpPoint(lead1, lead2, ctr+h1/len, hs); - pts[2] = interpPoint(lead1, lead2, ctr+h1/len, -hs); - pts[3] = interpPoint(lead1, lead2, ctr+h2/len, -hs); - pts[4] = interpPoint(lead1, lead2, ctr-h1/len, -hs); - pts[5] = interpPoint(lead1, lead2, ctr-h1/len, hs); - return createPolygon(pts); + Point pts[] = newPointArray(6); + float hs = 3 * gsize; + float h1 = 3 * gsize; + float h2 = h1 * 2; + double len = distance(lead1, lead2); + pts[0] = interpPoint(lead1, lead2, ctr - h2 / len, hs); + pts[1] = interpPoint(lead1, lead2, ctr + h1 / len, hs); + pts[2] = interpPoint(lead1, lead2, ctr + h1 / len, -hs); + pts[3] = interpPoint(lead1, lead2, ctr + h2 / len, -hs); + pts[4] = interpPoint(lead1, lead2, ctr - h1 / len, -hs); + pts[5] = interpPoint(lead1, lead2, ctr - h1 / len, hs); + return createPolygon(pts); } static String getVoltageDText(double v) { - return getUnitText(Math.abs(v), "V"); + return getUnitText(Math.abs(v), "V"); } + static String getVoltageText(double v) { - return getUnitText(v, "V"); + return getUnitText(v, "V"); } + static String getTimeText(double v) { - if (v >= 60) { - double h = Math.floor(v/3600); - v -= 3600*h; - double m = Math.floor(v/60); - v -= 60*m; - if (h == 0) - return m + ":" + ((v >= 10) ? "" : "0") + showFormat.format(v); - return h + ":" + ((m >= 10) ? "" : "0") + m + ":" + ((v >= 10) ? "" : "0") + showFormat.format(v); - } - return getUnitText(v, "s"); + if (v >= 60) { + double h = Math.floor(v / 3600); + v -= 3600 * h; + double m = Math.floor(v / 60); + v -= 60 * m; + if (h == 0) + return m + ":" + ((v >= 10) ? "" : "0") + showFormat.format(v); + return h + ":" + ((m >= 10) ? "" : "0") + m + ":" + ((v >= 10) ? "" : "0") + showFormat.format(v); + } + return getUnitText(v, "s"); } - + static String format(double v, boolean sf) { // if (sf && Math.abs(v) > 10) // return shortFormat.format(Math.round(v)); - return (sf ? shortFormat : showFormat).format(v); + return (sf ? shortFormat : showFormat).format(v); } - + static String getUnitText(double v, String u) { - return getUnitText(v,u, false); + return getUnitText(v, u, false); } static String getShortUnitText(double v, String u) { - return getUnitText(v,u, true); + return getUnitText(v, u, true); } - + private static String getUnitText(double v, String u, boolean sf) { - String sp = sf ? "" : " "; - double va = Math.abs(v); - if (va < 1e-14) - // this used to return null, but then wires would display "null" with 0V - return "0" + sp + u; - if (va < 1e-9) - return format(v*1e12, sf) + sp + "p" + u; - if (va < 1e-6) - return format(v*1e9, sf) + sp + "n" + u; - if (va < 1e-3) - return format(v*1e6, sf) + sp + Locale.muString + u; - if (va < 1) - return format(v*1e3, sf) + sp + "m" + u; - if (va < 1e3) - return format(v, sf) + sp + u; - if (va < 1e6) - return format(v*1e-3, sf) + sp + "k" + u; - if (va < 1e9) - return format(v*1e-6, sf) + sp + "M" + u; - if (va < 1e12) - return format(v*1e-9, sf) + sp + "G" + u; - return NumberFormat.getFormat("#.##E000").format(v) + sp + u; + String sp = sf ? "" : " "; + double va = Math.abs(v); + if (va < 1e-14) + // this used to return null, but then wires would display "null" with 0V + return "0" + sp + u; + if (va < 1e-9) + return format(v * 1e12, sf) + sp + "p" + u; + if (va < 1e-6) + return format(v * 1e9, sf) + sp + "n" + u; + if (va < 1e-3) + return format(v * 1e6, sf) + sp + Locale.muString + u; + if (va < 1) + return format(v * 1e3, sf) + sp + "m" + u; + if (va < 1e3) + return format(v, sf) + sp + u; + if (va < 1e6) + return format(v * 1e-3, sf) + sp + "k" + u; + if (va < 1e9) + return format(v * 1e-6, sf) + sp + "M" + u; + if (va < 1e12) + return format(v * 1e-9, sf) + sp + "G" + u; + return NumberFormat.getFormat("#.##E000").format(v) + sp + u; } - + static String getCurrentText(double i) { - return getUnitText(i, "A"); + return getUnitText(i, "A"); } + static String getCurrentDText(double i) { - return getUnitText(Math.abs(i), "A"); + return getUnitText(Math.abs(i), "A"); } - static String getUnitTextWithScale(double val, String utext, int scale) { return getUnitTextWithScale(val, utext, scale, false); } + static String getUnitTextWithScale(double val, String utext, int scale) { + return getUnitTextWithScale(val, utext, scale, false); + } static String getUnitTextWithScale(double val, String utext, int scale, boolean fixed) { - if (Math.abs(val) > 1e12) - return getUnitText(val, utext); - NumberFormat nf = fixed ? fixedFormat : showFormat; - if (scale == SCALE_1) - return nf.format(val) + " " + utext; - if (scale == SCALE_M) - return nf.format(1e3*val) + " m" + utext; - if (scale == SCALE_MU) - return nf.format(1e6*val) + " " + Locale.muString + utext; - return getUnitText(val, utext); + if (Math.abs(val) > 1e12) + return getUnitText(val, utext); + NumberFormat nf = fixed ? fixedFormat : showFormat; + if (scale == SCALE_1) + return nf.format(val) + " " + utext; + if (scale == SCALE_M) + return nf.format(1e3 * val) + " m" + utext; + if (scale == SCALE_MU) + return nf.format(1e6 * val) + " " + Locale.muString + utext; + return getUnitText(val, utext); } // update dot positions (curcount) for drawing current (simple case for single current) void updateDotCount() { - curcount = updateDotCount(current, curcount); + curcount = updateDotCount(current, curcount); } // update dot positions (curcount) for drawing current (general case for multiple currents) double updateDotCount(double cur, double cc) { - - if (!sim.simIsRunning()) - return cc; - double cadd = cur*currentMult; - if (cadd > 6 || cadd < -6) - return CURRENT_TOO_FAST; - if (cc == CURRENT_TOO_FAST) - cc = 0; - cadd %= 8; - return cc + cadd; + + if (!sim.simIsRunning()) + return cc; + double cadd = cur * currentMult; + if (cadd > 6 || cadd < -6) + return CURRENT_TOO_FAST; + if (cc == CURRENT_TOO_FAST) + cc = 0; + cadd %= 8; + return cc + cadd; } - + // update and draw current for simple two-terminal element void doDots(Graphics g) { - updateDotCount(); - if (sim.dragElm != this) - drawDots(g, point1, point2, curcount); + updateDotCount(); + if (sim.dragElm != this) + drawDots(g, point1, point2, curcount); } - - void doAdjust() {} - void setupAdjust() {} - + + void doAdjust() { + } + + void setupAdjust() { + } + // get component info for display in lower right void getInfo(String arr[]) { } - + int getBasicInfo(String arr[]) { - arr[1] = "I = " + getCurrentDText(getCurrent()); - arr[2] = "Vd = " + getVoltageDText(getVoltageDiff()); - return 3; + arr[1] = "I = " + getCurrentDText(getCurrent()); + arr[2] = "Vd = " + getVoltageDText(getVoltageDiff()); + return 3; } + String getScopeText(int v) { String info[] = new String[10]; getInfo(info); return info[0]; } - + Color getVoltageColor(Graphics g, double volts) { - if (needsHighlight()) { - return (selectColor); - } - if (!sim.voltsCheckItem.getState()) { - return(whiteColor); - } - int c = (int) ((volts+voltageRange)*(colorScaleCount-1)/ - (voltageRange*2)); - if (c < 0) - c = 0; - if (c >= colorScaleCount) - c = colorScaleCount-1; - return (colorScale[c]); + if (needsHighlight()) { + return (selectColor); + } + if (!sim.voltsCheckItem.getState()) { + return (whiteColor); + } + int c = (int) ((volts + voltageRange) * (colorScaleCount - 1) / + (voltageRange * 2)); + if (c < 0) + c = 0; + if (c >= colorScaleCount) + c = colorScaleCount - 1; + return (colorScale[c]); } - + void setVoltageColor(Graphics g, double volts) { - g.setColor(getVoltageColor(g, volts)); + g.setColor(getVoltageColor(g, volts)); } - + // yellow argument is unused, can't remember why it was there void setPowerColor(Graphics g, boolean yellow) { @@ -997,157 +1077,227 @@ void setPowerColor(Graphics g, boolean yellow) { setConductanceColor(g, current/getVoltageDiff()); return; }*/ - if (!sim.powerCheckItem.getState() ) - return; - setPowerColor(g, getPower()); + if (!sim.powerCheckItem.getState()) + return; + setPowerColor(g, getPower()); } - + void setPowerColor(Graphics g, double w0) { - if (!sim.powerCheckItem.getState() ) - return; - if (needsHighlight()) { - g.setColor(selectColor); - return; - } - w0 *= powerMult; - //System.out.println(w); - int i = (int) ((colorScaleCount/2)+(colorScaleCount/2)*-w0); - if (i<0) - i=0; - if (i>=colorScaleCount) - i=colorScaleCount-1; - g.setColor(colorScale[i]); + if (!sim.powerCheckItem.getState()) + return; + if (needsHighlight()) { + g.setColor(selectColor); + return; + } + w0 *= powerMult; + //System.out.println(w); + int i = (int) ((colorScaleCount / 2) + (colorScaleCount / 2) * -w0); + if (i < 0) + i = 0; + if (i >= colorScaleCount) + i = colorScaleCount - 1; + g.setColor(colorScale[i]); } + void setConductanceColor(Graphics g, double w0) { - w0 *= powerMult; - //System.out.println(w); - double w = (w0 < 0) ? -w0 : w0; - if (w > 1) - w = 1; - int rg = (int) (w*255); - g.setColor(new Color(rg, rg, rg)); - } - double getPower() { return getVoltageDiff()*current; } + w0 *= powerMult; + //System.out.println(w); + double w = (w0 < 0) ? -w0 : w0; + if (w > 1) + w = 1; + int rg = (int) (w * 255); + g.setColor(new Color(rg, rg, rg)); + } + + double getPower() { + return getVoltageDiff() * current; + } + double getScopeValue(int x) { - return (x == Scope.VAL_CURRENT) ? getCurrent() : - (x == Scope.VAL_POWER) ? getPower() : getVoltageDiff(); + return (x == Scope.VAL_CURRENT) ? getCurrent() : + (x == Scope.VAL_POWER) ? getPower() : getVoltageDiff(); } + int getScopeUnits(int x) { - return (x == Scope.VAL_CURRENT) ? Scope.UNITS_A : - (x == Scope.VAL_POWER) ? Scope.UNITS_W : Scope.UNITS_V; + return (x == Scope.VAL_CURRENT) ? Scope.UNITS_A : + (x == Scope.VAL_POWER) ? Scope.UNITS_W : Scope.UNITS_V; } - public EditInfo getEditInfo(int n) { return null; } - public void setEditValue(int n, EditInfo ei) {} - + + public EditInfo getEditInfo(int n) { + return null; + } + + public void setEditValue(int n, EditInfo ei) { + } + // get number of nodes that can be retrieved by getConnectionNode() - int getConnectionNodeCount() { return getPostCount(); } - + int getConnectionNodeCount() { + return getPostCount(); + } + // get nodes that can be passed to getConnection(), to test if this element connects // those two nodes; this is the same as getNode() for all but labeled nodes. - int getConnectionNode(int n) { return getNode(n); } - + int getConnectionNode(int n) { + return getNode(n); + } + // are n1 and n2 connected by this element? this is used to determine // unconnected nodes, and look for loops - boolean getConnection(int n1, int n2) { return true; } - + boolean getConnection(int n1, int n2) { + return true; + } + // is n1 connected to ground somehow? - boolean hasGroundConnection(int n1) { return false; } - + boolean hasGroundConnection(int n1) { + return false; + } + // is this a wire or equivalent to a wire? (used for circuit validation) - boolean isWireEquivalent() { return false; } - + boolean isWireEquivalent() { + return false; + } + // is this a wire we can remove? - boolean isRemovableWire() { return false; } - - boolean canViewInScope() { return getPostCount() <= 2; } + boolean isRemovableWire() { + return false; + } + + boolean canViewInScope() { + return getPostCount() <= 2; + } + boolean comparePair(int x1, int x2, int y1, int y2) { - return ((x1 == y1 && x2 == y2) || (x1 == y2 && x2 == y1)); + return ((x1 == y1 && x2 == y2) || (x1 == y2 && x2 == y1)); } - boolean needsHighlight() { - return mouseElmRef==this || selected || sim.plotYElm == this || - // Test if the current mouseElm is a ScopeElm and, if so, does it belong to this elm - (mouseElmRef instanceof ScopeElm && ((ScopeElm) mouseElmRef).elmScope.getElm()==this); + + boolean needsHighlight() { + return mouseElmRef == this || selected || sim.plotYElm == this || + // Test if the current mouseElm is a ScopeElm and, if so, does it belong to this elm + (mouseElmRef instanceof ScopeElm && ((ScopeElm) mouseElmRef).elmScope.getElm() == this); } - boolean isSelected() { return selected; } - boolean canShowValueInScope(int v) { return false; } - void setSelected(boolean x) { selected = x; } + + boolean isSelected() { + return selected; + } + + boolean canShowValueInScope(int v) { + return false; + } + + void setSelected(boolean x) { + selected = x; + } + void selectRect(Rectangle r, boolean add) { - if (r.intersects(boundingBox)) - selected = true; - else if (!add) - selected = false; - } - static int abs(int x) { return x < 0 ? -x : x; } - static int sign(int x) { return (x < 0) ? -1 : (x == 0) ? 0 : 1; } - static int min(int a, int b) { return (a < b) ? a : b; } - static int max(int a, int b) { return (a > b) ? a : b; } + if (r.intersects(boundingBox)) + selected = true; + else if (!add) + selected = false; + } + + static int abs(int x) { + return x < 0 ? -x : x; + } + + static int sign(int x) { + return (x < 0) ? -1 : (x == 0) ? 0 : 1; + } + + static int min(int a, int b) { + return (a < b) ? a : b; + } + + static int max(int a, int b) { + return (a > b) ? a : b; + } + static double distance(Point p1, Point p2) { - double x = p1.x-p2.x; - double y = p1.y-p2.y; - return Math.sqrt(x*x+y*y); + double x = p1.x - p2.x; + double y = p1.y - p2.y; + return Math.sqrt(x * x + y * y); + } + + Rectangle getBoundingBox() { + return boundingBox; + } + + boolean needsShortcut() { + return getShortcut() > 0; + } + + int getShortcut() { + return 0; + } + + boolean isGraphicElmt() { + return false; } - Rectangle getBoundingBox() { return boundingBox; } - boolean needsShortcut() { return getShortcut() > 0; } - int getShortcut() { return 0; } - boolean isGraphicElmt() { return false; } - void setMouseElm(boolean v) { - if (v) - mouseElmRef=this; - else if (mouseElmRef==this) - mouseElmRef=null; + if (v) + mouseElmRef = this; + else if (mouseElmRef == this) + mouseElmRef = null; } - void draggingDone() {} - - String dumpModel() { return null; } - + + void draggingDone() { + } + + String dumpModel() { + return null; + } + boolean isMouseElm() { - return mouseElmRef==this; + return mouseElmRef == this; } - - void updateModels() {} - void stepFinished() {} - + + void updateModels() { + } + + void stepFinished() { + } + // get current flowing into node n out of this element double getCurrentIntoNode(int n) { - // if we take out the getPostCount() == 2 it gives the wrong value for rails - if (n==0 && getPostCount() == 2) - return -current; - else - return current; + // if we take out the getPostCount() == 2 it gives the wrong value for rails + if (n == 0 && getPostCount() == 2) + return -current; + else + return current; } - + void flipPosts() { - int oldx = x; - int oldy = y; - x = x2; - y = y2; - x2 = oldx; - y2 = oldy; - setPoints(); + int oldx = x; + int oldy = y; + x = x2; + y = y2; + x2 = oldx; + y2 = oldy; + setPoints(); } - - String getClassName() { return getClass().getName().replace("com.lushprojects.circuitjs1.client.", ""); } - + + String getClassName() { + return getClass().getName().replace("com.lushprojects.circuitjs1.client.", ""); + } + native JsArrayString getJsArrayString() /*-{ return []; }-*/; - + JsArrayString getInfoJS() { - JsArrayString jsarr = getJsArrayString(); - String arr[] = new String[20]; - getInfo(arr); - int i; - for (i = 0; arr[i] != null; i++) - jsarr.push(arr[i]); - return jsarr; + JsArrayString jsarr = getJsArrayString(); + String arr[] = new String[20]; + getInfo(arr); + int i; + for (i = 0; arr[i] != null; i++) + jsarr.push(arr[i]); + return jsarr; } - + double getVoltageJS(int n) { - if (n >= volts.length) - return 0; - return volts[n]; + if (n >= volts.length) + return 0; + return volts[n]; } - + native void addJSMethods() /*-{ var that = this; this.getType = $entry(function() { return that.@com.lushprojects.circuitjs1.client.CircuitElm::getClassName()(); }); @@ -1158,7 +1308,7 @@ native void addJSMethods() /*-{ this.getLabelName = $entry(function() { return that.@com.lushprojects.circuitjs1.client.LabeledNodeElm::getName()(); }); this.getPostCount = $entry(function() { return that.@com.lushprojects.circuitjs1.client.CircuitElm::getPostCount()(); }); }-*/; - + native JavaScriptObject getJavaScriptObject() /*-{ return this; }-*/; - + } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/CircuitNode.java b/src/main/java/com/lushprojects/circuitjs1/client/CircuitNode.java index f89dd84..46811d3 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/CircuitNode.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/CircuitNode.java @@ -21,8 +21,11 @@ import java.util.Vector; -class CircuitNode { +public class CircuitNode { Vector links; boolean internal; - CircuitNode() { links = new Vector(); } + + CircuitNode() { + links = new Vector(); + } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/CircuitNodeLink.java b/src/main/java/com/lushprojects/circuitjs1/client/CircuitNodeLink.java index 3049acf..0a7b7c7 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/CircuitNodeLink.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/CircuitNodeLink.java @@ -19,7 +19,7 @@ package com.lushprojects.circuitjs1.client; -class CircuitNodeLink { - int num; - CircuitElm elm; - } +public class CircuitNodeLink { + int num; + CircuitElm elm; +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/ClockElm.java b/src/main/java/com/lushprojects/circuitjs1/client/ClockElm.java index 527413d..c6dd0f4 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/ClockElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/ClockElm.java @@ -19,14 +19,20 @@ package com.lushprojects.circuitjs1.client; -class ClockElm extends RailElm { - public ClockElm(int xx, int yy) { - super(xx, yy, WF_SQUARE); - maxVoltage = 2.5; - bias = 2.5; - frequency = 100; - flags |= FLAG_CLOCK; - } - Class getDumpClass() { return RailElm.class; } - int getShortcut() { return 0; } +public class ClockElm extends RailElm { + public ClockElm(int xx, int yy) { + super(xx, yy, WF_SQUARE); + maxVoltage = 2.5; + bias = 2.5; + frequency = 100; + flags |= FLAG_CLOCK; } + + Class getDumpClass() { + return RailElm.class; + } + + int getShortcut() { + return 0; + } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/Color.java b/src/main/java/com/lushprojects/circuitjs1/client/Color.java index 61bf86a..21c77e8 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/Color.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/Color.java @@ -19,8 +19,7 @@ package com.lushprojects.circuitjs1.client; -public class Color -{ +public class Color { public final static Color white = new Color(255, 255, 255); public final static Color lightGray = new Color(192, 192, 192); public final static Color gray = new Color(128, 128, 128); @@ -37,13 +36,13 @@ public class Color public final static Color cyan = new Color(0, 255, 255); public final static Color blue = new Color(0, 0, 255); public static final Color NONE = new Color(""); - + private int r, g, b; - + // only for special cases, like no color, or maybe named colors private String colorText = null; - public Color (String colorText) { + public Color(String colorText) { this.colorText = colorText; if (colorText.startsWith("#") && colorText.length() == 7) { String rs = colorText.substring(1, 3); @@ -56,49 +55,43 @@ public Color (String colorText) { } // create mixture of c1 and c2 - public Color (Color c1, Color c2, double mix) { - double m0 = 1-mix; - this.r = (int) (c1.r*m0 + c2.r*mix); - this.g = (int) (c1.g*m0 + c2.g*mix); - this.b = (int) (c1.b*m0 + c2.b*mix); + public Color(Color c1, Color c2, double mix) { + double m0 = 1 - mix; + this.r = (int) (c1.r * m0 + c2.r * mix); + this.g = (int) (c1.g * m0 + c2.g * mix); + this.b = (int) (c1.b * m0 + c2.b * mix); } - - public Color (int r, int g, int b) - { + + public Color(int r, int g, int b) { this.r = r; this.g = g; this.b = b; } - public int getRed () - { + public int getRed() { return r; } - public int getGreen () - { + public int getGreen() { return g; } - public int getBlue () - { + public int getBlue() { return b; } - public String getHexValue () - { + public String getHexValue() { if (colorText != null) { return colorText; } return "#" - + pad(Integer.toHexString(r)) - + pad(Integer.toHexString(g)) - + pad(Integer.toHexString(b)); + + pad(Integer.toHexString(r)) + + pad(Integer.toHexString(g)) + + pad(Integer.toHexString(b)); } - private String pad (String in) - { + private String pad(String in) { if (in.length() == 0) { return "00"; } @@ -108,8 +101,7 @@ private String pad (String in) return in; } - public String toString () - { + public String toString() { if (colorText != null) { return colorText; } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/ComparatorElm.java b/src/main/java/com/lushprojects/circuitjs1/client/ComparatorElm.java index 083024d..e1d1400 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/ComparatorElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/ComparatorElm.java @@ -1,96 +1,94 @@ package com.lushprojects.circuitjs1.client; - - - public class ComparatorElm extends CompositeElm { - + private static String modelString = "OpAmpElm 1 2 3\rAnalogSwitchElm 4 5 3\rGroundElm 5"; private static int[] modelExternalNodes = {2, 1, 4}; final int FLAG_SMALL = 2; int opsize, opheight, opwidth; - Point in1p[], in2p[], textp[]; - Polygon triangle; - Font plusFont; - + Point in1p[], in2p[], textp[]; + Polygon triangle; + Font plusFont; + public ComparatorElm(int xx, int yy) { - super(xx, yy, modelString, modelExternalNodes); - noDiagonal = true; - setSize(sim.smallGridCheckItem.getState() ? 1 : 2); + super(xx, yy, modelString, modelExternalNodes); + noDiagonal = true; + setSize(sim.smallGridCheckItem.getState() ? 1 : 2); } - + public ComparatorElm(int xa, int ya, int xb, int yb, int f, StringTokenizer st) { - super(xa, ya, xb, yb, f, st, modelString, modelExternalNodes); - noDiagonal = true; - setSize((f & FLAG_SMALL) != 0 ? 1 : 2); + super(xa, ya, xb, yb, f, st, modelString, modelExternalNodes); + noDiagonal = true; + setSize((f & FLAG_SMALL) != 0 ? 1 : 2); } - + public int getDumpType() { - return 401; + return 401; + } + + void setSize(int s) { + opsize = s; + opheight = 8 * s; + opwidth = 13 * s; + flags = (flags & ~FLAG_SMALL) | ((s == 1) ? FLAG_SMALL : 0); + } + + public boolean getConnection(int n1, int n2) { + return false; + } + + void draw(Graphics g) { + setBbox(point1, point2, opheight * 2); + setVoltageColor(g, volts[0]); + drawThickLine(g, in1p[0], in1p[1]); + setVoltageColor(g, volts[1]); + drawThickLine(g, in2p[0], in2p[1]); + g.setColor(needsHighlight() ? selectColor : lightGrayColor); + setPowerColor(g, true); + drawThickPolygon(g, triangle); + g.setFont(plusFont); + drawCenteredText(g, "-", textp[0].x, textp[0].y - 2, true); + drawCenteredText(g, "+", textp[1].x, textp[1].y, true); + drawCenteredText(g, "\u2265?", textp[2].x, textp[2].y, true); + setVoltageColor(g, volts[2]); + drawThickLine(g, lead2, point2); + curcount = updateDotCount(-getCurrentIntoNode(2), curcount); + drawDots(g, point2, lead2, curcount); + drawPosts(g); } - - void setSize(int s) { - opsize = s; - opheight = 8*s; - opwidth = 13*s; - flags = (flags & ~FLAG_SMALL) | ((s == 1) ? FLAG_SMALL : 0); - } - - public boolean getConnection(int n1, int n2) { return false; } - - void draw(Graphics g) { - setBbox(point1, point2, opheight*2); - setVoltageColor(g, volts[0]); - drawThickLine(g, in1p[0], in1p[1]); - setVoltageColor(g, volts[1]); - drawThickLine(g, in2p[0], in2p[1]); - g.setColor(needsHighlight() ? selectColor : lightGrayColor); - setPowerColor(g, true); - drawThickPolygon(g, triangle); - g.setFont(plusFont); - drawCenteredText(g, "-", textp[0].x, textp[0].y-2, true); - drawCenteredText(g, "+", textp[1].x, textp[1].y , true); - drawCenteredText(g, "\u2265?", textp[2].x, textp[2].y , true); - setVoltageColor(g, volts[2]); - drawThickLine(g, lead2, point2); - curcount = updateDotCount(-getCurrentIntoNode(2), curcount); - drawDots(g, point2, lead2, curcount); - drawPosts(g); - } - void setPoints() { - super.setPoints(); - if (dn > 150 && this == sim.dragElm) - setSize(2); - int ww = opwidth; - if (ww > dn/2) - ww = (int) (dn/2); - calcLeads(ww*2); - int hs = opheight*dsign; + void setPoints() { + super.setPoints(); + if (dn > 150 && this == sim.dragElm) + setSize(2); + int ww = opwidth; + if (ww > dn / 2) + ww = (int) (dn / 2); + calcLeads(ww * 2); + int hs = opheight * dsign; // if ((flags & FLAG_SWAP) != 0) // hs = -hs; - in1p = newPointArray(2); - in2p = newPointArray(2); - textp = newPointArray(3); - interpPoint2(point1, point2, in1p[0], in2p[0], 0, hs); - interpPoint2(lead1 , lead2, in1p[1], in2p[1], 0, hs); - interpPoint2(lead1 , lead2, textp[0], textp[1], .2, hs); - interpPoint(lead1, lead2, textp[2], 0.5, 0); - Point tris[] = newPointArray(2); - interpPoint2(lead1, lead2, tris[0], tris[1], 0, hs*2); - triangle = createPolygon(tris[0], tris[1], lead2); - plusFont = new Font("SansSerif", 0, opsize == 2 ? 14 : 10); - setPost(0, in1p[0]); - setPost(1,in2p[0]); - setPost(2,point2); - } - - - + in1p = newPointArray(2); + in2p = newPointArray(2); + textp = newPointArray(3); + interpPoint2(point1, point2, in1p[0], in2p[0], 0, hs); + interpPoint2(lead1, lead2, in1p[1], in2p[1], 0, hs); + interpPoint2(lead1, lead2, textp[0], textp[1], .2, hs); + interpPoint(lead1, lead2, textp[2], 0.5, 0); + Point tris[] = newPointArray(2); + interpPoint2(lead1, lead2, tris[0], tris[1], 0, hs * 2); + triangle = createPolygon(tris[0], tris[1], lead2); + plusFont = new Font("SansSerif", 0, opsize == 2 ? 14 : 10); + setPost(0, in1p[0]); + setPost(1, in2p[0]); + setPost(2, point2); + } + + void getInfo(String arr[]) { - arr[0] = "Comparator"; - arr[1] = "V+ = " + getVoltageText(volts[1]); - arr[2] = "V- = " + getVoltageText(volts[0]); + arr[0] = "Comparator"; + arr[1] = "V+ = " + getVoltageText(volts[1]); + arr[2] = "V- = " + getVoltageText(volts[0]); } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/CompositeElm.java b/src/main/java/com/lushprojects/circuitjs1/client/CompositeElm.java index 9bc1bc7..0e93f91 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/CompositeElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/CompositeElm.java @@ -22,390 +22,392 @@ public abstract class CompositeElm extends CircuitElm { // need to use escape() instead of converting spaces to _'s so composite elements can be nested final int FLAG_ESCAPE = 1; - + // list of elements contained in this subcircuit Vector compElmList; - + // list of nodes, mapping each one to a list of elements that reference that node protected Vector compNodeList; - + protected int numPosts = 0; protected int numNodes = 0; protected Point posts[]; protected Vector voltageSources; CompositeElm(int xx, int yy) { - super(xx, yy); + super(xx, yy); } - + public CompositeElm(int xa, int ya, int xb, int yb, int f) { - super(xa, ya, xb, yb, f); + super(xa, ya, xb, yb, f); } - + CompositeElm(int xx, int yy, String s, int externalNodes[]) { - super(xx, yy); - loadComposite(null, s, externalNodes); - allocNodes(); + super(xx, yy); + loadComposite(null, s, externalNodes); + allocNodes(); } public CompositeElm(int xa, int ya, int xb, int yb, int f, StringTokenizer st, String s, int externalNodes[]) { - super(xa, ya, xb, yb, f); - loadComposite(st, s, externalNodes); - allocNodes(); + super(xa, ya, xb, yb, f); + loadComposite(st, s, externalNodes); + allocNodes(); + } + + boolean useEscape() { + return (flags & FLAG_ESCAPE) != 0; } - boolean useEscape() { return (flags & FLAG_ESCAPE) != 0; } - public void loadComposite(StringTokenizer stIn, String model, int externalNodes[]) { - HashMap compNodeHash = new HashMap(); - StringTokenizer modelLinet = new StringTokenizer(model, "\r"); - CircuitNode cn; - CircuitNodeLink cnLink; - VoltageSourceRecord vsRecord; - - compElmList = new Vector(); - compNodeList = new Vector(); - voltageSources = new Vector(); - - // Build compElmList and compNodeHash from input string - - while (modelLinet.hasMoreTokens()) { - String line = modelLinet.nextToken(); - StringTokenizer stModel = new StringTokenizer(line, " +\t\n\r\f"); - String ceType = stModel.nextToken(); - CircuitElm newce = CirSim.constructElement(ceType, 0, 0); - if (stIn!=null) { - int tint = newce.getDumpType(); - String dumpedCe= stIn.nextToken(); - if (useEscape()) - dumpedCe = CustomLogicModel.unescape(dumpedCe); - StringTokenizer stCe = new StringTokenizer(dumpedCe, useEscape() ? " " : "_"); - int flags = new Integer(stCe.nextToken()).intValue(); - newce = CirSim.createCe(tint, 0, 0, 0, 0, flags, stCe); - } - if (newce instanceof GroundElm) - ((GroundElm) newce).setOldStyle(); - compElmList.add(newce); - - int thisPost = 0; - while (stModel.hasMoreTokens()) { - int nodeOfThisPost = new Integer(stModel.nextToken()).intValue(); - - // node = 0 means ground - if (nodeOfThisPost == 0) { - newce.setNode(thisPost, 0); - newce.setNodeVoltage(thisPost, 0); - thisPost++; - continue; - } - cnLink = new CircuitNodeLink(); - cnLink.num = thisPost; - cnLink.elm = newce; - if (!compNodeHash.containsKey(nodeOfThisPost)) { - cn = new CircuitNode(); - cn.links.add(cnLink); - compNodeHash.put(nodeOfThisPost, cn); - } else { - cn = compNodeHash.get(nodeOfThisPost); - cn.links.add(cnLink); - } - thisPost++; - } - } - - // Flatten compNodeHash in to compNodeList - numPosts = externalNodes.length; - for (int i = 0; i < externalNodes.length; i++) { // External Nodes First - if (compNodeHash.containsKey(externalNodes[i])) { - compNodeList.add(compNodeHash.get(externalNodes[i])); - compNodeHash.remove(externalNodes[i]); - } else - throw new IllegalArgumentException(); - } - for (Entry entry : compNodeHash.entrySet()) { - int key = entry.getKey(); - compNodeList.add(compNodeHash.get(key)); - } - - // allocate more nodes for sub-elements' internal nodes - for (int i = 0; i != compElmList.size(); i++) { - CircuitElm ce = compElmList.get(i); - int inodes = ce.getInternalNodeCount(); - for (int j = 0; j != inodes; j++) { - cnLink = new CircuitNodeLink(); - cnLink.num = j + ce.getPostCount(); - cnLink.elm = ce; - cn = new CircuitNode(); - cn.links.add(cnLink); - compNodeList.add(cn); - } - } - - numNodes = compNodeList.size(); + HashMap compNodeHash = new HashMap(); + StringTokenizer modelLinet = new StringTokenizer(model, "\r"); + CircuitNode cn; + CircuitNodeLink cnLink; + VoltageSourceRecord vsRecord; + + compElmList = new Vector(); + compNodeList = new Vector(); + voltageSources = new Vector(); + + // Build compElmList and compNodeHash from input string + + while (modelLinet.hasMoreTokens()) { + String line = modelLinet.nextToken(); + StringTokenizer stModel = new StringTokenizer(line, " +\t\n\r\f"); + String ceType = stModel.nextToken(); + CircuitElm newce = CirSim.constructElement(ceType, 0, 0); + if (stIn != null) { + int tint = newce.getDumpType(); + String dumpedCe = stIn.nextToken(); + if (useEscape()) + dumpedCe = CustomLogicModel.unescape(dumpedCe); + StringTokenizer stCe = new StringTokenizer(dumpedCe, useEscape() ? " " : "_"); + int flags = new Integer(stCe.nextToken()).intValue(); + newce = CirSim.createCe(tint, 0, 0, 0, 0, flags, stCe); + } + if (newce instanceof GroundElm) + ((GroundElm) newce).setOldStyle(); + compElmList.add(newce); + + int thisPost = 0; + while (stModel.hasMoreTokens()) { + int nodeOfThisPost = new Integer(stModel.nextToken()).intValue(); + + // node = 0 means ground + if (nodeOfThisPost == 0) { + newce.setNode(thisPost, 0); + newce.setNodeVoltage(thisPost, 0); + thisPost++; + continue; + } + cnLink = new CircuitNodeLink(); + cnLink.num = thisPost; + cnLink.elm = newce; + if (!compNodeHash.containsKey(nodeOfThisPost)) { + cn = new CircuitNode(); + cn.links.add(cnLink); + compNodeHash.put(nodeOfThisPost, cn); + } else { + cn = compNodeHash.get(nodeOfThisPost); + cn.links.add(cnLink); + } + thisPost++; + } + } + + // Flatten compNodeHash in to compNodeList + numPosts = externalNodes.length; + for (int i = 0; i < externalNodes.length; i++) { // External Nodes First + if (compNodeHash.containsKey(externalNodes[i])) { + compNodeList.add(compNodeHash.get(externalNodes[i])); + compNodeHash.remove(externalNodes[i]); + } else + throw new IllegalArgumentException(); + } + for (Entry entry : compNodeHash.entrySet()) { + int key = entry.getKey(); + compNodeList.add(compNodeHash.get(key)); + } + + // allocate more nodes for sub-elements' internal nodes + for (int i = 0; i != compElmList.size(); i++) { + CircuitElm ce = compElmList.get(i); + int inodes = ce.getInternalNodeCount(); + for (int j = 0; j != inodes; j++) { + cnLink = new CircuitNodeLink(); + cnLink.num = j + ce.getPostCount(); + cnLink.elm = ce; + cn = new CircuitNode(); + cn.links.add(cnLink); + compNodeList.add(cn); + } + } + + numNodes = compNodeList.size(); // CirSim.console("Dumping compNodeList"); // for (int i = 0; i < numNodes; i++) { // CirSim.console("New node" + i + " Size of links:" + compNodeList.get(i).links.size()); // } - posts = new Point[numPosts]; - - // Enumerate voltage sources - for (int i = 0; i < compElmList.size(); i++) { - int cnt = compElmList.get(i).getVoltageSourceCount(); - for (int j=0;j < cnt ; j++) { - vsRecord = new VoltageSourceRecord(); - vsRecord.elm = compElmList.get(i); - vsRecord.vsNumForElement = j; - voltageSources.add(vsRecord); - } - } - - // dump new circuits with escape() - flags |= FLAG_ESCAPE; + posts = new Point[numPosts]; + + // Enumerate voltage sources + for (int i = 0; i < compElmList.size(); i++) { + int cnt = compElmList.get(i).getVoltageSourceCount(); + for (int j = 0; j < cnt; j++) { + vsRecord = new VoltageSourceRecord(); + vsRecord.elm = compElmList.get(i); + vsRecord.vsNumForElement = j; + voltageSources.add(vsRecord); + } + } + + // dump new circuits with escape() + flags |= FLAG_ESCAPE; } public boolean nonLinear() { - for (int i = 0; i < compElmList.size(); i++) - if (compElmList.get(i).nonLinear()) - return true; - return false; + for (int i = 0; i < compElmList.size(); i++) + if (compElmList.get(i).nonLinear()) + return true; + return false; } public String dump() { - String dumpStr=super.dump(); - dumpStr += dumpElements(); - return dumpStr; + String dumpStr = super.dump(); + dumpStr += dumpElements(); + return dumpStr; } public String dumpElements() { - String dumpStr = ""; - for (int i = 0; i < compElmList.size(); i++) { - String tstring = compElmList.get(i).dump(); - tstring = tstring.replaceFirst("[A-Za-z0-9]+ 0 0 0 0 ", ""); // remove unused tint x1 y1 x2 y2 coords for internal components - dumpStr += " "+ CustomLogicModel.escape(tstring); - } - return dumpStr; + String dumpStr = ""; + for (int i = 0; i < compElmList.size(); i++) { + String tstring = compElmList.get(i).dump(); + tstring = tstring.replaceFirst("[A-Za-z0-9]+ 0 0 0 0 ", ""); // remove unused tint x1 y1 x2 y2 coords for internal components + dumpStr += " " + CustomLogicModel.escape(tstring); + } + return dumpStr; } // dump subset of elements (some of them may not have any state, and/or may be very long, so we avoid dumping them for brevity) public String dumpWithMask(int mask) { - String dumpStr=super.dump(); - return dumpStr + dumpElements(mask); + String dumpStr = super.dump(); + return dumpStr + dumpElements(mask); } public String dumpElements(int mask) { - String dumpStr = ""; - for (int i = 0; i < compElmList.size(); i++) { - if ((mask & (1< connectedNodes = new Vector(); - - // keep list of nodes connected to n1 - connectedNodes.add(n1); - int i; - for (i = 0; i < connectedNodes.size(); i++) { - // next node in list - int n = connectedNodes.get(i); - if (n == n2) - return true; - - // find all elements connected to n - Vector cnLinks = compNodeList.get(n).links; - for (int j = 0; j < cnLinks.size(); j++) { - CircuitNodeLink link = cnLinks.get(j); - CircuitElm lelm = link.elm; - // loop through all other nodes this element has - for (int k = 0; k != lelm.getConnectionNodeCount(); k++) - // are they connected? - if (k != link.num && lelm.getConnection(link.num, k)) { - int kn = lelm.getConnectionNode(k); - if (kn == 0) - return true; - int m; - // find local node number (kn is global) and add it to list - for (m = 0; m != nodes.length; m++) - if (nodes[m] == kn && !connectedNodes.contains(m)) - connectedNodes.add(m); - } - } - } - return false; - } - + Vector connectedNodes = new Vector(); + + // keep list of nodes connected to n1 + connectedNodes.add(n1); + int i; + for (i = 0; i < connectedNodes.size(); i++) { + // next node in list + int n = connectedNodes.get(i); + if (n == n2) + return true; + + // find all elements connected to n + Vector cnLinks = compNodeList.get(n).links; + for (int j = 0; j < cnLinks.size(); j++) { + CircuitNodeLink link = cnLinks.get(j); + CircuitElm lelm = link.elm; + // loop through all other nodes this element has + for (int k = 0; k != lelm.getConnectionNodeCount(); k++) + // are they connected? + if (k != link.num && lelm.getConnection(link.num, k)) { + int kn = lelm.getConnectionNode(k); + if (kn == 0) + return true; + int m; + // find local node number (kn is global) and add it to list + for (m = 0; m != nodes.length; m++) + if (nodes[m] == kn && !connectedNodes.contains(m)) + connectedNodes.add(m); + } + } + } + return false; + } + // is n1 connected to ground somehow? public boolean hasGroundConnection(int n1) { - Vector connectedNodes = new Vector(); - - // keep list of nodes connected to n1 - connectedNodes.add(n1); - int i; - for (i = 0; i < connectedNodes.size(); i++) { - // next node in list - int n = connectedNodes.get(i); - // find all elements connected to n - Vector cnLinks = compNodeList.get(n).links; - for (int j = 0; j < cnLinks.size(); j++) { - CircuitNodeLink link = cnLinks.get(j); - CircuitElm lelm = link.elm; - if (lelm.hasGroundConnection(link.num)) - return true; - // loop through all other nodes this element has - for (int k = 0; k != lelm.getConnectionNodeCount(); k++) - // are they connected? - if (k != link.num && lelm.getConnection(link.num, k)) { - int kn = lelm.getConnectionNode(k); - int m; - // find local node number (kn is global) and add it to list - for (m = 0; m != nodes.length; m++) - if (nodes[m] == kn && !connectedNodes.contains(m)) - connectedNodes.add(m); - } - } - } - return false; + Vector connectedNodes = new Vector(); + + // keep list of nodes connected to n1 + connectedNodes.add(n1); + int i; + for (i = 0; i < connectedNodes.size(); i++) { + // next node in list + int n = connectedNodes.get(i); + // find all elements connected to n + Vector cnLinks = compNodeList.get(n).links; + for (int j = 0; j < cnLinks.size(); j++) { + CircuitNodeLink link = cnLinks.get(j); + CircuitElm lelm = link.elm; + if (lelm.hasGroundConnection(link.num)) + return true; + // loop through all other nodes this element has + for (int k = 0; k != lelm.getConnectionNodeCount(); k++) + // are they connected? + if (k != link.num && lelm.getConnection(link.num, k)) { + int kn = lelm.getConnectionNode(k); + int m; + // find local node number (kn is global) and add it to list + for (m = 0; m != nodes.length; m++) + if (nodes[m] == kn && !connectedNodes.contains(m)) + connectedNodes.add(m); + } + } + } + return false; } public void reset() { - for (int i = 0; i < compElmList.size(); i++) - compElmList.get(i).reset(); - } + for (int i = 0; i < compElmList.size(); i++) + compElmList.get(i).reset(); + } int getPostCount() { - return numPosts; + return numPosts; } int getInternalNodeCount() { - return numNodes - numPosts; + return numNodes - numPosts; } Point getPost(int n) { - return posts[n]; + return posts[n]; } void setPost(int n, Point p) { - posts[n] = p; + posts[n] = p; } void setPost(int n, int x, int y) { - posts[n].x = x; - posts[n].y = y; + posts[n].x = x; + posts[n].y = y; } public double getPower() { - double power; - power = 0; - for (int i = 0; i < compElmList.size(); i++) - power += compElmList.get(i).getPower(); - return power; + double power; + power = 0; + for (int i = 0; i < compElmList.size(); i++) + power += compElmList.get(i).getPower(); + return power; } public void stamp() { - for (int i = 0; i < compElmList.size(); i++) { - CircuitElm ce = compElmList.get(i); - ce.setParentList(compElmList); - ce.stamp(); - } + for (int i = 0; i < compElmList.size(); i++) { + CircuitElm ce = compElmList.get(i); + ce.setParentList(compElmList); + ce.stamp(); + } } public void startIteration() { - for (int i = 0; i < compElmList.size(); i++) - compElmList.get(i).startIteration(); + for (int i = 0; i < compElmList.size(); i++) + compElmList.get(i).startIteration(); } - + public void doStep() { - for (int i = 0; i < compElmList.size(); i++) - compElmList.get(i).doStep(); + for (int i = 0; i < compElmList.size(); i++) + compElmList.get(i).doStep(); } public void stepFinished() { - for (int i = 0; i < compElmList.size(); i++) - compElmList.get(i).stepFinished(); + for (int i = 0; i < compElmList.size(); i++) + compElmList.get(i).stepFinished(); } // called to set node p (local to this element) to equal n (global) public void setNode(int p, int n) { - // nodes[p] = n - Vector cnLinks; - super.setNode(p, n); - cnLinks = compNodeList.get(p).links; + // nodes[p] = n + Vector cnLinks; + super.setNode(p, n); + cnLinks = compNodeList.get(p).links; // call setNode() for all elements that use that node - for (int i = 0; i < cnLinks.size(); i++) { - cnLinks.get(i).elm.setNode(cnLinks.get(i).num, n); - } + for (int i = 0; i < cnLinks.size(); i++) { + cnLinks.get(i).elm.setNode(cnLinks.get(i).num, n); + } } public void setNodeVoltage(int n, double c) { - // volts[n] = c; - Vector cnLinks; - super.setNodeVoltage(n, c); - cnLinks = compNodeList.get(n).links; - for (int i = 0; i < cnLinks.size(); i++) { - cnLinks.get(i).elm.setNodeVoltage(cnLinks.get(i).num, c); - } - volts[n]=c; + // volts[n] = c; + Vector cnLinks; + super.setNodeVoltage(n, c); + cnLinks = compNodeList.get(n).links; + for (int i = 0; i < cnLinks.size(); i++) { + cnLinks.get(i).elm.setNodeVoltage(cnLinks.get(i).num, c); + } + volts[n] = c; } public boolean canViewInScope() { - return false; + return false; } public void delete() { - for (int i = 0; i < compElmList.size(); i++) - compElmList.get(i).delete(); + for (int i = 0; i < compElmList.size(); i++) + compElmList.get(i).delete(); super.delete(); } public int getVoltageSourceCount() { - return voltageSources.size(); + return voltageSources.size(); } // Find the component with the nth voltage // and set the // appropriate source in that component void setVoltageSource(int n, int v) { - // voltSource(n) = v; - VoltageSourceRecord vsr; - vsr=voltageSources.get(n); - vsr.elm.setVoltageSource(vsr.vsNumForElement, v); - vsr.vsNode=v; + // voltSource(n) = v; + VoltageSourceRecord vsr; + vsr = voltageSources.get(n); + vsr.elm.setVoltageSource(vsr.vsNumForElement, v); + vsr.vsNode = v; } - + @Override - public void setCurrent(int vsn, double c) { - for (int i=0;i cnLinks; - cnLinks = compNodeList.get(n).links; - for (int i = 0; i < cnLinks.size(); i++) { - c+=cnLinks.get(i).elm.getCurrentIntoNode(cnLinks.get(i).num); - } - return c; + double c = 0; + Vector cnLinks; + cnLinks = compNodeList.get(n).links; + for (int i = 0; i < cnLinks.size(); i++) { + c += cnLinks.get(i).elm.getCurrentIntoNode(cnLinks.get(i).num); + } + return c; } } -class VoltageSourceRecord { - int vsNumForElement; - int vsNode; - CircuitElm elm; +public class VoltageSourceRecord { + int vsNumForElement; + int vsNode; + CircuitElm elm; } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/Counter2Elm.java b/src/main/java/com/lushprojects/circuitjs1/client/Counter2Elm.java index 412879a..195ef0c 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/Counter2Elm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/Counter2Elm.java @@ -21,128 +21,141 @@ import com.lushprojects.circuitjs1.client.util.Locale; -class Counter2Elm extends ChipElm { - int modulus; - - public Counter2Elm(int xx, int yy) { - super(xx, yy); - } - - public Counter2Elm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f, st); - try { - modulus = Integer.parseInt(st.nextToken()); - } catch (Exception e) {} - } - - String dump() { - return super.dump() + " " + modulus; - } - - boolean needsBits() { return true; } - String getChipName() { - if (modulus == 0) - return "Counter"; - return Locale.LS("Counter") + Locale.LS(" (mod ") + modulus + ")"; - } - - int clk, clr, enp, ent, rco, load; - - void setupPins() { - sizeX = 2; - sizeY = bits+3; - pins = new Pin[getPostCount()]; - int i; - for (i = 0; i != bits; i++) { - pins[i] = new Pin(i+1, SIDE_E, "Q" + (bits-i-1)); - pins[i].output = pins[i].state = true; - } - for (i = 0; i != bits; i++) { - int ii = i+bits; - pins[ii] = new Pin(i+1, SIDE_W, "I" + (bits-i-1)); - } - int p = bits*2; - clk = p; - clr = p+1; - enp = p+2; - rco = p+3; - load = p+4; - ent = p+5; - pins[clk] = new Pin(0, SIDE_W, ""); - pins[clk].clock = true; - pins[clr] = new Pin(bits+1, SIDE_W, "CLR"); - pins[clr].bubble = true; - pins[enp] = new Pin(bits+2, SIDE_W, "EnP"); - pins[rco] = new Pin(0, SIDE_E, "RCO"); - pins[rco].output = true; - pins[load] = new Pin(bits+1, SIDE_E, "LOAD"); - pins[load].bubble = true; - pins[ent] = new Pin(bits+2, SIDE_E, "EnT"); - } - int getPostCount() { - return bits*2+6; - } - public EditInfo getChipEditInfo(int n) { - if (n == 0) - return new EditInfo("# of Bits", bits, 1, 1).setDimensionless(); - if (n == 1) - return new EditInfo("Modulus", modulus, 1, 1).setDimensionless(); - return null; - } - public void setChipEditValue(int n, EditInfo ei) { - if (n == 0 && ei.value >= 2) { - bits = (int)ei.value; - setupPins(); - setPoints(); - allocNodes(); - } - if (n == 1) - modulus = (int)ei.value; - } - int getVoltageSourceCount() { return bits+1; } - - boolean carry; - - void execute() { - if (pins[clk].value && !lastClock) { - if (pins[enp].value && pins[ent].value) { - int i; - int value = 0; - - // get current value - int lastBit = bits-1; - for (i = 0; i != bits; i++) - if (pins[lastBit-i].value) - value |= 1<= 2) { + bits = (int) ei.value; + setupPins(); + setPoints(); + allocNodes(); + } + if (n == 1) + modulus = (int) ei.value; + } + + int getVoltageSourceCount() { + return bits + 1; + } + + boolean carry; + + void execute() { + if (pins[clk].value && !lastClock) { + if (pins[enp].value && pins[ent].value) { + int i; + int value = 0; + + // get current value + int lastBit = bits - 1; + for (i = 0; i != bits; i++) + if (pins[lastBit - i].value) + value |= 1 << i; + + // update value + value++; + int realmod = (modulus == 0) ? (1 << bits) : modulus; + value %= realmod; + + // convert value to binary + for (i = 0; i != bits; i++) + writeOutput(lastBit - i, (value & (1 << i)) != 0); + + carry = (value == realmod - 1); + } + + if (!pins[load].value) { + int i; + for (i = 0; i != bits; i++) + writeOutput(i, pins[i + bits].value); + } + } + if (!pins[clr].value) { + int i; + for (i = 0; i != bits; i++) + writeOutput(i, false); + carry = false; + } + + lastClock = pins[clk].value; + writeOutput(rco, carry && pins[ent].value); + } + + int getDumpType() { + return 421; } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/CounterElm.java b/src/main/java/com/lushprojects/circuitjs1/client/CounterElm.java index 3249e7d..775e0ba 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/CounterElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/CounterElm.java @@ -21,140 +21,161 @@ import com.lushprojects.circuitjs1.client.util.Locale; -class CounterElm extends ChipElm { - boolean invertreset; - int modulus; - final int FLAG_UP_DOWN = 4; - final int FLAG_NEGATIVE_EDGE = 8; - - public CounterElm(int xx, int yy) { - super(xx, yy); - } - - public CounterElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f, st); - invertreset = true; - try { - invertreset = Boolean.parseBoolean(st.nextToken()); - modulus = Integer.parseInt(st.nextToken()); - } catch (Exception e) {} - pins[1].bubble = invertreset; - } - - String dump() { - return super.dump() + " " + invertreset + " " + modulus; - } - - boolean needsBits() { return true; } - String getChipName() { - if (modulus == 0) - return "Counter"; - return Locale.LS("Counter") + Locale.LS(" (mod ") + modulus + ")"; - } - void setupPins() { - sizeX = 2; - sizeY = bits > 2 ? bits : 2; - pins = new Pin[getPostCount()]; - pins[0] = new Pin(0, SIDE_W, ""); - pins[0].clock = true; - pins[0].bubble = negativeEdgeTriggered(); - pins[1] = new Pin(sizeY-1, SIDE_W, "R"); - pins[1].bubble = invertreset; - int i; - for (i = 0; i != bits; i++) { - int ii = i+2; - pins[ii] = new Pin(i, SIDE_E, "Q" + (bits-i-1)); - pins[ii].output = pins[ii].state = true; - } - if (hasUpDown()) - pins[bits+2] = new Pin(sizeY-2, SIDE_W, "U/D"); - allocNodes(); - } - int getPostCount() { - return (hasUpDown()) ? bits+3 : bits+2; - } - public EditInfo getChipEditInfo(int n) { - if (n == 0) { - EditInfo ei = new EditInfo("", 0, -1, -1); - ei.checkbox = new Checkbox("Invert reset pin",invertreset); - return ei; - } - if (n == 1) - return new EditInfo("# of Bits", bits, 1, 1).setDimensionless(); - if (n == 2) - return new EditInfo("Modulus", modulus, 1, 1).setDimensionless(); - if (n == 3) { - EditInfo ei = new EditInfo("", 0, -1, -1); - ei.checkbox = new Checkbox("Up/Down Pin", hasUpDown()); - return ei; - } - if (n == 4) { - EditInfo ei = new EditInfo("", 0, -1, -1); - ei.checkbox = new Checkbox("Negative Edge Triggered", negativeEdgeTriggered()); - return ei; - } - return null; - } - public void setChipEditValue(int n, EditInfo ei) { - if (n == 0) { - invertreset = ei.checkbox.getState(); - setupPins(); - setPoints(); - } - if (n == 1 && ei.value >= 3) { - bits = (int)ei.value; - setupPins(); - setPoints(); - } - if (n == 2) - modulus = (int)ei.value; - if (n == 3) { - flags = ei.changeFlag(flags, FLAG_UP_DOWN); - setupPins(); - setPoints(); - } - if (n == 4) { - flags = ei.changeFlag(flags, FLAG_NEGATIVE_EDGE); - setupPins(); - setPoints(); - } - } - boolean hasUpDown() { return (flags & FLAG_UP_DOWN) != 0; } - boolean negativeEdgeTriggered() { return (flags & FLAG_NEGATIVE_EDGE) != 0; } - int getVoltageSourceCount() { return bits; } - void execute() { - boolean neg = negativeEdgeTriggered(); - if (pins[0].value != neg && lastClock == neg) { - int i; - int value = 0; - - // get direction - int dir = 1; - if (hasUpDown() && pins[bits+2].value) - dir = -1; - - // get current value - int lastBit = 2+bits-1; - for (i = 0; i != bits; i++) - if (pins[lastBit-i].value) - value |= 1< 2 ? bits : 2; + pins = new Pin[getPostCount()]; + pins[0] = new Pin(0, SIDE_W, ""); + pins[0].clock = true; + pins[0].bubble = negativeEdgeTriggered(); + pins[1] = new Pin(sizeY - 1, SIDE_W, "R"); + pins[1].bubble = invertreset; + int i; + for (i = 0; i != bits; i++) { + int ii = i + 2; + pins[ii] = new Pin(i, SIDE_E, "Q" + (bits - i - 1)); + pins[ii].output = pins[ii].state = true; + } + if (hasUpDown()) + pins[bits + 2] = new Pin(sizeY - 2, SIDE_W, "U/D"); + allocNodes(); + } + + int getPostCount() { + return (hasUpDown()) ? bits + 3 : bits + 2; + } + + public EditInfo getChipEditInfo(int n) { + if (n == 0) { + EditInfo ei = new EditInfo("", 0, -1, -1); + ei.checkbox = new Checkbox("Invert reset pin", invertreset); + return ei; + } + if (n == 1) + return new EditInfo("# of Bits", bits, 1, 1).setDimensionless(); + if (n == 2) + return new EditInfo("Modulus", modulus, 1, 1).setDimensionless(); + if (n == 3) { + EditInfo ei = new EditInfo("", 0, -1, -1); + ei.checkbox = new Checkbox("Up/Down Pin", hasUpDown()); + return ei; + } + if (n == 4) { + EditInfo ei = new EditInfo("", 0, -1, -1); + ei.checkbox = new Checkbox("Negative Edge Triggered", negativeEdgeTriggered()); + return ei; + } + return null; + } + + public void setChipEditValue(int n, EditInfo ei) { + if (n == 0) { + invertreset = ei.checkbox.getState(); + setupPins(); + setPoints(); + } + if (n == 1 && ei.value >= 3) { + bits = (int) ei.value; + setupPins(); + setPoints(); + } + if (n == 2) + modulus = (int) ei.value; + if (n == 3) { + flags = ei.changeFlag(flags, FLAG_UP_DOWN); + setupPins(); + setPoints(); + } + if (n == 4) { + flags = ei.changeFlag(flags, FLAG_NEGATIVE_EDGE); + setupPins(); + setPoints(); + } + } + + boolean hasUpDown() { + return (flags & FLAG_UP_DOWN) != 0; + } + + boolean negativeEdgeTriggered() { + return (flags & FLAG_NEGATIVE_EDGE) != 0; + } + + int getVoltageSourceCount() { + return bits; + } + + void execute() { + boolean neg = negativeEdgeTriggered(); + if (pins[0].value != neg && lastClock == neg) { + int i; + int value = 0; + + // get direction + int dir = 1; + if (hasUpDown() && pins[bits + 2].value) + dir = -1; + + // get current value + int lastBit = 2 + bits - 1; + for (i = 0; i != bits; i++) + if (pins[lastBit - i].value) + value |= 1 << i; + + // update value + value += dir; + if (modulus != 0) + value = (value + modulus) % modulus; + + // convert value to binary + for (i = 0; i != bits; i++) + pins[lastBit - i].value = (value & (1 << i)) != 0; + } + if (!pins[1].value == invertreset) { + int i; + for (i = 0; i != bits; i++) + pins[i + 2].value = false; + } + lastClock = pins[0].value; + } + + int getDumpType() { + return 164; } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/CrystalElm.java b/src/main/java/com/lushprojects/circuitjs1/client/CrystalElm.java index 4b6637c..4077724 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/CrystalElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/CrystalElm.java @@ -21,140 +21,146 @@ import com.lushprojects.circuitjs1.client.util.Locale; -class CrystalElm extends CompositeElm { - double seriesCapacitance, parallelCapacitance; - double inductance, resistance; - Point plate1[], plate2[]; - private static String modelString = "CapacitorElm 1 2\rCapacitorElm 1 3\rInductorElm 3 4\rResistorElm 4 2"; - private static int[] modelExternalNodes = { 1, 2 }; - - public CrystalElm(int xx, int yy) { - super(xx, yy, modelString, modelExternalNodes); - parallelCapacitance = 28.7e-12; - seriesCapacitance = 0.1e-12; - inductance = 2.5e-3; - resistance = 6.4; - initCrystal(); - } - public CrystalElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f, st, modelString, modelExternalNodes); - CapacitorElm c1 = (CapacitorElm) compElmList.get(0); - parallelCapacitance = c1.getCapacitance(); - CapacitorElm c2 = (CapacitorElm) compElmList.get(1); - seriesCapacitance = c2.getCapacitance(); - InductorElm i1 = (InductorElm) compElmList.get(2); - inductance = i1.getInductance(); - ResistorElm r1 = (ResistorElm) compElmList.get(3); - resistance = r1.getResistance(); - initCrystal(); - } - - private void initCrystal() { - CapacitorElm c1 = (CapacitorElm) compElmList.get(0); - c1.setCapacitance(parallelCapacitance); - CapacitorElm c2 = (CapacitorElm) compElmList.get(1); - c2.setCapacitance(seriesCapacitance); - InductorElm i1 = (InductorElm) compElmList.get(2); - i1.setInductance(inductance); - ResistorElm r1 = (ResistorElm) compElmList.get(3); - r1.setResistance(resistance); - } - - - int getDumpType() { return 412; } - - Point sandwichPoints[]; - - void setPoints() { - super.setPoints(); - double f = (dn/2-10)/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, 8); - interpPoint2(point1, point2, plate2[0], plate2[1], 1-f, 8); - - sandwichPoints = newPointArray(4); - double f2 = (dn/2-5)/dn; - interpPoint2(point1, point2, sandwichPoints[0], sandwichPoints[1], f2, 10); - interpPoint2(point1, point2, sandwichPoints[3], sandwichPoints[2], 1-f2, 10); - - // need to do this explicitly for CompositeElms - setPost(0, point1); - setPost(1, point2); - } - - 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]); - - int i; - setVoltageColor(g, .5*(volts[0]+volts[1])); - for (i = 0; i != 4; i++) - drawThickLine(g, sandwichPoints[i], sandwichPoints[(i+1) % 4]); - - updateDotCount(); - if (sim.dragElm != this) { - drawDots(g, point1, lead1, curcount); - drawDots(g, point2, lead2, -curcount); - } - drawPosts(g); - } - - void calculateCurrent() { - current = getCurrentIntoNode(1); - } - - void getInfo(String arr[]) { - arr[0] = "crystal"; - getBasicInfo(arr); - arr[3] = "fs = " + getUnitText(1/(Math.sqrt(inductance*seriesCapacitance)*Math.PI*2), "Hz"); +public class CrystalElm extends CompositeElm { + double seriesCapacitance, parallelCapacitance; + double inductance, resistance; + Point plate1[], plate2[]; + private static String modelString = "CapacitorElm 1 2\rCapacitorElm 1 3\rInductorElm 3 4\rResistorElm 4 2"; + private static int[] modelExternalNodes = {1, 2}; + + public CrystalElm(int xx, int yy) { + super(xx, yy, modelString, modelExternalNodes); + parallelCapacitance = 28.7e-12; + seriesCapacitance = 0.1e-12; + inductance = 2.5e-3; + resistance = 6.4; + initCrystal(); + } + + public CrystalElm(int xa, int ya, int xb, int yb, int f, + StringTokenizer st) { + super(xa, ya, xb, yb, f, st, modelString, modelExternalNodes); + CapacitorElm c1 = (CapacitorElm) compElmList.get(0); + parallelCapacitance = c1.getCapacitance(); + CapacitorElm c2 = (CapacitorElm) compElmList.get(1); + seriesCapacitance = c2.getCapacitance(); + InductorElm i1 = (InductorElm) compElmList.get(2); + inductance = i1.getInductance(); + ResistorElm r1 = (ResistorElm) compElmList.get(3); + resistance = r1.getResistance(); + initCrystal(); + } + + private void initCrystal() { + CapacitorElm c1 = (CapacitorElm) compElmList.get(0); + c1.setCapacitance(parallelCapacitance); + CapacitorElm c2 = (CapacitorElm) compElmList.get(1); + c2.setCapacitance(seriesCapacitance); + InductorElm i1 = (InductorElm) compElmList.get(2); + i1.setInductance(inductance); + ResistorElm r1 = (ResistorElm) compElmList.get(3); + r1.setResistance(resistance); + } + + + int getDumpType() { + return 412; + } + + Point sandwichPoints[]; + + void setPoints() { + super.setPoints(); + double f = (dn / 2 - 10) / 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, 8); + interpPoint2(point1, point2, plate2[0], plate2[1], 1 - f, 8); + + sandwichPoints = newPointArray(4); + double f2 = (dn / 2 - 5) / dn; + interpPoint2(point1, point2, sandwichPoints[0], sandwichPoints[1], f2, 10); + interpPoint2(point1, point2, sandwichPoints[3], sandwichPoints[2], 1 - f2, 10); + + // need to do this explicitly for CompositeElms + setPost(0, point1); + setPost(1, point2); + } + + 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]); + + int i; + setVoltageColor(g, .5 * (volts[0] + volts[1])); + for (i = 0; i != 4; i++) + drawThickLine(g, sandwichPoints[i], sandwichPoints[(i + 1) % 4]); + + updateDotCount(); + if (sim.dragElm != this) { + drawDots(g, point1, lead1, curcount); + drawDots(g, point2, lead2, -curcount); + } + drawPosts(g); + } + + void calculateCurrent() { + current = getCurrentIntoNode(1); + } + + void getInfo(String arr[]) { + arr[0] = "crystal"; + getBasicInfo(arr); + arr[3] = "fs = " + getUnitText(1 / (Math.sqrt(inductance * seriesCapacitance) * Math.PI * 2), "Hz"); // 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 boolean canViewInScope() { return true; } - - public EditInfo getEditInfo(int n) { - if (n == 0) - return new EditInfo(EditInfo.makeLink("crystal.html", "Parallel Capacitance"), parallelCapacitance); - if (n == 1) - return new EditInfo("Series Capacitance (F)", seriesCapacitance); - if (n == 2) - return new EditInfo("Inductance (H)", inductance, 0, 0); - if (n == 3) - return new EditInfo("Resistance (" + Locale.ohmString + ")", resistance, 0, 0); - return null; - } - public void setEditValue(int n, EditInfo ei) { - if (n == 0 && ei.value > 0) - parallelCapacitance = ei.value; - if (n == 1 && ei.value > 0) - seriesCapacitance = ei.value; - if (n == 2 && ei.value > 0) - inductance = ei.value; - if (n == 3 && ei.value > 0) - resistance = ei.value; - initCrystal(); - } + //double v = getVoltageDiff(); + //arr[4] = "U = " + getUnitText(.5*capacitance*v*v, "J"); + } + + public boolean canViewInScope() { + return true; + } + + public EditInfo getEditInfo(int n) { + if (n == 0) + return new EditInfo(EditInfo.makeLink("crystal.html", "Parallel Capacitance"), parallelCapacitance); + if (n == 1) + return new EditInfo("Series Capacitance (F)", seriesCapacitance); + if (n == 2) + return new EditInfo("Inductance (H)", inductance, 0, 0); + if (n == 3) + return new EditInfo("Resistance (" + Locale.ohmString + ")", resistance, 0, 0); + return null; + } + + public void setEditValue(int n, EditInfo ei) { + if (n == 0 && ei.value > 0) + parallelCapacitance = ei.value; + if (n == 1 && ei.value > 0) + seriesCapacitance = ei.value; + if (n == 2 && ei.value > 0) + inductance = ei.value; + if (n == 3 && ei.value > 0) + resistance = ei.value; + initCrystal(); } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/CurrentElm.java b/src/main/java/com/lushprojects/circuitjs1/client/CurrentElm.java index 822a542..9f665e5 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/CurrentElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/CurrentElm.java @@ -19,90 +19,104 @@ package com.lushprojects.circuitjs1.client; - class CurrentElm extends CircuitElm { - double currentValue; - boolean broken; - public CurrentElm(int xx, int yy) { - super(xx, yy); - currentValue = .01; - } - public CurrentElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f); - try { - currentValue = new Double(st.nextToken()).doubleValue(); - } catch (Exception e) { - currentValue = .01; - } - } - String dump() { - return super.dump() + " " + currentValue; - } - int getDumpType() { return 'i'; } - - Polygon arrow; - Point ashaft1, ashaft2, center; - void setPoints() { - super.setPoints(); - calcLeads(26); - ashaft1 = interpPoint(lead1, lead2, .25); - ashaft2 = interpPoint(lead1, lead2, .6); - center = interpPoint(lead1, lead2, .5); - Point p2 = interpPoint(lead1, lead2, .75); - arrow = calcArrow(center, p2, 4, 4); - } - void draw(Graphics g) { - int cr = 12; - draw2Leads(g); - setVoltageColor(g, (volts[0]+volts[1])/2); - setPowerColor(g, false); - - drawThickCircle(g, center.x, center.y, cr); - drawThickLine(g, ashaft1, ashaft2); - - g.fillPolygon(arrow); - setBbox(point1, point2, cr); - doDots(g); - if (sim.showValuesCheckItem.getState() && current != 0) { - String s = getShortUnitText(current, "A"); - if (dx == 0 || dy == 0) - drawValues(g, s, cr); - } - drawPosts(g); - } - - // analyzeCircuit determines if current source has a path or if it's broken - void setBroken(boolean b) { - broken = b; - } - - // we defer stamping current sources until we can tell if they have a current path or not - void stamp() { - if (broken) { - // no current path; stamping a current source would cause a matrix error. - sim.stampResistor(nodes[0], nodes[1], 1e8); - current = 0; - } else { - // ok to stamp a current source - sim.stampCurrentSource(nodes[0], nodes[1], currentValue); - current = currentValue; - } - } - - public EditInfo getEditInfo(int n) { - if (n == 0) - return new EditInfo("Current (A)", currentValue, 0, .1); - return null; - } - public void setEditValue(int n, EditInfo ei) { - currentValue = ei.value; - } - void getInfo(String arr[]) { - arr[0] = "current source"; - getBasicInfo(arr); - } - double getVoltageDiff() { - return volts[1] - volts[0]; - } - double getPower() { return -getVoltageDiff()*current; } +public class CurrentElm extends CircuitElm { + double currentValue; + boolean broken; + + public CurrentElm(int xx, int yy) { + super(xx, yy); + currentValue = .01; + } + + public CurrentElm(int xa, int ya, int xb, int yb, int f, + StringTokenizer st) { + super(xa, ya, xb, yb, f); + try { + currentValue = new Double(st.nextToken()).doubleValue(); + } catch (Exception e) { + currentValue = .01; + } + } + + String dump() { + return super.dump() + " " + currentValue; + } + + int getDumpType() { + return 'i'; + } + + Polygon arrow; + Point ashaft1, ashaft2, center; + + void setPoints() { + super.setPoints(); + calcLeads(26); + ashaft1 = interpPoint(lead1, lead2, .25); + ashaft2 = interpPoint(lead1, lead2, .6); + center = interpPoint(lead1, lead2, .5); + Point p2 = interpPoint(lead1, lead2, .75); + arrow = calcArrow(center, p2, 4, 4); + } + + void draw(Graphics g) { + int cr = 12; + draw2Leads(g); + setVoltageColor(g, (volts[0] + volts[1]) / 2); + setPowerColor(g, false); + + drawThickCircle(g, center.x, center.y, cr); + drawThickLine(g, ashaft1, ashaft2); + + g.fillPolygon(arrow); + setBbox(point1, point2, cr); + doDots(g); + if (sim.showValuesCheckItem.getState() && current != 0) { + String s = getShortUnitText(current, "A"); + if (dx == 0 || dy == 0) + drawValues(g, s, cr); + } + drawPosts(g); + } + + // analyzeCircuit determines if current source has a path or if it's broken + void setBroken(boolean b) { + broken = b; + } + + // we defer stamping current sources until we can tell if they have a current path or not + void stamp() { + if (broken) { + // no current path; stamping a current source would cause a matrix error. + sim.stampResistor(nodes[0], nodes[1], 1e8); + current = 0; + } else { + // ok to stamp a current source + sim.stampCurrentSource(nodes[0], nodes[1], currentValue); + current = currentValue; + } + } + + public EditInfo getEditInfo(int n) { + if (n == 0) + return new EditInfo("Current (A)", currentValue, 0, .1); + return null; + } + + public void setEditValue(int n, EditInfo ei) { + currentValue = ei.value; + } + + void getInfo(String arr[]) { + arr[0] = "current source"; + getBasicInfo(arr); + } + + double getVoltageDiff() { + return volts[1] - volts[0]; + } + + double getPower() { + return -getVoltageDiff() * current; } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/CustomCompositeChipElm.java b/src/main/java/com/lushprojects/circuitjs1/client/CustomCompositeChipElm.java index 816829b..c58b075 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/CustomCompositeChipElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/CustomCompositeChipElm.java @@ -19,43 +19,56 @@ package com.lushprojects.circuitjs1.client; - // concrete subclass of ChipElm that can be used by other elements (like CustomCompositeElm) to draw chips. - // CustomCompositeElm can't be a subclass of both ChipElm and CompositeElm. - class CustomCompositeChipElm extends ChipElm { - String label; - - public CustomCompositeChipElm(int xx, int yy) { - super(xx, yy); - setSize(2); - } - boolean needsBits() { return false; } - void setupPins() { } - int getVoltageSourceCount() { return 0; } - void setPins(Pin p[]) { - pins = p; - } - void allocPins(int n) { - pins = new Pin[n]; - } - void setPin(int n, int p, int s, String t) { - pins[n] = new Pin(p, s, t); - pins[n].fixName(); - } - - void setLabel(String text) { - label = text; - } - - void drawLabel(Graphics g, int x, int y) { - if (label == null) - return; - g.save(); - g.context.setTextBaseline("middle"); - g.context.setTextAlign("center"); - g.drawString(label, x, y); - g.restore(); - } - - int getPostCount() { return pins == null ? 1 : pins.length; } +// concrete subclass of ChipElm that can be used by other elements (like CustomCompositeElm) to draw chips. +// CustomCompositeElm can't be a subclass of both ChipElm and CompositeElm. +public class CustomCompositeChipElm extends ChipElm { + String label; + + public CustomCompositeChipElm(int xx, int yy) { + super(xx, yy); + setSize(2); + } + + boolean needsBits() { + return false; + } + + void setupPins() { + } + + int getVoltageSourceCount() { + return 0; + } + + void setPins(Pin p[]) { + pins = p; + } + + void allocPins(int n) { + pins = new Pin[n]; + } + + void setPin(int n, int p, int s, String t) { + pins[n] = new Pin(p, s, t); + pins[n].fixName(); + } + + void setLabel(String text) { + label = text; + } + + void drawLabel(Graphics g, int x, int y) { + if (label == null) + return; + g.save(); + g.context.setTextBaseline("middle"); + g.context.setTextAlign("center"); + g.drawString(label, x, y); + g.restore(); + } + + int getPostCount() { + return pins == null ? 1 : pins.length; } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/CustomCompositeElm.java b/src/main/java/com/lushprojects/circuitjs1/client/CustomCompositeElm.java index eb798de..f23a1a8 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/CustomCompositeElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/CustomCompositeElm.java @@ -16,134 +16,136 @@ public class CustomCompositeElm extends CompositeElm { CustomCompositeModel model; static String lastModelName = "default"; static final int FLAG_SMALL = 2; - + public CustomCompositeElm(int xx, int yy) { - super(xx, yy); - - // use last model as default when creating new element in UI. - // use default otherwise, to avoid infinite recursion when creating nested subcircuits. - modelName = (xx == 0 && yy == 0) ? "default" : lastModelName; - - flags |= FLAG_ESCAPE; - if (sim.smallGridCheckItem.getState()) - flags |= FLAG_SMALL; - updateModels(); + super(xx, yy); + + // use last model as default when creating new element in UI. + // use default otherwise, to avoid infinite recursion when creating nested subcircuits. + modelName = (xx == 0 && yy == 0) ? "default" : lastModelName; + + flags |= FLAG_ESCAPE; + if (sim.smallGridCheckItem.getState()) + flags |= FLAG_SMALL; + updateModels(); } public CustomCompositeElm(int xx, int yy, String name) { - super(xx, yy); - modelName = name; - flags |= FLAG_ESCAPE; - if (sim.smallGridCheckItem.getState()) - flags |= FLAG_SMALL; - updateModels(); + super(xx, yy); + modelName = name; + flags |= FLAG_ESCAPE; + if (sim.smallGridCheckItem.getState()) + flags |= FLAG_SMALL; + updateModels(); } - + public CustomCompositeElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f); - modelName = CustomLogicModel.unescape(st.nextToken()); - updateModels(st); + StringTokenizer st) { + super(xa, ya, xb, yb, f); + modelName = CustomLogicModel.unescape(st.nextToken()); + updateModels(st); } - + public String dump() { - // insert model name before the elements - String s = super.dumpWithMask(0); - s += " " + CustomLogicModel.escape(modelName); - s += dumpElements(); - return s; + // insert model name before the elements + String s = super.dumpWithMask(0); + s += " " + CustomLogicModel.escape(modelName); + s += dumpElements(); + return s; } - + String dumpModel() { - String modelStr = ""; - - // dump models of all children - for (int i = 0; i < compElmList.size(); i++) { - CircuitElm ce = compElmList.get(i); - String m = ce.dumpModel(); - if (m != null && !m.isEmpty()) { - if (!modelStr.isEmpty()) - modelStr += "\n"; - modelStr += m; - } - } - if (model.dumped) - return modelStr; - - // dump our model - if (!modelStr.isEmpty()) - modelStr += "\n"; - modelStr += model.dump(); - - return modelStr; + String modelStr = ""; + + // dump models of all children + for (int i = 0; i < compElmList.size(); i++) { + CircuitElm ce = compElmList.get(i); + String m = ce.dumpModel(); + if (m != null && !m.isEmpty()) { + if (!modelStr.isEmpty()) + modelStr += "\n"; + modelStr += m; + } + } + if (model.dumped) + return modelStr; + + // dump our model + if (!modelStr.isEmpty()) + modelStr += "\n"; + modelStr += model.dump(); + + return modelStr; } - + void draw(Graphics g) { - int i; - for (i = 0; i != postCount; i++) { - chip.volts[i] = volts[i]; - chip.pins[i].current = getCurrentIntoNode(i); - } - chip.setSelected(needsHighlight()); - chip.draw(g); - boundingBox = chip.boundingBox; + int i; + for (i = 0; i != postCount; i++) { + chip.volts[i] = volts[i]; + chip.pins[i].current = getCurrentIntoNode(i); + } + chip.setSelected(needsHighlight()); + chip.draw(g); + boundingBox = chip.boundingBox; } void setPoints() { - chip = new CustomCompositeChipElm(x, y); - chip.x2 = x2; - chip.y2 = y2; - chip.flags = (flags & (ChipElm.FLAG_FLIP_X | ChipElm.FLAG_FLIP_Y | ChipElm.FLAG_FLIP_XY)); - if (x2-x > model.sizeX*16 && this == sim.dragElm) - flags &= ~FLAG_SMALL; - chip.setSize((flags & FLAG_SMALL) != 0 ? 1 : 2); - chip.setLabel((model.flags & CustomCompositeModel.FLAG_SHOW_LABEL) != 0 ? model.name : null); - - chip.sizeX = model.sizeX; - chip.sizeY = model.sizeY; - chip.allocPins(postCount); - int i; - for (i = 0; i != postCount; i++) { - ExtListEntry pin = model.extList.get(i); - chip.setPin(i, pin.pos, pin.side, pin.name); - } - - chip.setPoints(); - for (i = 0; i != getPostCount(); i++) - setPost(i, chip.getPost(i)); + chip = new CustomCompositeChipElm(x, y); + chip.x2 = x2; + chip.y2 = y2; + chip.flags = (flags & (ChipElm.FLAG_FLIP_X | ChipElm.FLAG_FLIP_Y | ChipElm.FLAG_FLIP_XY)); + if (x2 - x > model.sizeX * 16 && this == sim.dragElm) + flags &= ~FLAG_SMALL; + chip.setSize((flags & FLAG_SMALL) != 0 ? 1 : 2); + chip.setLabel((model.flags & CustomCompositeModel.FLAG_SHOW_LABEL) != 0 ? model.name : null); + + chip.sizeX = model.sizeX; + chip.sizeY = model.sizeY; + chip.allocPins(postCount); + int i; + for (i = 0; i != postCount; i++) { + ExtListEntry pin = model.extList.get(i); + chip.setPin(i, pin.pos, pin.side, pin.name); + } + + chip.setPoints(); + for (i = 0; i != getPostCount(); i++) + setPost(i, chip.getPost(i)); } public void updateModels() { - updateModels(null); + updateModels(null); } - + public void updateModels(StringTokenizer st) { - model = CustomCompositeModel.getModelWithName(modelName); - if (model == null) - return; - postCount = model.extList.size(); - int externalNodes[] = new int[postCount]; - int i; - for (i = 0; i != postCount; i++) - externalNodes[i] = model.extList.get(i).node; - if (st == null) - st = new StringTokenizer(model.elmDump, " "); - loadComposite(st, model.nodeList, externalNodes); - allocNodes(); - setPoints(); + model = CustomCompositeModel.getModelWithName(modelName); + if (model == null) + return; + postCount = model.extList.size(); + int externalNodes[] = new int[postCount]; + int i; + for (i = 0; i != postCount; i++) + externalNodes[i] = model.extList.get(i).node; + if (st == null) + st = new StringTokenizer(model.elmDump, " "); + loadComposite(st, model.nodeList, externalNodes); + allocNodes(); + setPoints(); + } + + int getPostCount() { + return postCount; } - - int getPostCount() { return postCount; } - + Vector models; - + public EditInfo getEditInfo(int n) { - // if model is built in, don't allow it to be changed - if (model.builtin) - n += 2; - - if (n == 0) { - EditInfo ei = new EditInfo(EditInfo.makeLink("subcircuits.html", "Model Name"), 0, -1, -1); + // if model is built in, don't allow it to be changed + if (model.builtin) + n += 2; + + if (n == 0) { + EditInfo ei = new EditInfo(EditInfo.makeLink("subcircuits.html", "Model Name"), 0, -1, -1); models = CustomCompositeModel.getModelList(); ei.choice = new Choice(); int i; @@ -153,8 +155,8 @@ public EditInfo getEditInfo(int n) { if (ccm == model) ei.choice.select(i); } - return ei; - } + return ei; + } if (n == 1) { EditInfo ei = new EditInfo("", 0, -1, -1); ei.button = new Button(Locale.LS("Edit Pin Layout")); @@ -180,23 +182,23 @@ public EditInfo getEditInfo(int n) { ei.button = new Button(Locale.LS("Load Model Circuit")); return ei; } - return null; + return null; } public void setEditValue(int n, EditInfo ei) { - if (model.builtin) - n += 2; - if (n == 0) { + if (model.builtin) + n += 2; + if (n == 0) { model = models.get(ei.choice.getSelectedIndex()); - lastModelName = modelName = model.name; - updateModels(); - setPoints(); - return; - } + lastModelName = modelName = model.name; + updateModels(); + setPoints(); + return; + } if (n == 1) { if (model.name.equals("default")) { - Window.alert(Locale.LS("Can't edit this model.")); - return; + Window.alert(Locale.LS("Can't edit this model.")); + return; } EditCompositeModelDialog dlg = new EditCompositeModelDialog(); dlg.setModel(model); @@ -222,20 +224,22 @@ public void setEditValue(int n, EditInfo ei) { CirSim.editDialog.closeDialog(); } } - - int getDumpType() { return 410; } + + int getDumpType() { + return 410; + } void getInfo(String arr[]) { - super.getInfo(arr); - if (model.builtin) - arr[0] = model.name.substring(1); - else - arr[0] = "subcircuit (" + model.name + ")"; - int i; - for (i = 0; i != postCount; i++) { - if (i+1 >= arr.length) - break; - arr[i+1] = model.extList.get(i).name + " = " + getVoltageText(volts[i]); - } + super.getInfo(arr); + if (model.builtin) + arr[0] = model.name.substring(1); + else + arr[0] = "subcircuit (" + model.name + ")"; + int i; + for (i = 0; i != postCount; i++) { + if (i + 1 >= arr.length) + break; + arr[i + 1] = model.extList.get(i).name + " = " + getVoltageText(volts[i]); + } } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/CustomCompositeModel.java b/src/main/java/com/lushprojects/circuitjs1/client/CustomCompositeModel.java index 458c5c7..feed121 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/CustomCompositeModel.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/CustomCompositeModel.java @@ -10,9 +10,20 @@ // model for subcircuits -class ExtListEntry { - ExtListEntry(String s, int n) { name = s; node = n; side = ChipElm.SIDE_W; } - ExtListEntry(String s, int n, int p, int sd) { name = s; node = n; pos = p; side = sd; } +public class ExtListEntry { + ExtListEntry(String s, int n) { + name = s; + node = n; + side = ChipElm.SIDE_W; + } + + ExtListEntry(String s, int n, int p, int sd) { + name = s; + node = n; + pos = p; + side = sd; + } + String name; int node, pos, side; }; @@ -20,7 +31,7 @@ class ExtListEntry { public class CustomCompositeModel implements Comparable { static HashMap modelMap; - + int flags, sizeX, sizeY; String name; String nodeList; @@ -31,87 +42,87 @@ public class CustomCompositeModel implements Comparable { boolean builtin; static int sequenceNumber; static final int FLAG_SHOW_LABEL = 1; - + void setName(String n) { - modelMap.remove(name); - name = n; - modelMap.put(name, this); - sequenceNumber++; + modelMap.remove(name); + name = n; + modelMap.put(name, this); + sequenceNumber++; } static void initModelMap() { - modelMap = new HashMap(); - - // create default stub model - Vector extList = new Vector(); - extList.add(new ExtListEntry("gnd", 1)); - CustomCompositeModel d = createModel("default", "0 0", "GroundElm 1", extList); - d.sizeX = d.sizeY = 1; - modelMap.put(d.name, d); - sequenceNumber = 1; - - // get models from local storage + modelMap = new HashMap(); + + // create default stub model + Vector extList = new Vector(); + extList.add(new ExtListEntry("gnd", 1)); + CustomCompositeModel d = createModel("default", "0 0", "GroundElm 1", extList); + d.sizeX = d.sizeY = 1; + modelMap.put(d.name, d); + sequenceNumber = 1; + + // get models from local storage Storage stor = Storage.getLocalStorageIfSupported(); if (stor != null) { int len = stor.getLength(); int i; for (i = 0; i != len; i++) { - String key = stor.key(i); - if (!key.startsWith("subcircuit:")) - continue; - String data = stor.getItem(key); - String firstLine = data; - int lineLen = data.indexOf('\n'); - if (lineLen != -1) - firstLine = data.substring(0, lineLen); - StringTokenizer st = new StringTokenizer(firstLine, " "); - if (st.nextToken() == ".") { - CustomCompositeModel model = undumpModel(st); - if (lineLen != -1) - model.modelCircuit = data.substring(lineLen+1); - } + String key = stor.key(i); + if (!key.startsWith("subcircuit:")) + continue; + String data = stor.getItem(key); + String firstLine = data; + int lineLen = data.indexOf('\n'); + if (lineLen != -1) + firstLine = data.substring(0, lineLen); + StringTokenizer st = new StringTokenizer(firstLine, " "); + if (st.nextToken() == ".") { + CustomCompositeModel model = undumpModel(st); + if (lineLen != -1) + model.modelCircuit = data.substring(lineLen + 1); + } } } - + loadBuiltinModels(); } - + static CustomCompositeModel getModelWithName(String name) { - if (modelMap == null) - initModelMap(); - CustomCompositeModel lm = modelMap.get(name); - return lm; + if (modelMap == null) + initModelMap(); + CustomCompositeModel lm = modelMap.get(name); + return lm; } static CustomCompositeModel createModel(String name, String elmDump, String nodeList, Vector extList) { - CustomCompositeModel lm = new CustomCompositeModel(); - lm.name = name; - lm.elmDump = elmDump; - lm.nodeList = nodeList; - lm.extList = extList; + CustomCompositeModel lm = new CustomCompositeModel(); + lm.name = name; + lm.elmDump = elmDump; + lm.nodeList = nodeList; + lm.extList = extList; modelMap.put(name, lm); sequenceNumber++; return lm; } static void clearDumpedFlags() { - if (modelMap == null) - return; - Iterator it = modelMap.entrySet().iterator(); - while (it.hasNext()) { - Map.Entry pair = (Map.Entry)it.next(); - pair.getValue().dumped = false; - } + if (modelMap == null) + return; + Iterator it = modelMap.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry pair = (Map.Entry) it.next(); + pair.getValue().dumped = false; + } } static Vector getModelList() { Vector vector = new Vector(); Iterator it = modelMap.entrySet().iterator(); while (it.hasNext()) { - Map.Entry pair = (Map.Entry)it.next(); + Map.Entry pair = (Map.Entry) it.next(); CustomCompositeModel dm = pair.getValue(); if (dm.builtin) - continue; + continue; vector.add(dm); } Collections.sort(vector); @@ -121,55 +132,55 @@ static Vector getModelList() { public int compareTo(CustomCompositeModel dm) { return name.compareTo(dm.name); } - + CustomCompositeModel() { } - + static CustomCompositeModel undumpModel(StringTokenizer st) { - String name = CustomLogicModel.unescape(st.nextToken()); + String name = CustomLogicModel.unescape(st.nextToken()); // CustomCompositeElm.lastModelName = name; - CustomCompositeModel model = getModelWithName(name); - if (model == null) { - model = new CustomCompositeModel(); - model.name = name; - modelMap.put(name, model); - sequenceNumber++; - } else if (model.modelCircuit != null) { - // if model has an associated model circuit, don't overwrite it. keep the old one. - CirSim.console("ignoring model " + name + ", using stored version instead"); - return model; - } - model.undump(st); - return model; - } - + CustomCompositeModel model = getModelWithName(name); + if (model == null) { + model = new CustomCompositeModel(); + model.name = name; + modelMap.put(name, model); + sequenceNumber++; + } else if (model.modelCircuit != null) { + // if model has an associated model circuit, don't overwrite it. keep the old one. + CirSim.console("ignoring model " + name + ", using stored version instead"); + return model; + } + model.undump(st); + return model; + } + void undump(StringTokenizer st) { - flags = Integer.parseInt(st.nextToken()); - sizeX = Integer.parseInt(st.nextToken()); - sizeY = Integer.parseInt(st.nextToken()); - int extCount = Integer.parseInt(st.nextToken()); - int i; - extList = new Vector(); - for (i = 0; i != extCount; i++) { - String s = CustomLogicModel.unescape(st.nextToken()); - int n = Integer.parseInt(st.nextToken()); - int p = Integer.parseInt(st.nextToken()); - int sd = Integer.parseInt(st.nextToken()); - extList.add(new ExtListEntry(s, n, p, sd)); - } - nodeList = CustomLogicModel.unescape(st.nextToken()); - elmDump = CustomLogicModel.unescape(st.nextToken()); - } - + flags = Integer.parseInt(st.nextToken()); + sizeX = Integer.parseInt(st.nextToken()); + sizeY = Integer.parseInt(st.nextToken()); + int extCount = Integer.parseInt(st.nextToken()); + int i; + extList = new Vector(); + for (i = 0; i != extCount; i++) { + String s = CustomLogicModel.unescape(st.nextToken()); + int n = Integer.parseInt(st.nextToken()); + int p = Integer.parseInt(st.nextToken()); + int sd = Integer.parseInt(st.nextToken()); + extList.add(new ExtListEntry(s, n, p, sd)); + } + nodeList = CustomLogicModel.unescape(st.nextToken()); + elmDump = CustomLogicModel.unescape(st.nextToken()); + } + boolean isSaved() { - if (name == null) - return false; + if (name == null) + return false; Storage stor = Storage.getLocalStorageIfSupported(); if (stor == null) return false; return stor.getItem("subcircuit:" + name) != null; } - + void setSaved(boolean sv) { Storage stor = Storage.getLocalStorageIfSupported(); if (stor == null) @@ -180,34 +191,36 @@ void setSaved(boolean sv) { } else stor.removeItem("subcircuit:" + name); } - + String arrayToList(String arr[]) { - if (arr == null) - return ""; - if (arr.length == 0) - return ""; - String x = arr[0]; - int i; - for (i = 1; i < arr.length; i++) - x += "," + arr[i]; - return x; - } - - boolean showLabel() { return (flags & FLAG_SHOW_LABEL) != 0; } - + if (arr == null) + return ""; + if (arr.length == 0) + return ""; + String x = arr[0]; + int i; + for (i = 1; i < arr.length; i++) + x += "," + arr[i]; + return x; + } + + boolean showLabel() { + return (flags & FLAG_SHOW_LABEL) != 0; + } + void setShowLabel(boolean sl) { - flags = (sl) ? (flags | FLAG_SHOW_LABEL) : (flags & ~FLAG_SHOW_LABEL); + flags = (sl) ? (flags | FLAG_SHOW_LABEL) : (flags & ~FLAG_SHOW_LABEL); } - String [] listToArray(String arr) { - return arr.split(","); + String[] listToArray(String arr) { + return arr.split(","); } - + String dump() { - if (builtin) - return ""; - dumped = true; - String str = ". " + CustomLogicModel.escape(name) + " " + flags + " " + sizeX + " " + sizeY + " " + extList.size() + " "; + if (builtin) + return ""; + dumped = true; + String str = ". " + CustomLogicModel.escape(name) + " " + flags + " " + sizeX + " " + sizeY + " " + extList.size() + " "; int i; for (i = 0; i != extList.size(); i++) { ExtListEntry ent = extList.get(i); @@ -218,23 +231,23 @@ String dump() { str += " " + CustomLogicModel.escape(nodeList) + " " + CustomLogicModel.escape(elmDump); return str; } - + boolean canLoadModelCircuit() { - return modelCircuit != null && modelCircuit.length() > 0; + return modelCircuit != null && modelCircuit.length() > 0; } - + static void loadBuiltinModels() { - String lm317 = ". ~LM317-v2 0 2 2 3 adj 2 1 1 in 1 0 2 out 3 0 3 JfetElm\\s3\\s4\\s1\\s\\rResistorElm\\s5\\s39\\rCapacitorElm\\s39\\s6\\rCapacitorElm\\s39\\s5\\rTransistorElm\\s39\\s5\\s6\\s\\rResistorElm\\s7\\s40\\rCapacitorElm\\s40\\s8\\rCapacitorElm\\s40\\s5\\rTransistorElm\\s40\\s5\\s8\\s\\rResistorElm\\s5\\s41\\rCapacitorElm\\s41\\s9\\rCapacitorElm\\s41\\s7\\rTransistorElm\\s41\\s7\\s9\\s\\rResistorElm\\s7\\s42\\rCapacitorElm\\s42\\s3\\rCapacitorElm\\s42\\s10\\rTransistorElm\\s42\\s10\\s3\\s\\rResistorElm\\s10\\s43\\rCapacitorElm\\s43\\s11\\rCapacitorElm\\s43\\s3\\rTransistorElm\\s43\\s3\\s11\\s\\rResistorElm\\s10\\s44\\rCapacitorElm\\s44\\s13\\rCapacitorElm\\s44\\s12\\rTransistorElm\\s44\\s12\\s13\\s\\rResistorElm\\s5\\s45\\rCapacitorElm\\s45\\s14\\rCapacitorElm\\s45\\s11\\rTransistorElm\\s45\\s11\\s14\\s\\rResistorElm\\s12\\s46\\rCapacitorElm\\s46\\s11\\rCapacitorElm\\s46\\s15\\rTransistorElm\\s46\\s15\\s11\\s\\rResistorElm\\s5\\s47\\rCapacitorElm\\s47\\s17\\rCapacitorElm\\s47\\s16\\rTransistorElm\\s47\\s16\\s17\\s\\rResistorElm\\s15\\s48\\rCapacitorElm\\s48\\s18\\rCapacitorElm\\s48\\s16\\rTransistorElm\\s48\\s16\\s18\\s\\rResistorElm\\s19\\s49\\rCapacitorElm\\s49\\s16\\rCapacitorElm\\s49\\s3\\rTransistorElm\\s49\\s3\\s16\\s\\rResistorElm\\s20\\s50\\rCapacitorElm\\s50\\s19\\rCapacitorElm\\s50\\s1\\rTransistorElm\\s50\\s1\\s19\\s\\rResistorElm\\s5\\s51\\rCapacitorElm\\s51\\s21\\rCapacitorElm\\s51\\s20\\rTransistorElm\\s51\\s20\\s21\\s\\rResistorElm\\s22\\s52\\rCapacitorElm\\s52\\s20\\rCapacitorElm\\s52\\s3\\rTransistorElm\\s52\\s3\\s20\\s\\rResistorElm\\s23\\s53\\rCapacitorElm\\s53\\s16\\rCapacitorElm\\s53\\s22\\rTransistorElm\\s53\\s22\\s16\\s\\rResistorElm\\s3\\s54\\rCapacitorElm\\s54\\s24\\rCapacitorElm\\s54\\s22\\rTransistorElm\\s54\\s22\\s24\\s\\rResistorElm\\s23\\s55\\rCapacitorElm\\s55\\s16\\rCapacitorElm\\s55\\s23\\rTransistorElm\\s55\\s23\\s16\\s\\rResistorElm\\s3\\s56\\rCapacitorElm\\s56\\s25\\rCapacitorElm\\s56\\s23\\rTransistorElm\\s56\\s23\\s25\\s\\rResistorElm\\s26\\s57\\rCapacitorElm\\s57\\s16\\rCapacitorElm\\s57\\s3\\rTransistorElm\\s57\\s3\\s16\\s\\rResistorElm\\s27\\s58\\rCapacitorElm\\s58\\s3\\rCapacitorElm\\s58\\s26\\rTransistorElm\\s58\\s26\\s3\\s\\rResistorElm\\s28\\s59\\rCapacitorElm\\s59\\s1\\rCapacitorElm\\s59\\s28\\rTransistorElm\\s59\\s28\\s1\\s\\rResistorElm\\s28\\s60\\rCapacitorElm\\s60\\s1\\rCapacitorElm\\s60\\s16\\rTransistorElm\\s60\\s16\\s1\\s\\rResistorElm\\s16\\s61\\rCapacitorElm\\s61\\s29\\rCapacitorElm\\s61\\s28\\rTransistorElm\\s61\\s28\\s29\\s\\rResistorElm\\s31\\s62\\rCapacitorElm\\s62\\s32\\rCapacitorElm\\s62\\s30\\rTransistorElm\\s62\\s30\\s32\\s\\rResistorElm\\s31\\s63\\rCapacitorElm\\s63\\s33\\rCapacitorElm\\s63\\s30\\rTransistorElm\\s63\\s30\\s33\\s\\rResistorElm\\s34\\s64\\rCapacitorElm\\s64\\s35\\rCapacitorElm\\s64\\s1\\rTransistorElm\\s64\\s1\\s35\\s\\rResistorElm\\s35\\s65\\rCapacitorElm\\s65\\s36\\rCapacitorElm\\s65\\s1\\rTransistorElm\\s65\\s1\\s36\\s\\rDiodeElm\\s3\\s4\\rDiodeElm\\s37\\s1\\rDiodeElm\\s32\\s38\\rResistorElm\\s1\\s6\\rResistorElm\\s1\\s9\\rResistorElm\\s1\\s14\\rResistorElm\\s1\\s17\\rResistorElm\\s1\\s21\\rResistorElm\\s4\\s7\\rResistorElm\\s7\\s10\\rResistorElm\\s11\\s12\\rResistorElm\\s8\\s3\\rResistorElm\\s13\\s3\\rResistorElm\\s15\\s3\\rResistorElm\\s18\\s3\\rResistorElm\\s19\\s3\\rResistorElm\\s2\\s24\\rResistorElm\\s24\\s25\\rResistorElm\\s16\\s26\\rResistorElm\\s16\\s31\\rResistorElm\\s29\\s35\\rResistorElm\\s16\\s34\\rResistorElm\\s27\\s30\\rResistorElm\\s30\\s31\\rResistorElm\\s3\\s35\\rResistorElm\\s37\\s38\\rResistorElm\\s33\\s32\\rResistorElm\\s33\\s36\\rResistorElm\\s36\\s3\\rCapacitorElm\\s22\\s3\\rCapacitorElm\\s22\\s2\\rCapacitorElm\\s26\\s27\\rCapacitorElm\\s5\\s3\\rCapacitorElm\\s28\\s3\\rCapacitorElm\\s23\\s3\\r 0\\\\s-7\\\\s0.0001\\s0\\\\s200\\s2\\\\s1.5000000000000002e-13\\\\s0\\\\s0\\s2\\\\s1e-13\\\\s0\\\\s0\\s0\\\\s-1\\\\s0\\\\s0\\\\s40\\\\s~lm317-qpl-A0.1\\s0\\\\s500\\s2\\\\s4e-13\\\\s0\\\\s0\\s2\\\\s2e-13\\\\s0\\\\s0\\s0\\\\s1\\\\s0\\\\s0\\\\s80\\\\s~lm317-qnl-A0.2\\s0\\\\s200\\s2\\\\s1.5000000000000002e-13\\\\s0\\\\s0\\s2\\\\s1e-13\\\\s0\\\\s0\\s0\\\\s-1\\\\s0\\\\s0\\\\s40\\\\s~lm317-qpl-A0.1\\s0\\\\s500\\s2\\\\s4e-13\\\\s0\\\\s0\\s2\\\\s2e-13\\\\s0\\\\s0\\s0\\\\s1\\\\s0\\\\s0\\\\s80\\\\s~lm317-qnl-A0.2\\s0\\\\s100\\s2\\\\s3.0000000000000003e-13\\\\s0\\\\s0\\s2\\\\s2e-13\\\\s0\\\\s0\\s0\\\\s-1\\\\s0\\\\s0\\\\s40\\\\s~lm317-qpl-A0.2\\s0\\\\s500\\s2\\\\s4e-13\\\\s0\\\\s0\\s2\\\\s2e-13\\\\s0\\\\s0\\s0\\\\s1\\\\s0\\\\s0\\\\s80\\\\s~lm317-qnl-A0.2\\s0\\\\s100\\s2\\\\s3.0000000000000003e-13\\\\s0\\\\s0\\s2\\\\s2e-13\\\\s0\\\\s0\\s0\\\\s-1\\\\s0\\\\s0\\\\s40\\\\s~lm317-qpl-A0.2\\s0\\\\s100\\s2\\\\s3.0000000000000003e-13\\\\s0\\\\s0\\s2\\\\s2e-13\\\\s0\\\\s0\\s0\\\\s-1\\\\s0\\\\s0\\\\s40\\\\s~lm317-qpl-A0.2\\s0\\\\s100\\s2\\\\s3.0000000000000003e-13\\\\s0\\\\s0\\s2\\\\s2e-13\\\\s0\\\\s0\\s0\\\\s-1\\\\s0\\\\s0\\\\s40\\\\s~lm317-qpl-A0.2\\s0\\\\s500\\s2\\\\s4e-13\\\\s0\\\\s0\\s2\\\\s2e-13\\\\s0\\\\s0\\s0\\\\s1\\\\s0\\\\s0\\\\s80\\\\s~lm317-qnl-A0.2\\s0\\\\s100\\s2\\\\s3.0000000000000003e-13\\\\s0\\\\s0\\s2\\\\s2e-13\\\\s0\\\\s0\\s0\\\\s-1\\\\s0\\\\s0\\\\s40\\\\s~lm317-qpl-A0.2\\s0\\\\s500\\s2\\\\s4e-13\\\\s0\\\\s0\\s2\\\\s2e-13\\\\s0\\\\s0\\s0\\\\s1\\\\s0\\\\s0\\\\s80\\\\s~lm317-qnl-A0.2\\s0\\\\s100\\s2\\\\s3.0000000000000003e-13\\\\s0\\\\s0\\s2\\\\s2e-13\\\\s0\\\\s0\\s0\\\\s-1\\\\s0\\\\s0\\\\s40\\\\s~lm317-qpl-A0.2\\s0\\\\s100\\s2\\\\s3.0000000000000003e-13\\\\s0\\\\s0\\s2\\\\s2e-13\\\\s0\\\\s0\\s0\\\\s-1\\\\s0\\\\s0\\\\s40\\\\s~lm317-qpl-A0.2\\s0\\\\s100\\s2\\\\s3.0000000000000003e-13\\\\s0\\\\s0\\s2\\\\s2e-13\\\\s0\\\\s0\\s0\\\\s-1\\\\s0\\\\s0\\\\s40\\\\s~lm317-qpl-A0.2\\s0\\\\s500\\s2\\\\s4e-13\\\\s0\\\\s0\\s2\\\\s2e-13\\\\s0\\\\s0\\s0\\\\s1\\\\s0\\\\s0\\\\s80\\\\s~lm317-qnl-A0.2\\s0\\\\s100\\s2\\\\s3.0000000000000003e-13\\\\s0\\\\s0\\s2\\\\s2e-13\\\\s0\\\\s0\\s0\\\\s-1\\\\s0\\\\s0\\\\s40\\\\s~lm317-qpl-A0.2\\s0\\\\s50\\s2\\\\s4e-12\\\\s0\\\\s0\\s2\\\\s2e-12\\\\s0\\\\s0\\s0\\\\s1\\\\s0\\\\s0\\\\s80\\\\s~lm317-qnl-A2\\s0\\\\s100\\s2\\\\s3.0000000000000003e-13\\\\s0\\\\s0\\s2\\\\s2e-13\\\\s0\\\\s0\\s0\\\\s-1\\\\s0\\\\s0\\\\s40\\\\s~lm317-qpl-A0.2\\s0\\\\s500\\s2\\\\s4e-13\\\\s0\\\\s0\\s2\\\\s2e-13\\\\s0\\\\s0\\s0\\\\s1\\\\s0\\\\s0\\\\s80\\\\s~lm317-qnl-A0.2\\s0\\\\s10\\s2\\\\s3e-12\\\\s0\\\\s0\\s2\\\\s2e-12\\\\s0\\\\s0\\s0\\\\s-1\\\\s0\\\\s0\\\\s40\\\\s~lm317-qpl-A2\\s0\\\\s10\\s2\\\\s3e-12\\\\s0\\\\s0\\s2\\\\s2e-12\\\\s0\\\\s0\\s0\\\\s-1\\\\s0\\\\s0\\\\s40\\\\s~lm317-qpl-A2\\s0\\\\s50\\s2\\\\s4e-12\\\\s0\\\\s0\\s2\\\\s2e-12\\\\s0\\\\s0\\s0\\\\s1\\\\s0\\\\s0\\\\s80\\\\s~lm317-qnl-A2\\s0\\\\s500\\s2\\\\s4e-13\\\\s0\\\\s0\\s2\\\\s2e-13\\\\s0\\\\s0\\s0\\\\s1\\\\s0\\\\s0\\\\s80\\\\s~lm317-qnl-A0.2\\s0\\\\s500\\s2\\\\s4e-13\\\\s0\\\\s0\\s2\\\\s2e-13\\\\s0\\\\s0\\s0\\\\s1\\\\s0\\\\s0\\\\s80\\\\s~lm317-qnl-A0.2\\s0\\\\s20\\s2\\\\s1e-11\\\\s0\\\\s0\\s2\\\\s5e-12\\\\s0\\\\s0\\s0\\\\s1\\\\s0\\\\s0\\\\s80\\\\s~lm317-qnl-A5\\s0\\\\s2\\s2\\\\s1e-10\\\\s0\\\\s0\\s2\\\\s5e-11\\\\s0\\\\s0\\s0\\\\s1\\\\s0\\\\s0\\\\s80\\\\s~lm317-qnl-A50\\s2\\\\s~lm317-dz\\s2\\\\s~lm317-dz\\s2\\\\s~lm317-dz\\s0\\\\s310\\s0\\\\s310\\s0\\\\s190\\s0\\\\s82\\s0\\\\s5600\\s0\\\\s100000\\s0\\\\s130\\s0\\\\s12400\\s0\\\\s180\\s0\\\\s4100\\s0\\\\s5800\\s0\\\\s72\\s0\\\\s5100\\s0\\\\s12000\\s0\\\\s2400\\s0\\\\s6700\\s0\\\\s12000\\s0\\\\s130\\s0\\\\s370\\s0\\\\s13000\\s0\\\\s400\\s0\\\\s160\\s0\\\\s18000\\s0\\\\s160\\s0\\\\s3\\s0\\\\s0.1\\s2\\\\s3e-11\\\\s0\\\\s0\\s2\\\\s3e-11\\\\s0\\\\s0\\s2\\\\s5e-12\\\\s0\\\\s0\\s2\\\\s2e-12\\\\s0\\\\s0\\s2\\\\s1e-12\\\\s0\\\\s0\\s2\\\\s1e-12\\\\s0\\\\s0"; - String tl431 = ". ~TL431 0 1 3 3 A 2 0 1 C 1 0 0 ref 3 1 2 ResistorElm\\s3\\s18\\rCapacitorElm\\s18\\s4\\rCapacitorElm\\s18\\s1\\rTransistorElm\\s18\\s1\\s4\\s\\rResistorElm\\s4\\s5\\rResistorElm\\s5\\s6\\rResistorElm\\s5\\s7\\rResistorElm\\s6\\s19\\rCapacitorElm\\s19\\s2\\rCapacitorElm\\s19\\s6\\rTransistorElm\\s19\\s6\\s2\\s\\rResistorElm\\s6\\s20\\rCapacitorElm\\s20\\s8\\rCapacitorElm\\s20\\s7\\rTransistorElm\\s20\\s7\\s8\\s\\rResistorElm\\s8\\s2\\rResistorElm\\s4\\s21\\rCapacitorElm\\s21\\s10\\rCapacitorElm\\s21\\s9\\rTransistorElm\\s21\\s9\\s10\\s\\rResistorElm\\s10\\s11\\rResistorElm\\s7\\s22\\rCapacitorElm\\s22\\s2\\rCapacitorElm\\s22\\s11\\rTransistorElm\\s22\\s11\\s2\\s\\rResistorElm\\s13\\s23\\rCapacitorElm\\s23\\s2\\rCapacitorElm\\s23\\s12\\rTransistorElm\\s23\\s12\\s2\\s\\rResistorElm\\s9\\s24\\rCapacitorElm\\s24\\s14\\rCapacitorElm\\s24\\s9\\rTransistorElm\\s24\\s9\\s14\\s\\rResistorElm\\s9\\s25\\rCapacitorElm\\s25\\s15\\rCapacitorElm\\s25\\s12\\rTransistorElm\\s25\\s12\\s15\\s\\rResistorElm\\s1\\s14\\rResistorElm\\s1\\s15\\rResistorElm\\s12\\s26\\rCapacitorElm\\s26\\s16\\rCapacitorElm\\s26\\s1\\rTransistorElm\\s26\\s1\\s16\\s\\rResistorElm\\s17\\s16\\rResistorElm\\s17\\s27\\rCapacitorElm\\s27\\s2\\rCapacitorElm\\s27\\s1\\rTransistorElm\\s27\\s1\\s2\\s\\rResistorElm\\s17\\s2\\rResistorElm\\s12\\s28\\rCapacitorElm\\s28\\s3\\rCapacitorElm\\s28\\s12\\rTransistorElm\\s28\\s12\\s3\\s\\rDiodeElm\\s2\\s12\\rResistorElm\\s13\\s6\\rDiodeElm\\s2\\s1\\rCapacitorElm\\s1\\s12\\rCapacitorElm\\s7\\s11\\r 0\\\\s40\\s2\\\\s1e-12\\\\s0\\\\s0\\s2\\\\s2e-12\\\\s0\\\\s0\\s0\\\\s1\\\\s0\\\\s0\\\\s140\\\\s~tl431ed-qn_ed\\s0\\\\s3280\\s0\\\\s2400\\s0\\\\s7200\\s0\\\\s33.333333333333336\\s2\\\\s1.2e-12\\\\s0\\\\s0\\s2\\\\s2.4e-12\\\\s0\\\\s0\\s0\\\\s1\\\\s0\\\\s0\\\\s140\\\\s~tl431ed-qn_ed-A1.2\\s0\\\\s18.18181818181818\\s2\\\\s2.2000000000000003e-12\\\\s0\\\\s0\\s2\\\\s4.400000000000001e-12\\\\s0\\\\s0\\s0\\\\s1\\\\s0\\\\s0\\\\s140\\\\s~tl431ed-qn_ed-A2.2\\s0\\\\s800\\s0\\\\s40\\s2\\\\s1e-12\\\\s0\\\\s0\\s2\\\\s2e-12\\\\s0\\\\s0\\s0\\\\s1\\\\s0\\\\s0\\\\s140\\\\s~tl431ed-qn_ed\\s0\\\\s4000\\s0\\\\s40\\s2\\\\s1e-12\\\\s0\\\\s0\\s2\\\\s2e-12\\\\s0\\\\s0\\s0\\\\s1\\\\s0\\\\s0\\\\s140\\\\s~tl431ed-qn_ed\\s0\\\\s80\\s2\\\\s5e-13\\\\s0\\\\s0\\s2\\\\s1e-12\\\\s0\\\\s0\\s0\\\\s1\\\\s0\\\\s0\\\\s140\\\\s~tl431ed-qn_ed-A0.5\\s0\\\\s80\\s2\\\\s1e-12\\\\s0\\\\s0\\s2\\\\s3e-12\\\\s0\\\\s0\\s0\\\\s-1\\\\s0\\\\s0\\\\s60\\\\s~tl431ed-qp_ed\\s0\\\\s80\\s2\\\\s1e-12\\\\s0\\\\s0\\s2\\\\s3e-12\\\\s0\\\\s0\\s0\\\\s-1\\\\s0\\\\s0\\\\s60\\\\s~tl431ed-qp_ed\\s0\\\\s800\\s0\\\\s800\\s0\\\\s40\\s2\\\\s1e-12\\\\s0\\\\s0\\s2\\\\s2e-12\\\\s0\\\\s0\\s0\\\\s1\\\\s0\\\\s0\\\\s140\\\\s~tl431ed-qn_ed\\s0\\\\s150\\s0\\\\s8\\s2\\\\s5e-12\\\\s0\\\\s0\\s2\\\\s1e-11\\\\s0\\\\s0\\s0\\\\s1\\\\s0\\\\s0\\\\s140\\\\s~tl431ed-qn_ed-A5\\s0\\\\s10000\\s0\\\\s40\\s2\\\\s1e-12\\\\s0\\\\s0\\s2\\\\s2e-12\\\\s0\\\\s0\\s0\\\\s1\\\\s0\\\\s0\\\\s140\\\\s~tl431ed-qn_ed\\s2\\\\s~tl431ed-d_ed\\s0\\\\s1000\\s2\\\\s~tl431ed-d_ed\\s2\\\\s1e-11\\\\s0\\\\s0\\s2\\\\s2e-11\\\\s0\\\\s0"; - - - String models[] = { lm317, tl431 }; - int i; - for (i = 0; i != models.length; i++) { - StringTokenizer st = new StringTokenizer(models[i], " "); - st.nextToken(); - CustomCompositeModel model = undumpModel(st); - model.builtin = true; - } + String lm317 = ". ~LM317-v2 0 2 2 3 adj 2 1 1 in 1 0 2 out 3 0 3 JfetElm\\s3\\s4\\s1\\s\\rResistorElm\\s5\\s39\\rCapacitorElm\\s39\\s6\\rCapacitorElm\\s39\\s5\\rTransistorElm\\s39\\s5\\s6\\s\\rResistorElm\\s7\\s40\\rCapacitorElm\\s40\\s8\\rCapacitorElm\\s40\\s5\\rTransistorElm\\s40\\s5\\s8\\s\\rResistorElm\\s5\\s41\\rCapacitorElm\\s41\\s9\\rCapacitorElm\\s41\\s7\\rTransistorElm\\s41\\s7\\s9\\s\\rResistorElm\\s7\\s42\\rCapacitorElm\\s42\\s3\\rCapacitorElm\\s42\\s10\\rTransistorElm\\s42\\s10\\s3\\s\\rResistorElm\\s10\\s43\\rCapacitorElm\\s43\\s11\\rCapacitorElm\\s43\\s3\\rTransistorElm\\s43\\s3\\s11\\s\\rResistorElm\\s10\\s44\\rCapacitorElm\\s44\\s13\\rCapacitorElm\\s44\\s12\\rTransistorElm\\s44\\s12\\s13\\s\\rResistorElm\\s5\\s45\\rCapacitorElm\\s45\\s14\\rCapacitorElm\\s45\\s11\\rTransistorElm\\s45\\s11\\s14\\s\\rResistorElm\\s12\\s46\\rCapacitorElm\\s46\\s11\\rCapacitorElm\\s46\\s15\\rTransistorElm\\s46\\s15\\s11\\s\\rResistorElm\\s5\\s47\\rCapacitorElm\\s47\\s17\\rCapacitorElm\\s47\\s16\\rTransistorElm\\s47\\s16\\s17\\s\\rResistorElm\\s15\\s48\\rCapacitorElm\\s48\\s18\\rCapacitorElm\\s48\\s16\\rTransistorElm\\s48\\s16\\s18\\s\\rResistorElm\\s19\\s49\\rCapacitorElm\\s49\\s16\\rCapacitorElm\\s49\\s3\\rTransistorElm\\s49\\s3\\s16\\s\\rResistorElm\\s20\\s50\\rCapacitorElm\\s50\\s19\\rCapacitorElm\\s50\\s1\\rTransistorElm\\s50\\s1\\s19\\s\\rResistorElm\\s5\\s51\\rCapacitorElm\\s51\\s21\\rCapacitorElm\\s51\\s20\\rTransistorElm\\s51\\s20\\s21\\s\\rResistorElm\\s22\\s52\\rCapacitorElm\\s52\\s20\\rCapacitorElm\\s52\\s3\\rTransistorElm\\s52\\s3\\s20\\s\\rResistorElm\\s23\\s53\\rCapacitorElm\\s53\\s16\\rCapacitorElm\\s53\\s22\\rTransistorElm\\s53\\s22\\s16\\s\\rResistorElm\\s3\\s54\\rCapacitorElm\\s54\\s24\\rCapacitorElm\\s54\\s22\\rTransistorElm\\s54\\s22\\s24\\s\\rResistorElm\\s23\\s55\\rCapacitorElm\\s55\\s16\\rCapacitorElm\\s55\\s23\\rTransistorElm\\s55\\s23\\s16\\s\\rResistorElm\\s3\\s56\\rCapacitorElm\\s56\\s25\\rCapacitorElm\\s56\\s23\\rTransistorElm\\s56\\s23\\s25\\s\\rResistorElm\\s26\\s57\\rCapacitorElm\\s57\\s16\\rCapacitorElm\\s57\\s3\\rTransistorElm\\s57\\s3\\s16\\s\\rResistorElm\\s27\\s58\\rCapacitorElm\\s58\\s3\\rCapacitorElm\\s58\\s26\\rTransistorElm\\s58\\s26\\s3\\s\\rResistorElm\\s28\\s59\\rCapacitorElm\\s59\\s1\\rCapacitorElm\\s59\\s28\\rTransistorElm\\s59\\s28\\s1\\s\\rResistorElm\\s28\\s60\\rCapacitorElm\\s60\\s1\\rCapacitorElm\\s60\\s16\\rTransistorElm\\s60\\s16\\s1\\s\\rResistorElm\\s16\\s61\\rCapacitorElm\\s61\\s29\\rCapacitorElm\\s61\\s28\\rTransistorElm\\s61\\s28\\s29\\s\\rResistorElm\\s31\\s62\\rCapacitorElm\\s62\\s32\\rCapacitorElm\\s62\\s30\\rTransistorElm\\s62\\s30\\s32\\s\\rResistorElm\\s31\\s63\\rCapacitorElm\\s63\\s33\\rCapacitorElm\\s63\\s30\\rTransistorElm\\s63\\s30\\s33\\s\\rResistorElm\\s34\\s64\\rCapacitorElm\\s64\\s35\\rCapacitorElm\\s64\\s1\\rTransistorElm\\s64\\s1\\s35\\s\\rResistorElm\\s35\\s65\\rCapacitorElm\\s65\\s36\\rCapacitorElm\\s65\\s1\\rTransistorElm\\s65\\s1\\s36\\s\\rDiodeElm\\s3\\s4\\rDiodeElm\\s37\\s1\\rDiodeElm\\s32\\s38\\rResistorElm\\s1\\s6\\rResistorElm\\s1\\s9\\rResistorElm\\s1\\s14\\rResistorElm\\s1\\s17\\rResistorElm\\s1\\s21\\rResistorElm\\s4\\s7\\rResistorElm\\s7\\s10\\rResistorElm\\s11\\s12\\rResistorElm\\s8\\s3\\rResistorElm\\s13\\s3\\rResistorElm\\s15\\s3\\rResistorElm\\s18\\s3\\rResistorElm\\s19\\s3\\rResistorElm\\s2\\s24\\rResistorElm\\s24\\s25\\rResistorElm\\s16\\s26\\rResistorElm\\s16\\s31\\rResistorElm\\s29\\s35\\rResistorElm\\s16\\s34\\rResistorElm\\s27\\s30\\rResistorElm\\s30\\s31\\rResistorElm\\s3\\s35\\rResistorElm\\s37\\s38\\rResistorElm\\s33\\s32\\rResistorElm\\s33\\s36\\rResistorElm\\s36\\s3\\rCapacitorElm\\s22\\s3\\rCapacitorElm\\s22\\s2\\rCapacitorElm\\s26\\s27\\rCapacitorElm\\s5\\s3\\rCapacitorElm\\s28\\s3\\rCapacitorElm\\s23\\s3\\r 0\\\\s-7\\\\s0.0001\\s0\\\\s200\\s2\\\\s1.5000000000000002e-13\\\\s0\\\\s0\\s2\\\\s1e-13\\\\s0\\\\s0\\s0\\\\s-1\\\\s0\\\\s0\\\\s40\\\\s~lm317-qpl-A0.1\\s0\\\\s500\\s2\\\\s4e-13\\\\s0\\\\s0\\s2\\\\s2e-13\\\\s0\\\\s0\\s0\\\\s1\\\\s0\\\\s0\\\\s80\\\\s~lm317-qnl-A0.2\\s0\\\\s200\\s2\\\\s1.5000000000000002e-13\\\\s0\\\\s0\\s2\\\\s1e-13\\\\s0\\\\s0\\s0\\\\s-1\\\\s0\\\\s0\\\\s40\\\\s~lm317-qpl-A0.1\\s0\\\\s500\\s2\\\\s4e-13\\\\s0\\\\s0\\s2\\\\s2e-13\\\\s0\\\\s0\\s0\\\\s1\\\\s0\\\\s0\\\\s80\\\\s~lm317-qnl-A0.2\\s0\\\\s100\\s2\\\\s3.0000000000000003e-13\\\\s0\\\\s0\\s2\\\\s2e-13\\\\s0\\\\s0\\s0\\\\s-1\\\\s0\\\\s0\\\\s40\\\\s~lm317-qpl-A0.2\\s0\\\\s500\\s2\\\\s4e-13\\\\s0\\\\s0\\s2\\\\s2e-13\\\\s0\\\\s0\\s0\\\\s1\\\\s0\\\\s0\\\\s80\\\\s~lm317-qnl-A0.2\\s0\\\\s100\\s2\\\\s3.0000000000000003e-13\\\\s0\\\\s0\\s2\\\\s2e-13\\\\s0\\\\s0\\s0\\\\s-1\\\\s0\\\\s0\\\\s40\\\\s~lm317-qpl-A0.2\\s0\\\\s100\\s2\\\\s3.0000000000000003e-13\\\\s0\\\\s0\\s2\\\\s2e-13\\\\s0\\\\s0\\s0\\\\s-1\\\\s0\\\\s0\\\\s40\\\\s~lm317-qpl-A0.2\\s0\\\\s100\\s2\\\\s3.0000000000000003e-13\\\\s0\\\\s0\\s2\\\\s2e-13\\\\s0\\\\s0\\s0\\\\s-1\\\\s0\\\\s0\\\\s40\\\\s~lm317-qpl-A0.2\\s0\\\\s500\\s2\\\\s4e-13\\\\s0\\\\s0\\s2\\\\s2e-13\\\\s0\\\\s0\\s0\\\\s1\\\\s0\\\\s0\\\\s80\\\\s~lm317-qnl-A0.2\\s0\\\\s100\\s2\\\\s3.0000000000000003e-13\\\\s0\\\\s0\\s2\\\\s2e-13\\\\s0\\\\s0\\s0\\\\s-1\\\\s0\\\\s0\\\\s40\\\\s~lm317-qpl-A0.2\\s0\\\\s500\\s2\\\\s4e-13\\\\s0\\\\s0\\s2\\\\s2e-13\\\\s0\\\\s0\\s0\\\\s1\\\\s0\\\\s0\\\\s80\\\\s~lm317-qnl-A0.2\\s0\\\\s100\\s2\\\\s3.0000000000000003e-13\\\\s0\\\\s0\\s2\\\\s2e-13\\\\s0\\\\s0\\s0\\\\s-1\\\\s0\\\\s0\\\\s40\\\\s~lm317-qpl-A0.2\\s0\\\\s100\\s2\\\\s3.0000000000000003e-13\\\\s0\\\\s0\\s2\\\\s2e-13\\\\s0\\\\s0\\s0\\\\s-1\\\\s0\\\\s0\\\\s40\\\\s~lm317-qpl-A0.2\\s0\\\\s100\\s2\\\\s3.0000000000000003e-13\\\\s0\\\\s0\\s2\\\\s2e-13\\\\s0\\\\s0\\s0\\\\s-1\\\\s0\\\\s0\\\\s40\\\\s~lm317-qpl-A0.2\\s0\\\\s500\\s2\\\\s4e-13\\\\s0\\\\s0\\s2\\\\s2e-13\\\\s0\\\\s0\\s0\\\\s1\\\\s0\\\\s0\\\\s80\\\\s~lm317-qnl-A0.2\\s0\\\\s100\\s2\\\\s3.0000000000000003e-13\\\\s0\\\\s0\\s2\\\\s2e-13\\\\s0\\\\s0\\s0\\\\s-1\\\\s0\\\\s0\\\\s40\\\\s~lm317-qpl-A0.2\\s0\\\\s50\\s2\\\\s4e-12\\\\s0\\\\s0\\s2\\\\s2e-12\\\\s0\\\\s0\\s0\\\\s1\\\\s0\\\\s0\\\\s80\\\\s~lm317-qnl-A2\\s0\\\\s100\\s2\\\\s3.0000000000000003e-13\\\\s0\\\\s0\\s2\\\\s2e-13\\\\s0\\\\s0\\s0\\\\s-1\\\\s0\\\\s0\\\\s40\\\\s~lm317-qpl-A0.2\\s0\\\\s500\\s2\\\\s4e-13\\\\s0\\\\s0\\s2\\\\s2e-13\\\\s0\\\\s0\\s0\\\\s1\\\\s0\\\\s0\\\\s80\\\\s~lm317-qnl-A0.2\\s0\\\\s10\\s2\\\\s3e-12\\\\s0\\\\s0\\s2\\\\s2e-12\\\\s0\\\\s0\\s0\\\\s-1\\\\s0\\\\s0\\\\s40\\\\s~lm317-qpl-A2\\s0\\\\s10\\s2\\\\s3e-12\\\\s0\\\\s0\\s2\\\\s2e-12\\\\s0\\\\s0\\s0\\\\s-1\\\\s0\\\\s0\\\\s40\\\\s~lm317-qpl-A2\\s0\\\\s50\\s2\\\\s4e-12\\\\s0\\\\s0\\s2\\\\s2e-12\\\\s0\\\\s0\\s0\\\\s1\\\\s0\\\\s0\\\\s80\\\\s~lm317-qnl-A2\\s0\\\\s500\\s2\\\\s4e-13\\\\s0\\\\s0\\s2\\\\s2e-13\\\\s0\\\\s0\\s0\\\\s1\\\\s0\\\\s0\\\\s80\\\\s~lm317-qnl-A0.2\\s0\\\\s500\\s2\\\\s4e-13\\\\s0\\\\s0\\s2\\\\s2e-13\\\\s0\\\\s0\\s0\\\\s1\\\\s0\\\\s0\\\\s80\\\\s~lm317-qnl-A0.2\\s0\\\\s20\\s2\\\\s1e-11\\\\s0\\\\s0\\s2\\\\s5e-12\\\\s0\\\\s0\\s0\\\\s1\\\\s0\\\\s0\\\\s80\\\\s~lm317-qnl-A5\\s0\\\\s2\\s2\\\\s1e-10\\\\s0\\\\s0\\s2\\\\s5e-11\\\\s0\\\\s0\\s0\\\\s1\\\\s0\\\\s0\\\\s80\\\\s~lm317-qnl-A50\\s2\\\\s~lm317-dz\\s2\\\\s~lm317-dz\\s2\\\\s~lm317-dz\\s0\\\\s310\\s0\\\\s310\\s0\\\\s190\\s0\\\\s82\\s0\\\\s5600\\s0\\\\s100000\\s0\\\\s130\\s0\\\\s12400\\s0\\\\s180\\s0\\\\s4100\\s0\\\\s5800\\s0\\\\s72\\s0\\\\s5100\\s0\\\\s12000\\s0\\\\s2400\\s0\\\\s6700\\s0\\\\s12000\\s0\\\\s130\\s0\\\\s370\\s0\\\\s13000\\s0\\\\s400\\s0\\\\s160\\s0\\\\s18000\\s0\\\\s160\\s0\\\\s3\\s0\\\\s0.1\\s2\\\\s3e-11\\\\s0\\\\s0\\s2\\\\s3e-11\\\\s0\\\\s0\\s2\\\\s5e-12\\\\s0\\\\s0\\s2\\\\s2e-12\\\\s0\\\\s0\\s2\\\\s1e-12\\\\s0\\\\s0\\s2\\\\s1e-12\\\\s0\\\\s0"; + String tl431 = ". ~TL431 0 1 3 3 A 2 0 1 C 1 0 0 ref 3 1 2 ResistorElm\\s3\\s18\\rCapacitorElm\\s18\\s4\\rCapacitorElm\\s18\\s1\\rTransistorElm\\s18\\s1\\s4\\s\\rResistorElm\\s4\\s5\\rResistorElm\\s5\\s6\\rResistorElm\\s5\\s7\\rResistorElm\\s6\\s19\\rCapacitorElm\\s19\\s2\\rCapacitorElm\\s19\\s6\\rTransistorElm\\s19\\s6\\s2\\s\\rResistorElm\\s6\\s20\\rCapacitorElm\\s20\\s8\\rCapacitorElm\\s20\\s7\\rTransistorElm\\s20\\s7\\s8\\s\\rResistorElm\\s8\\s2\\rResistorElm\\s4\\s21\\rCapacitorElm\\s21\\s10\\rCapacitorElm\\s21\\s9\\rTransistorElm\\s21\\s9\\s10\\s\\rResistorElm\\s10\\s11\\rResistorElm\\s7\\s22\\rCapacitorElm\\s22\\s2\\rCapacitorElm\\s22\\s11\\rTransistorElm\\s22\\s11\\s2\\s\\rResistorElm\\s13\\s23\\rCapacitorElm\\s23\\s2\\rCapacitorElm\\s23\\s12\\rTransistorElm\\s23\\s12\\s2\\s\\rResistorElm\\s9\\s24\\rCapacitorElm\\s24\\s14\\rCapacitorElm\\s24\\s9\\rTransistorElm\\s24\\s9\\s14\\s\\rResistorElm\\s9\\s25\\rCapacitorElm\\s25\\s15\\rCapacitorElm\\s25\\s12\\rTransistorElm\\s25\\s12\\s15\\s\\rResistorElm\\s1\\s14\\rResistorElm\\s1\\s15\\rResistorElm\\s12\\s26\\rCapacitorElm\\s26\\s16\\rCapacitorElm\\s26\\s1\\rTransistorElm\\s26\\s1\\s16\\s\\rResistorElm\\s17\\s16\\rResistorElm\\s17\\s27\\rCapacitorElm\\s27\\s2\\rCapacitorElm\\s27\\s1\\rTransistorElm\\s27\\s1\\s2\\s\\rResistorElm\\s17\\s2\\rResistorElm\\s12\\s28\\rCapacitorElm\\s28\\s3\\rCapacitorElm\\s28\\s12\\rTransistorElm\\s28\\s12\\s3\\s\\rDiodeElm\\s2\\s12\\rResistorElm\\s13\\s6\\rDiodeElm\\s2\\s1\\rCapacitorElm\\s1\\s12\\rCapacitorElm\\s7\\s11\\r 0\\\\s40\\s2\\\\s1e-12\\\\s0\\\\s0\\s2\\\\s2e-12\\\\s0\\\\s0\\s0\\\\s1\\\\s0\\\\s0\\\\s140\\\\s~tl431ed-qn_ed\\s0\\\\s3280\\s0\\\\s2400\\s0\\\\s7200\\s0\\\\s33.333333333333336\\s2\\\\s1.2e-12\\\\s0\\\\s0\\s2\\\\s2.4e-12\\\\s0\\\\s0\\s0\\\\s1\\\\s0\\\\s0\\\\s140\\\\s~tl431ed-qn_ed-A1.2\\s0\\\\s18.18181818181818\\s2\\\\s2.2000000000000003e-12\\\\s0\\\\s0\\s2\\\\s4.400000000000001e-12\\\\s0\\\\s0\\s0\\\\s1\\\\s0\\\\s0\\\\s140\\\\s~tl431ed-qn_ed-A2.2\\s0\\\\s800\\s0\\\\s40\\s2\\\\s1e-12\\\\s0\\\\s0\\s2\\\\s2e-12\\\\s0\\\\s0\\s0\\\\s1\\\\s0\\\\s0\\\\s140\\\\s~tl431ed-qn_ed\\s0\\\\s4000\\s0\\\\s40\\s2\\\\s1e-12\\\\s0\\\\s0\\s2\\\\s2e-12\\\\s0\\\\s0\\s0\\\\s1\\\\s0\\\\s0\\\\s140\\\\s~tl431ed-qn_ed\\s0\\\\s80\\s2\\\\s5e-13\\\\s0\\\\s0\\s2\\\\s1e-12\\\\s0\\\\s0\\s0\\\\s1\\\\s0\\\\s0\\\\s140\\\\s~tl431ed-qn_ed-A0.5\\s0\\\\s80\\s2\\\\s1e-12\\\\s0\\\\s0\\s2\\\\s3e-12\\\\s0\\\\s0\\s0\\\\s-1\\\\s0\\\\s0\\\\s60\\\\s~tl431ed-qp_ed\\s0\\\\s80\\s2\\\\s1e-12\\\\s0\\\\s0\\s2\\\\s3e-12\\\\s0\\\\s0\\s0\\\\s-1\\\\s0\\\\s0\\\\s60\\\\s~tl431ed-qp_ed\\s0\\\\s800\\s0\\\\s800\\s0\\\\s40\\s2\\\\s1e-12\\\\s0\\\\s0\\s2\\\\s2e-12\\\\s0\\\\s0\\s0\\\\s1\\\\s0\\\\s0\\\\s140\\\\s~tl431ed-qn_ed\\s0\\\\s150\\s0\\\\s8\\s2\\\\s5e-12\\\\s0\\\\s0\\s2\\\\s1e-11\\\\s0\\\\s0\\s0\\\\s1\\\\s0\\\\s0\\\\s140\\\\s~tl431ed-qn_ed-A5\\s0\\\\s10000\\s0\\\\s40\\s2\\\\s1e-12\\\\s0\\\\s0\\s2\\\\s2e-12\\\\s0\\\\s0\\s0\\\\s1\\\\s0\\\\s0\\\\s140\\\\s~tl431ed-qn_ed\\s2\\\\s~tl431ed-d_ed\\s0\\\\s1000\\s2\\\\s~tl431ed-d_ed\\s2\\\\s1e-11\\\\s0\\\\s0\\s2\\\\s2e-11\\\\s0\\\\s0"; + + + String models[] = {lm317, tl431}; + int i; + for (i = 0; i != models.length; i++) { + StringTokenizer st = new StringTokenizer(models[i], " "); + st.nextToken(); + CustomCompositeModel model = undumpModel(st); + model.builtin = true; + } } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/CustomLogicElm.java b/src/main/java/com/lushprojects/circuitjs1/client/CustomLogicElm.java index ef0d9c1..a164754 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/CustomLogicElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/CustomLogicElm.java @@ -12,250 +12,258 @@ public class CustomLogicElm extends ChipElm { boolean patternValues[]; boolean highImpedance[]; static String lastModelName = "default"; - + public CustomLogicElm(int xx, int yy) { - super(xx, yy); - modelName = lastModelName; - setupPins(); + super(xx, yy); + modelName = lastModelName; + setupPins(); } public CustomLogicElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f, st); - modelName = CustomLogicModel.unescape(st.nextToken()); - updateModels(); - int i; - for (i = 0; i != getPostCount(); i++) { - if (pins[i].output) { - volts[i] = new Double(st.nextToken()).doubleValue(); - pins[i].value = volts[i] > getThreshold(); - } - } + StringTokenizer st) { + super(xa, ya, xb, yb, f, st); + modelName = CustomLogicModel.unescape(st.nextToken()); + updateModels(); + int i; + for (i = 0; i != getPostCount(); i++) { + if (pins[i].output) { + volts[i] = new Double(st.nextToken()).doubleValue(); + pins[i].value = volts[i] > getThreshold(); + } + } } - + String dump() { - String s = super.dump(); - s += " " + CustomLogicModel.escape(modelName); + String s = super.dump(); + s += " " + CustomLogicModel.escape(modelName); - // the code to do this in ChipElm doesn't work here because we don't know - // how many pins to read until we read the model name! So we have to - // duplicate it here. + // the code to do this in ChipElm doesn't work here because we don't know + // how many pins to read until we read the model name! So we have to + // duplicate it here. int i; for (i = 0; i != getPostCount(); i++) { if (pins[i].output) s += " " + volts[i]; } - return s; + return s; } - + String dumpModel() { - if (model.dumped) - return ""; - return model.dump(); + if (model.dumped) + return ""; + return model.dump(); } - + public void updateModels() { - model = CustomLogicModel.getModelWithNameOrCopy(modelName, model); - setupPins(); - allocNodes(); - setPoints(); + model = CustomLogicModel.getModelWithNameOrCopy(modelName, model); + setupPins(); + allocNodes(); + setPoints(); } - + @Override void setupPins() { - if (modelName == null) { - postCount = bits; - allocNodes(); - return; - } - - model = CustomLogicModel.getModelWithName(modelName); - inputCount = model.inputs.length; - outputCount = model.outputs.length; - sizeY = inputCount > outputCount ? inputCount : outputCount; - if (sizeY == 0) - sizeY = 1; - sizeX = 2; - postCount = inputCount+outputCount; - pins = new Pin[postCount]; - int i; - for (i = 0; i != inputCount; i++) { - pins[i] = new Pin(i, SIDE_W, model.inputs[i]); - pins[i].fixName(); - } - for (i = 0; i != outputCount; i++) { - pins[i+inputCount] = new Pin(i, SIDE_E, model.outputs[i]); - pins[i+inputCount].output = true; - pins[i+inputCount].fixName(); - } - lastValues = new boolean[postCount]; - patternValues = new boolean[26]; - highImpedance = new boolean[postCount]; + if (modelName == null) { + postCount = bits; + allocNodes(); + return; + } + + model = CustomLogicModel.getModelWithName(modelName); + inputCount = model.inputs.length; + outputCount = model.outputs.length; + sizeY = inputCount > outputCount ? inputCount : outputCount; + if (sizeY == 0) + sizeY = 1; + sizeX = 2; + postCount = inputCount + outputCount; + pins = new Pin[postCount]; + int i; + for (i = 0; i != inputCount; i++) { + pins[i] = new Pin(i, SIDE_W, model.inputs[i]); + pins[i].fixName(); + } + for (i = 0; i != outputCount; i++) { + pins[i + inputCount] = new Pin(i, SIDE_E, model.outputs[i]); + pins[i + inputCount].output = true; + pins[i + inputCount].fixName(); + } + lastValues = new boolean[postCount]; + patternValues = new boolean[26]; + highImpedance = new boolean[postCount]; + } + + int getPostCount() { + return postCount; } - int getPostCount() { return postCount; } - @Override int getVoltageSourceCount() { - return outputCount; + return outputCount; } // keep track of whether we have any tri-state outputs. if not, then we can simplify things quite a bit, making the simulation faster - boolean hasTriState() { return model == null ? false : model.triState; } - - boolean nonLinear() { return hasTriState(); } - + boolean hasTriState() { + return model == null ? false : model.triState; + } + + boolean nonLinear() { + return hasTriState(); + } + int getInternalNodeCount() { - // for tri-state outputs, we need an internal node to connect a voltage source to, and then connect a resistor from there to the output. - // we do this for all outputs if any of them are tri-state - return (hasTriState()) ? outputCount : 0; + // for tri-state outputs, we need an internal node to connect a voltage source to, and then connect a resistor from there to the output. + // we do this for all outputs if any of them are tri-state + return (hasTriState()) ? outputCount : 0; } - + void stamp() { - int i; - int add = (hasTriState()) ? outputCount : 0; - for (i = 0; i != getPostCount(); i++) { - Pin p = pins[i]; - if (p.output) { - sim.stampVoltageSource(0, nodes[i+add], p.voltSource); - if (hasTriState()) { - sim.stampNonLinear(nodes[i+add]); - sim.stampNonLinear(nodes[i]); - } - } - } + int i; + int add = (hasTriState()) ? outputCount : 0; + for (i = 0; i != getPostCount(); i++) { + Pin p = pins[i]; + if (p.output) { + sim.stampVoltageSource(0, nodes[i + add], p.voltSource); + if (hasTriState()) { + sim.stampNonLinear(nodes[i + add]); + sim.stampNonLinear(nodes[i]); + } + } + } } - + void doStep() { - int i; - for (i = 0; i != getPostCount(); i++) { - Pin p = pins[i]; - if (!p.output) - p.value = volts[i] > getThreshold(); - } - execute(); - int add = (hasTriState()) ? outputCount : 0; - for (i = 0; i != getPostCount(); i++) { - Pin p = pins[i]; - if (p.output) { - // connect output voltage source (to internal node if tri-state, otherwise connect directly to output) - sim.updateVoltageSource(0, nodes[i+add], p.voltSource, p.value ? highVoltage : 0); - - // add resistor for tri-state if necessary - if (hasTriState()) - sim.stampResistor(nodes[i+add], nodes[i], highImpedance[i] ? 1e8 : 1e-3); - } - } + int i; + for (i = 0; i != getPostCount(); i++) { + Pin p = pins[i]; + if (!p.output) + p.value = volts[i] > getThreshold(); + } + execute(); + int add = (hasTriState()) ? outputCount : 0; + for (i = 0; i != getPostCount(); i++) { + Pin p = pins[i]; + if (p.output) { + // connect output voltage source (to internal node if tri-state, otherwise connect directly to output) + sim.updateVoltageSource(0, nodes[i + add], p.voltSource, p.value ? highVoltage : 0); + + // add resistor for tri-state if necessary + if (hasTriState()) + sim.stampResistor(nodes[i + add], nodes[i], highImpedance[i] ? 1e8 : 1e-3); + } + } } void execute() { - int i; - for (i = 0; i != model.rulesLeft.size(); i++) { - // check for a match - String rl = model.rulesLeft.get(i); - int j; - for (j = 0; j != rl.length(); j++) { - char x = rl.charAt(j); - if (x == '0' || x == '1') { - if (pins[j].value == (x == '1')) - continue; - break; - } - - // don't care - if (x == '?') - continue; - - // up transition - if (x == '+') { - if (pins[j].value && !lastValues[j]) - continue; - break; - } - - // down transition - if (x == '-') { - if (!pins[j].value && lastValues[j]) - continue; - break; - } - - // save pattern values - if (x >= 'a' && x <= 'z') { - patternValues[x-'a'] = pins[j].value; - continue; - } - - // compare pattern values - if (x >= 'A' && x <= 'z') { - if (patternValues[x-'A'] != pins[j].value) - break; - continue; - } - } - if (j != rl.length()) - continue; - - // success - String rr = model.rulesRight.get(i); - for (j = 0; j != rr.length(); j++) { - char x = rr.charAt(j); - highImpedance[j+inputCount] = false; - if (x >= 'a' && x <= 'z') - pins[j+inputCount].value = patternValues[x-'a']; - else if (x == '_') - highImpedance[j+inputCount] = true; - else - pins[j+inputCount].value = (x == '1'); - } - break; - } - - // save values for transition checking - int j; - for (j = 0; j != postCount; j++) - lastValues[j] = pins[j].value; + int i; + for (i = 0; i != model.rulesLeft.size(); i++) { + // check for a match + String rl = model.rulesLeft.get(i); + int j; + for (j = 0; j != rl.length(); j++) { + char x = rl.charAt(j); + if (x == '0' || x == '1') { + if (pins[j].value == (x == '1')) + continue; + break; + } + + // don't care + if (x == '?') + continue; + + // up transition + if (x == '+') { + if (pins[j].value && !lastValues[j]) + continue; + break; + } + + // down transition + if (x == '-') { + if (!pins[j].value && lastValues[j]) + continue; + break; + } + + // save pattern values + if (x >= 'a' && x <= 'z') { + patternValues[x - 'a'] = pins[j].value; + continue; + } + + // compare pattern values + if (x >= 'A' && x <= 'z') { + if (patternValues[x - 'A'] != pins[j].value) + break; + continue; + } + } + if (j != rl.length()) + continue; + + // success + String rr = model.rulesRight.get(i); + for (j = 0; j != rr.length(); j++) { + char x = rr.charAt(j); + highImpedance[j + inputCount] = false; + if (x >= 'a' && x <= 'z') + pins[j + inputCount].value = patternValues[x - 'a']; + else if (x == '_') + highImpedance[j + inputCount] = true; + else + pins[j + inputCount].value = (x == '1'); + } + break; + } + + // save values for transition checking + int j; + for (j = 0; j != postCount; j++) + lastValues[j] = pins[j].value; } - + public EditInfo getChipEditInfo(int n) { - if (n == 0) { - EditInfo ei = new EditInfo("Model Name", 0, -1, -1); - ei.text = modelName; - ei.disallowSliders(); - return ei; - } - if (n == 1) { + if (n == 0) { + EditInfo ei = new EditInfo("Model Name", 0, -1, -1); + ei.text = modelName; + ei.disallowSliders(); + return ei; + } + if (n == 1) { EditInfo ei = new EditInfo("", 0, -1, -1); ei.button = new Button(Locale.LS("Edit Model")); return ei; - } - return null; + } + return null; } - + public void setChipEditValue(int n, EditInfo ei) { - if (n == 0) { - String newModelName = ei.textf.getText(); - if (modelName.equals(newModelName)) - return; - modelName = lastModelName = ei.textf.getText(); - model = CustomLogicModel.getModelWithNameOrCopy(modelName, model); - setupPins(); - allocNodes(); - setPoints(); - return; - } - if (n == 1) { - EditDialog editDialog = new EditDialog(model, sim); - CirSim.customLogicEditDialog = editDialog; - editDialog.show(); - return; - } + if (n == 0) { + String newModelName = ei.textf.getText(); + if (modelName.equals(newModelName)) + return; + modelName = lastModelName = ei.textf.getText(); + model = CustomLogicModel.getModelWithNameOrCopy(modelName, model); + setupPins(); + allocNodes(); + setPoints(); + return; + } + if (n == 1) { + EditDialog editDialog = new EditDialog(model, sim); + CirSim.customLogicEditDialog = editDialog; + editDialog.show(); + return; + } + } + + int getDumpType() { + return 208; } - - int getDumpType() { return 208; } void getInfo(String arr[]) { - super.getInfo(arr); - arr[0] = model.infoText; + super.getInfo(arr); + arr[0] = model.infoText; } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/CustomLogicModel.java b/src/main/java/com/lushprojects/circuitjs1/client/CustomLogicModel.java index 775c5cf..5fe17e3 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/CustomLogicModel.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/CustomLogicModel.java @@ -12,7 +12,7 @@ public class CustomLogicModel implements Editable { static int FLAG_SCHMITT = 1; static HashMap modelMap; - + int flags; String name; String[] inputs; @@ -22,92 +22,92 @@ public class CustomLogicModel implements Editable { Vector rulesLeft, rulesRight; boolean dumped; boolean triState; - + static CustomLogicModel getModelWithName(String name) { - if (modelMap == null) - modelMap = new HashMap(); - CustomLogicModel lm = modelMap.get(name); - if (lm != null) - return lm; - lm = new CustomLogicModel(); - lm.name = name; - lm.infoText = (name.equals("default")) ? "custom logic" : name; - modelMap.put(name, lm); - return lm; + if (modelMap == null) + modelMap = new HashMap(); + CustomLogicModel lm = modelMap.get(name); + if (lm != null) + return lm; + lm = new CustomLogicModel(); + lm.name = name; + lm.infoText = (name.equals("default")) ? "custom logic" : name; + modelMap.put(name, lm); + return lm; } - + static CustomLogicModel getModelWithNameOrCopy(String name, CustomLogicModel oldmodel) { - if (modelMap == null) - modelMap = new HashMap(); - CustomLogicModel lm = modelMap.get(name); - if (lm != null) - return lm; - lm = new CustomLogicModel(oldmodel); - lm.name = name; - lm.infoText = name; - modelMap.put(name, lm); - return lm; + if (modelMap == null) + modelMap = new HashMap(); + CustomLogicModel lm = modelMap.get(name); + if (lm != null) + return lm; + lm = new CustomLogicModel(oldmodel); + lm.name = name; + lm.infoText = name; + modelMap.put(name, lm); + return lm; } - + static void clearDumpedFlags() { - if (modelMap == null) - return; - Iterator it = modelMap.entrySet().iterator(); - while (it.hasNext()) { - Map.Entry pair = (Map.Entry)it.next(); - pair.getValue().dumped = false; - } + if (modelMap == null) + return; + Iterator it = modelMap.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry pair = (Map.Entry) it.next(); + pair.getValue().dumped = false; + } } - + CustomLogicModel() { - inputs = listToArray("A,B"); - outputs = listToArray("C,D"); - rulesLeft = new Vector(); - rulesRight = new Vector(); - rules = ""; + inputs = listToArray("A,B"); + outputs = listToArray("C,D"); + rulesLeft = new Vector(); + rulesRight = new Vector(); + rules = ""; } - + CustomLogicModel(CustomLogicModel copy) { - flags = copy.flags; - inputs = copy.inputs; - outputs = copy.outputs; - infoText = copy.infoText; - rules = copy.rules; - rulesLeft = copy.rulesLeft; - rulesRight = copy.rulesRight; + flags = copy.flags; + inputs = copy.inputs; + outputs = copy.outputs; + infoText = copy.infoText; + rules = copy.rules; + rulesLeft = copy.rulesLeft; + rulesRight = copy.rulesRight; } - + static void undumpModel(StringTokenizer st) { - String name = unescape(st.nextToken()); - CustomLogicModel model = getModelWithName(name); - model.undump(st); + String name = unescape(st.nextToken()); + CustomLogicModel model = getModelWithName(name); + model.undump(st); } - + void undump(StringTokenizer st) { - flags = new Integer(st.nextToken()).intValue(); - inputs = listToArray(unescape(st.nextToken())); - outputs = listToArray(unescape(st.nextToken())); - infoText = unescape(st.nextToken()); - rules = unescape(st.nextToken()); - parseRules(); + flags = new Integer(st.nextToken()).intValue(); + inputs = listToArray(unescape(st.nextToken())); + outputs = listToArray(unescape(st.nextToken())); + infoText = unescape(st.nextToken()); + rules = unescape(st.nextToken()); + parseRules(); } - + String arrayToList(String arr[]) { - if (arr == null) - return ""; - if (arr.length == 0) - return ""; - String x = arr[0]; - int i; - for (i = 1; i < arr.length; i++) - x += "," + arr[i]; - return x; + if (arr == null) + return ""; + if (arr.length == 0) + return ""; + String x = arr[0]; + int i; + for (i = 1; i < arr.length; i++) + x += "," + arr[i]; + return x; } - - String [] listToArray(String arr) { - return arr.split(","); + + String[] listToArray(String arr) { + return arr.split(","); } - + public EditInfo getEditInfo(int n) { if (n == 0) { EditInfo ei = new EditInfo("Inputs", 0, -1, -1); @@ -139,126 +139,126 @@ public EditInfo getEditInfo(int n) { return ei; } */ - return null; + return null; } public void setEditValue(int n, EditInfo ei) { - if (n == 0) - inputs = listToArray(ei.textf.getText()); - if (n == 1) - outputs = listToArray(ei.textf.getText()); - if (n == 2) - infoText = ei.textf.getText(); - if (n == 3) { - rules = ei.textArea.getText(); - parseRules(); - } - if (n == 4) { - if (ei.checkbox.getState()) - flags |= FLAG_SCHMITT; - else - flags &= ~FLAG_SCHMITT; - } - CirSim.theSim.updateModels(); + if (n == 0) + inputs = listToArray(ei.textf.getText()); + if (n == 1) + outputs = listToArray(ei.textf.getText()); + if (n == 2) + infoText = ei.textf.getText(); + if (n == 3) { + rules = ei.textArea.getText(); + parseRules(); + } + if (n == 4) { + if (ei.checkbox.getState()) + flags |= FLAG_SCHMITT; + else + flags &= ~FLAG_SCHMITT; + } + CirSim.theSim.updateModels(); } void parseRules() { - String lines[] = rules.split("\n"); - int i; - rulesLeft = new Vector(); - rulesRight = new Vector(); - triState = false; - for (i = 0; i != lines.length; i++) { - String s = lines[i].toLowerCase(); - if (s.length() == 0 || s.startsWith("#")) - continue; - String s0[] = s.replaceAll(" ", "").split("="); - if (s0.length != 2) { - Window.alert("Error on line " + (i+1) + " of model description"); - return; - } - if (s0[0].length() < inputs.length) { - Window.alert("Model must have >= " + (inputs.length) + " digits on left side"); - return; - } - if (s0[0].length() > inputs.length + outputs.length) { - Window.alert("Model must have <= " + (inputs.length+outputs.length) + " digits on left side"); - return; - } - if (s0[1].length() != outputs.length) { - Window.alert("Model must have " + (outputs.length) + " digits on right side"); - return; - } - String rl = s0[0]; - boolean used[] = new boolean[26]; - int j; - String newRl = ""; - for (j = 0; j != rl.length(); j++) { - char x = rl.charAt(j); - if (x == '?' || x == '+' || x == '-' || x == '0' || x == '1') { - newRl += x; - continue; - } - if (x < 'a' || x > 'z') { - Window.alert("Error on line " + (i+1) + " of model description"); - return; - } - // if a letter appears twice, capitalize it the 2nd time so we can compare - if (used[x-'a']) { - newRl += (char)(x + 'A' - 'a'); - continue; - } - used[x-'a'] = true; - newRl += x; - } - String rr = s0[1]; - if (rr.contains("_")) - triState = true; - rulesLeft.add(newRl); - rulesRight.add(s0[1]); - } + String lines[] = rules.split("\n"); + int i; + rulesLeft = new Vector(); + rulesRight = new Vector(); + triState = false; + for (i = 0; i != lines.length; i++) { + String s = lines[i].toLowerCase(); + if (s.length() == 0 || s.startsWith("#")) + continue; + String s0[] = s.replaceAll(" ", "").split("="); + if (s0.length != 2) { + Window.alert("Error on line " + (i + 1) + " of model description"); + return; + } + if (s0[0].length() < inputs.length) { + Window.alert("Model must have >= " + (inputs.length) + " digits on left side"); + return; + } + if (s0[0].length() > inputs.length + outputs.length) { + Window.alert("Model must have <= " + (inputs.length + outputs.length) + " digits on left side"); + return; + } + if (s0[1].length() != outputs.length) { + Window.alert("Model must have " + (outputs.length) + " digits on right side"); + return; + } + String rl = s0[0]; + boolean used[] = new boolean[26]; + int j; + String newRl = ""; + for (j = 0; j != rl.length(); j++) { + char x = rl.charAt(j); + if (x == '?' || x == '+' || x == '-' || x == '0' || x == '1') { + newRl += x; + continue; + } + if (x < 'a' || x > 'z') { + Window.alert("Error on line " + (i + 1) + " of model description"); + return; + } + // if a letter appears twice, capitalize it the 2nd time so we can compare + if (used[x - 'a']) { + newRl += (char) (x + 'A' - 'a'); + continue; + } + used[x - 'a'] = true; + newRl += x; + } + String rr = s0[1]; + if (rr.contains("_")) + triState = true; + rulesLeft.add(newRl); + rulesRight.add(s0[1]); + } } - + String dump() { - dumped = true; - if (rules.length() > 0 && !rules.endsWith("\n")) - rules += "\n"; - return "! " + escape(name) + " " + flags + " " + escape(arrayToList(inputs)) + " " + - escape(arrayToList(outputs)) + " " + escape(infoText) + " " + escape(rules); + dumped = true; + if (rules.length() > 0 && !rules.endsWith("\n")) + rules += "\n"; + return "! " + escape(name) + " " + flags + " " + escape(arrayToList(inputs)) + " " + + escape(arrayToList(outputs)) + " " + escape(infoText) + " " + escape(rules); } - + static String escape(String s) { - if (s.length() == 0) - return "\\0"; - return s.replace("\\", "\\\\").replace("\n", "\\n").replace(" ", "\\s").replace("+", "\\p"). - replace("=", "\\q").replace("#", "\\h").replace("&", "\\a").replace("\r", "\\r"); + if (s.length() == 0) + return "\\0"; + return s.replace("\\", "\\\\").replace("\n", "\\n").replace(" ", "\\s").replace("+", "\\p"). + replace("=", "\\q").replace("#", "\\h").replace("&", "\\a").replace("\r", "\\r"); } - + static String unescape(String s) { - if (s.equals("\\0")) - return ""; - int i; - for (i = 0; i < s.length(); i++) { - if (s.charAt(i) == '\\') { - char c = s.charAt(i+1); - if (c == 'n') - s = s.substring(0, i) + "\n" + s.substring(i+2); - else if (c == 'r') - s = s.substring(0, i) + "\r" + s.substring(i+2); - else if (c == 's') - s = s.substring(0, i) + " " + s.substring(i+2); - else if (c == 'p') - s = s.substring(0, i) + "+" + s.substring(i+2); - else if (c == 'q') - s = s.substring(0, i) + "=" + s.substring(i+2); - else if (c == 'h') - s = s.substring(0, i) + "#" + s.substring(i+2); - else if (c == 'a') - s = s.substring(0, i) + "&" + s.substring(i+2); - else - s = s.substring(0, i) + s.substring(i+1); - } - } - return s; + if (s.equals("\\0")) + return ""; + int i; + for (i = 0; i < s.length(); i++) { + if (s.charAt(i) == '\\') { + char c = s.charAt(i + 1); + if (c == 'n') + s = s.substring(0, i) + "\n" + s.substring(i + 2); + else if (c == 'r') + s = s.substring(0, i) + "\r" + s.substring(i + 2); + else if (c == 's') + s = s.substring(0, i) + " " + s.substring(i + 2); + else if (c == 'p') + s = s.substring(0, i) + "+" + s.substring(i + 2); + else if (c == 'q') + s = s.substring(0, i) + "=" + s.substring(i + 2); + else if (c == 'h') + s = s.substring(0, i) + "#" + s.substring(i + 2); + else if (c == 'a') + s = s.substring(0, i) + "&" + s.substring(i + 2); + else + s = s.substring(0, i) + s.substring(i + 1); + } + } + return s; } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/CustomTransformerElm.java b/src/main/java/com/lushprojects/circuitjs1/client/CustomTransformerElm.java index 49f8ff6..5466451 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/CustomTransformerElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/CustomTransformerElm.java @@ -21,436 +21,456 @@ import com.google.gwt.user.client.Window; -class CustomTransformerElm extends CircuitElm { - double coilCurrents[], coilInductances[], coilCurCounts[], coilCurSourceValues[], coilPolarities[]; - double nodeCurrents[], nodeCurCounts[]; - - // node number n of first node of each coil (second node = n+1) - int coilNodes[]; - - int coilCount, nodeCount; - - // number of primary coils - int primaryCoils; - - Point nodePoints[], nodeTaps[], ptCore[]; - String description; - double inductance, couplingCoef; - boolean needDots; - - Point dots[]; - int width; - - public CustomTransformerElm(int xx, int yy) { - super(xx, yy); - inductance = 4; - width = 32; - noDiagonal = true; - couplingCoef = .999; - description = "1,1:1"; - parseDescription(description); - } - public CustomTransformerElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f); - width = 32; // max(32, abs(yb-ya)); - inductance = new Double(st.nextToken()).doubleValue(); - couplingCoef = new Double(st.nextToken()).doubleValue(); - String str = st.nextToken(); - description = CustomLogicModel.unescape(str); - coilCount = new Integer(st.nextToken()).intValue(); - int i; - coilCurrents = new double[coilCount]; - for (i = 0; i != coilCount; i++) - coilCurrents[i] = new Double(st.nextToken()).doubleValue(); - noDiagonal = true; - parseDescription(description); - } - void drag(int xx, int yy) { - xx = sim.snapGrid(xx); - yy = sim.snapGrid(yy); +public class CustomTransformerElm extends CircuitElm { + double coilCurrents[], coilInductances[], coilCurCounts[], coilCurSourceValues[], coilPolarities[]; + double nodeCurrents[], nodeCurCounts[]; + + // node number n of first node of each coil (second node = n+1) + int coilNodes[]; + + int coilCount, nodeCount; + + // number of primary coils + int primaryCoils; + + Point nodePoints[], nodeTaps[], ptCore[]; + String description; + double inductance, couplingCoef; + boolean needDots; + + Point dots[]; + int width; + + public CustomTransformerElm(int xx, int yy) { + super(xx, yy); + inductance = 4; + width = 32; + noDiagonal = true; + couplingCoef = .999; + description = "1,1:1"; + parseDescription(description); + } + + public CustomTransformerElm(int xa, int ya, int xb, int yb, int f, + StringTokenizer st) { + super(xa, ya, xb, yb, f); + width = 32; // max(32, abs(yb-ya)); + inductance = new Double(st.nextToken()).doubleValue(); + couplingCoef = new Double(st.nextToken()).doubleValue(); + String str = st.nextToken(); + description = CustomLogicModel.unescape(str); + coilCount = new Integer(st.nextToken()).intValue(); + int i; + coilCurrents = new double[coilCount]; + for (i = 0; i != coilCount; i++) + coilCurrents[i] = new Double(st.nextToken()).doubleValue(); + noDiagonal = true; + parseDescription(description); + } + + void drag(int xx, int yy) { + xx = sim.snapGrid(xx); + yy = sim.snapGrid(yy); // width = max(32, abs(yy-y)); - if (xx == x) - yy = y; - x2 = xx; y2 = yy; - setPoints(); - } - int getDumpType() { return 406; } - String dump() { - String s = super.dump() + " " + inductance + " " + couplingCoef + " " + CustomLogicModel.escape(description) + " " + coilCount + " "; - int i; - for (i = 0; i != coilCount; i++) { - s += coilCurrents[i] + " "; - } - return s; - } - - void parseDescription() { - parseDescription(description); - } - - boolean parseDescription(String desc) { - // a number indicates a coil (number = turns ratio to base inductance coil) - // (negative number = reverse polarity) - // : separates primary and secondary - // , separates two coils - // + separates two connected coils (tapped) - StringTokenizer st = new StringTokenizer(desc, ",:+", true); - - // count coils/nodes - coilCount = nodeCount = 0; - while (st.hasMoreTokens()) { - String s = st.nextToken(); - if (s == "+") - nodeCount--; - if (s == "," || s == "+" || s == ":") - continue; - nodeCount += 2; - coilCount++; - } - - coilNodes = new int[coilCount]; - coilInductances = new double[coilCount]; - // save coil currents if possible (needed for undumping) - if (coilCurrents == null || coilCurrents.length != coilCount) - coilCurrents = new double[coilCount]; - coilCurCounts = new double[coilCount]; - coilCurSourceValues = new double[coilCount]; - coilPolarities = new double[coilCount]; - nodePoints = newPointArray(nodeCount); - nodeTaps = newPointArray(nodeCount); - nodeCurrents = new double[nodeCount]; - nodeCurCounts = new double[nodeCount]; - - // start over - st = new StringTokenizer(desc, ",:+", true); - int nodeNum = 0; - int coilNum = 0; - primaryCoils = 0; - boolean secondary = false; - needDots = false; - while (true) { - String tok = st.nextToken(); - double n = 0; - try { - n = Double.parseDouble(tok); - } catch (Exception e) { return false; } - if (n == 0) - return false; - // create new coil - coilNodes[coilNum] = nodeNum; - coilInductances[coilNum] = n*n*inductance; - coilPolarities[coilNum] = 1; - if (n < 0) { - coilPolarities[coilNum] = -1; - needDots = true; - } - nodeNum += 2; - coilNum++; - if (!secondary) - primaryCoils = coilNum; - if (!st.hasMoreTokens()) - break; - tok = st.nextToken(); - if (tok == ",") - continue; - if (tok == "+") { - nodeNum--; - continue; - } - if (tok == ":") { - // switch to secondary - if (secondary) - return false; - secondary = true; - continue; - } - return false; - } - allocNodes(); - setPoints(); - xformMatrix = null; - return true; - } - - boolean isTrapezoidal() { return (flags & Inductor.FLAG_BACK_EULER) == 0; } - void draw(Graphics g) { - int i; - - // draw taps - for (i = 0; i != getPostCount(); i++) { - setVoltageColor(g, volts[i]); - drawThickLine(g, nodePoints[i], nodeTaps[i]); - } - - // draw coils - for (i = 0; i != coilCount; i++) { - int n = coilNodes[i]; - setVoltageColor(g, volts[n]); - setPowerColor(g, coilCurrents[i]*(volts[n]-volts[n+1])); - drawCoil(g, (i >= primaryCoils ? -6 : 6), nodeTaps[n], nodeTaps[n+1], volts[n], volts[n+1]); - if (dots != null) { - g.setColor(needsHighlight() ? selectColor : lightGrayColor); - g.fillOval(dots[i].x-2, dots[i].y-2, 5, 5); - } - } - g.setColor(needsHighlight() ? selectColor : lightGrayColor); - - // draw core - for (i = 0; i != 2; i++) { - drawThickLine(g, ptCore[i], ptCore[i+2]); - } - - // draw coil currents - for (i = 0; i != coilCount; i++) { - coilCurCounts[i] = updateDotCount(coilCurrents[i], coilCurCounts[i]); - int ni = coilNodes[i]; - drawDots(g, nodeTaps[ni], nodeTaps[ni+1], coilCurCounts[i]); - } - - // draw tap currents - for (i = 0; i != nodeCount; i++) { - nodeCurCounts[i] = updateDotCount(nodeCurrents[i], nodeCurCounts[i]); - drawDots(g, nodePoints[i], nodeTaps[i], nodeCurCounts[i]); - } - - drawPosts(g); - setBbox(nodePoints[0], nodePoints[nodeCount-1], 0); - adjustBbox(ptCore[0], ptCore[3]); - } - - void setPoints() { - super.setPoints(); - point2.y = point1.y; - int i; - int primaryNodes = (primaryCoils == coilCount) ? nodeCount : coilNodes[primaryCoils]; - dn = Math.abs(point1.x-point2.x); - double ce = .5-12/dn; - double cd = .5-2/dn; - double maxWidth = 0; - int step; - for (step = 0; step != 2; step++) { - int c = 0; - double offset = 0; - for (i = 0; i != nodeCount; i++) { - if (i == primaryNodes) - offset = 0; - if (step == 1) { - if (i == primaryNodes-1 || i == nodeCount-1) - offset = maxWidth; - interpPoint(point1, point2, nodePoints[i], i < primaryNodes ? 0 : 1, -offset); - interpPoint(point1, point2, nodeTaps[i] , i < primaryNodes ? ce : 1-ce, -offset); - } - maxWidth = Math.max(maxWidth, offset); - int nn = c < coilCount ? coilNodes[c] : -1; - if (nn == i) { - // this is first node of a coil, make room - c++; - offset += width; - } else { - // this is last node of a coil, make small gap - offset += 16; - } - } - } - ptCore = newPointArray(4); - for (i = 0; i != 4; i += 2) { - double h = (i == 2) ? -maxWidth : 0; - interpPoint(point1, point2, ptCore[i], cd, h); - interpPoint(point1, point2, ptCore[i+1], 1-cd, h); - } - - if (needDots) { - dots = new Point[coilCount]; - double dotp = Math.abs(7./width); - for (i = 0; i != coilCount; i++) { - int n = coilNodes[i]; - dots[i] = interpPoint(nodeTaps[n], nodeTaps[n+1], coilPolarities[i] > 0 ? dotp : 1-dotp, i < primaryCoils ? -7 : 7); - } - } else - dots = null; - } - Point getPost(int n) { - return nodePoints[n]; - } - int getPostCount() { return nodeCount; } - void reset() { - int i; - for (i = 0; i != coilCount; i++) - coilCurrents[i] = coilCurSourceValues[i] = coilCurCounts[i] = 0; - for (i = 0; i != nodeCount; i++) - volts[i] = nodeCurrents[i] = nodeCurCounts[i] = 0; - } - double xformMatrix[][]; - - void stamp() { - // equations for transformer: - // v1 = L1 di1/dt + M12 di2/dt + M13 di3/dt + ... - // v2 = M21 di1/dt + L2 di2/dt + M23 di3/dt + ... - // v3 = ... (one row for each coil) - // we invert that to get: - // di1/dt = a1 v1 + a2 v2 + ... - // di2/dt = a3 v1 + a4 v2 + ... - // integrate di1/dt using trapezoidal approx and we get: - // i1(t2) = i1(t1) + dt/2 (i1(t1) + i1(t2)) - // = i1(t1) + a1 dt/2 v1(t1) + a2 dt/2 v2(t1) + ... + - // a1 dt/2 v1(t2) + a2 dt/2 v2(t2) + ... - // the norton equivalent of this for i1 is: - // a. current source, I = i1(t1) + a1 dt/2 v1(t1) + a2 dt/2 v2(t1) + ... - // b. resistor, G = a1 dt/2 - // c. current source controlled by voltage v2, G = a2 dt/2 - // and for i2: - // a. current source, I = i2(t1) + a3 dt/2 v1(t1) + a4 dt/2 v2(t1) + ... - // b. resistor, G = a3 dt/2 - // c. current source controlled by voltage v2, G = a4 dt/2 - // - // For backward euler, the current source value is just i1(t1) and we use - // dt instead of dt/2 for the resistor and VCCS. - xformMatrix = new double[coilCount][coilCount]; - int i; - // fill diagonal - for (i = 0; i != coilCount; i++) - xformMatrix[i][i] = coilInductances[i]; - int j; - // fill off-diagonal - for (i = 0; i != coilCount; i++) - for (j = 0; j != i; j++) - xformMatrix[i][j] = xformMatrix[j][i] = couplingCoef*Math.sqrt(coilInductances[i]*coilInductances[j])*coilPolarities[i]*coilPolarities[j]; - - CirSim.invertMatrix(xformMatrix, coilCount); - - double ts = isTrapezoidal() ? sim.timeStep/2 : sim.timeStep; - for (i = 0; i != coilCount; i++) - for (j = 0; j != coilCount; j++) { - // multiply in dt/2 (or dt for backward euler) - xformMatrix[i][j] *= ts; - int ni = coilNodes[i]; - int nj = coilNodes[j]; - if (i == j) - sim.stampConductance(nodes[ni], nodes[ni+1], xformMatrix[i][i]); - else - sim.stampVCCurrentSource(nodes[ni], nodes[ni+1], nodes[nj], nodes[nj+1], xformMatrix[i][j]); - } - for (i = 0; i != nodeCount; i++) - sim.stampRightSide(nodes[i]); - } - - void startIteration() { - int i; - for (i = 0; i != coilCount; i++) { - double val = coilCurrents[i]; - if (isTrapezoidal()) { - int j; - for (j = 0; j != coilCount; j++) { - int n = coilNodes[j]; - double voltdiff = volts[n]-volts[n+1]; - val += voltdiff*xformMatrix[i][j]; - } - } - coilCurSourceValues[i] = val; - } - } - - void doStep() { - int i; - for (i = 0; i != coilCount; i++) { - int n = coilNodes[i]; - sim.stampCurrentSource(nodes[n], nodes[n+1], coilCurSourceValues[i]); - } - } - - void calculateCurrent() { - int i; - for (i = 0; i != nodeCount; i++) - nodeCurrents[i] = 0; - for (i = 0; i != coilCount; i++) { - double val = coilCurSourceValues[i]; - if (xformMatrix != null) { - int j; - for (j = 0; j != coilCount; j++) { - int n = coilNodes[j]; - double voltdiff = volts[n]-volts[n+1]; - val += voltdiff*xformMatrix[i][j]; - } - } - coilCurrents[i] = val; - int ni = coilNodes[i]; - nodeCurrents[ni] += val; - nodeCurrents[ni+1] -= val; - } - } - - @Override double getCurrentIntoNode(int n) { - return -nodeCurrents[n]; - } - - void getInfo(String arr[]) { - arr[0] = "transformer (custom)"; - arr[1] = "L = " + getUnitText(inductance, "H"); - int i; - for (i = 0; i != coilCount ; i++) { - if (2+i*2 >= arr.length) - break; - int ni = coilNodes[i]; - arr[2+i*2] = "Vd" + (i+1) + " = " + getVoltageText(volts[ni]-volts[ni+1]); - arr[3+i*2] = "I" + (i+1) + " = " + getCurrentText(coilCurrents[i]); - } - } - - boolean getConnection(int n1, int n2) { - int i; - for (i = 0; i != coilCount; i++) - if (comparePair(n1, n2, coilNodes[i], coilNodes[i]+1)) - return true; - return false; - } - - public EditInfo getEditInfo(int n) { - if (n == 0) - return new EditInfo("Base Inductance (H)", inductance, .01, 5); - if (n == 1) { - EditInfo ei = new EditInfo(EditInfo.makeLink("customtransformer.html", "Description"), 0, -1, -1); - ei.text = description; - ei.disallowSliders(); - return ei; - } - if (n == 2) - return new EditInfo("Coupling Coefficient", couplingCoef, 0, 1). - setDimensionless(); - if (n == 3) { - 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) { - inductance = ei.value; - parseDescription(); - } - if (n == 1) { - String s = ei.textf.getText(); - if (s != description) { - if (!parseDescription(s)) { - parseDescription(description); - Window.alert("Parse error in description"); - } else - description = s; - setPoints(); - } - } - if (n == 2 && ei.value > 0 && ei.value < 1) { - couplingCoef = ei.value; - parseDescription(); - } - if (n == 3) { - if (ei.checkbox.getState()) - flags &= ~Inductor.FLAG_BACK_EULER; - else - flags |= Inductor.FLAG_BACK_EULER; - parseDescription(); - } - } + if (xx == x) + yy = y; + x2 = xx; + y2 = yy; + setPoints(); + } + + int getDumpType() { + return 406; + } + + String dump() { + String s = super.dump() + " " + inductance + " " + couplingCoef + " " + CustomLogicModel.escape(description) + " " + coilCount + " "; + int i; + for (i = 0; i != coilCount; i++) { + s += coilCurrents[i] + " "; + } + return s; + } + + void parseDescription() { + parseDescription(description); + } + + boolean parseDescription(String desc) { + // a number indicates a coil (number = turns ratio to base inductance coil) + // (negative number = reverse polarity) + // : separates primary and secondary + // , separates two coils + // + separates two connected coils (tapped) + StringTokenizer st = new StringTokenizer(desc, ",:+", true); + + // count coils/nodes + coilCount = nodeCount = 0; + while (st.hasMoreTokens()) { + String s = st.nextToken(); + if (s == "+") + nodeCount--; + if (s == "," || s == "+" || s == ":") + continue; + nodeCount += 2; + coilCount++; + } + + coilNodes = new int[coilCount]; + coilInductances = new double[coilCount]; + // save coil currents if possible (needed for undumping) + if (coilCurrents == null || coilCurrents.length != coilCount) + coilCurrents = new double[coilCount]; + coilCurCounts = new double[coilCount]; + coilCurSourceValues = new double[coilCount]; + coilPolarities = new double[coilCount]; + nodePoints = newPointArray(nodeCount); + nodeTaps = newPointArray(nodeCount); + nodeCurrents = new double[nodeCount]; + nodeCurCounts = new double[nodeCount]; + + // start over + st = new StringTokenizer(desc, ",:+", true); + int nodeNum = 0; + int coilNum = 0; + primaryCoils = 0; + boolean secondary = false; + needDots = false; + while (true) { + String tok = st.nextToken(); + double n = 0; + try { + n = Double.parseDouble(tok); + } catch (Exception e) { + return false; + } + if (n == 0) + return false; + // create new coil + coilNodes[coilNum] = nodeNum; + coilInductances[coilNum] = n * n * inductance; + coilPolarities[coilNum] = 1; + if (n < 0) { + coilPolarities[coilNum] = -1; + needDots = true; + } + nodeNum += 2; + coilNum++; + if (!secondary) + primaryCoils = coilNum; + if (!st.hasMoreTokens()) + break; + tok = st.nextToken(); + if (tok == ",") + continue; + if (tok == "+") { + nodeNum--; + continue; + } + if (tok == ":") { + // switch to secondary + if (secondary) + return false; + secondary = true; + continue; + } + return false; + } + allocNodes(); + setPoints(); + xformMatrix = null; + return true; + } + + boolean isTrapezoidal() { + return (flags & Inductor.FLAG_BACK_EULER) == 0; + } + + void draw(Graphics g) { + int i; + + // draw taps + for (i = 0; i != getPostCount(); i++) { + setVoltageColor(g, volts[i]); + drawThickLine(g, nodePoints[i], nodeTaps[i]); + } + + // draw coils + for (i = 0; i != coilCount; i++) { + int n = coilNodes[i]; + setVoltageColor(g, volts[n]); + setPowerColor(g, coilCurrents[i] * (volts[n] - volts[n + 1])); + drawCoil(g, (i >= primaryCoils ? -6 : 6), nodeTaps[n], nodeTaps[n + 1], volts[n], volts[n + 1]); + if (dots != null) { + g.setColor(needsHighlight() ? selectColor : lightGrayColor); + g.fillOval(dots[i].x - 2, dots[i].y - 2, 5, 5); + } + } + g.setColor(needsHighlight() ? selectColor : lightGrayColor); + + // draw core + for (i = 0; i != 2; i++) { + drawThickLine(g, ptCore[i], ptCore[i + 2]); + } + + // draw coil currents + for (i = 0; i != coilCount; i++) { + coilCurCounts[i] = updateDotCount(coilCurrents[i], coilCurCounts[i]); + int ni = coilNodes[i]; + drawDots(g, nodeTaps[ni], nodeTaps[ni + 1], coilCurCounts[i]); + } + + // draw tap currents + for (i = 0; i != nodeCount; i++) { + nodeCurCounts[i] = updateDotCount(nodeCurrents[i], nodeCurCounts[i]); + drawDots(g, nodePoints[i], nodeTaps[i], nodeCurCounts[i]); + } + + drawPosts(g); + setBbox(nodePoints[0], nodePoints[nodeCount - 1], 0); + adjustBbox(ptCore[0], ptCore[3]); + } + + void setPoints() { + super.setPoints(); + point2.y = point1.y; + int i; + int primaryNodes = (primaryCoils == coilCount) ? nodeCount : coilNodes[primaryCoils]; + dn = Math.abs(point1.x - point2.x); + double ce = .5 - 12 / dn; + double cd = .5 - 2 / dn; + double maxWidth = 0; + int step; + for (step = 0; step != 2; step++) { + int c = 0; + double offset = 0; + for (i = 0; i != nodeCount; i++) { + if (i == primaryNodes) + offset = 0; + if (step == 1) { + if (i == primaryNodes - 1 || i == nodeCount - 1) + offset = maxWidth; + interpPoint(point1, point2, nodePoints[i], i < primaryNodes ? 0 : 1, -offset); + interpPoint(point1, point2, nodeTaps[i], i < primaryNodes ? ce : 1 - ce, -offset); + } + maxWidth = Math.max(maxWidth, offset); + int nn = c < coilCount ? coilNodes[c] : -1; + if (nn == i) { + // this is first node of a coil, make room + c++; + offset += width; + } else { + // this is last node of a coil, make small gap + offset += 16; + } + } + } + ptCore = newPointArray(4); + for (i = 0; i != 4; i += 2) { + double h = (i == 2) ? -maxWidth : 0; + interpPoint(point1, point2, ptCore[i], cd, h); + interpPoint(point1, point2, ptCore[i + 1], 1 - cd, h); + } + + if (needDots) { + dots = new Point[coilCount]; + double dotp = Math.abs(7. / width); + for (i = 0; i != coilCount; i++) { + int n = coilNodes[i]; + dots[i] = interpPoint(nodeTaps[n], nodeTaps[n + 1], coilPolarities[i] > 0 ? dotp : 1 - dotp, i < primaryCoils ? -7 : 7); + } + } else + dots = null; + } + + Point getPost(int n) { + return nodePoints[n]; + } + + int getPostCount() { + return nodeCount; + } + + void reset() { + int i; + for (i = 0; i != coilCount; i++) + coilCurrents[i] = coilCurSourceValues[i] = coilCurCounts[i] = 0; + for (i = 0; i != nodeCount; i++) + volts[i] = nodeCurrents[i] = nodeCurCounts[i] = 0; + } + + double xformMatrix[][]; + + void stamp() { + // equations for transformer: + // v1 = L1 di1/dt + M12 di2/dt + M13 di3/dt + ... + // v2 = M21 di1/dt + L2 di2/dt + M23 di3/dt + ... + // v3 = ... (one row for each coil) + // we invert that to get: + // di1/dt = a1 v1 + a2 v2 + ... + // di2/dt = a3 v1 + a4 v2 + ... + // integrate di1/dt using trapezoidal approx and we get: + // i1(t2) = i1(t1) + dt/2 (i1(t1) + i1(t2)) + // = i1(t1) + a1 dt/2 v1(t1) + a2 dt/2 v2(t1) + ... + + // a1 dt/2 v1(t2) + a2 dt/2 v2(t2) + ... + // the norton equivalent of this for i1 is: + // a. current source, I = i1(t1) + a1 dt/2 v1(t1) + a2 dt/2 v2(t1) + ... + // b. resistor, G = a1 dt/2 + // c. current source controlled by voltage v2, G = a2 dt/2 + // and for i2: + // a. current source, I = i2(t1) + a3 dt/2 v1(t1) + a4 dt/2 v2(t1) + ... + // b. resistor, G = a3 dt/2 + // c. current source controlled by voltage v2, G = a4 dt/2 + // + // For backward euler, the current source value is just i1(t1) and we use + // dt instead of dt/2 for the resistor and VCCS. + xformMatrix = new double[coilCount][coilCount]; + int i; + // fill diagonal + for (i = 0; i != coilCount; i++) + xformMatrix[i][i] = coilInductances[i]; + int j; + // fill off-diagonal + for (i = 0; i != coilCount; i++) + for (j = 0; j != i; j++) + xformMatrix[i][j] = xformMatrix[j][i] = couplingCoef * Math.sqrt(coilInductances[i] * coilInductances[j]) * coilPolarities[i] * coilPolarities[j]; + + CirSim.invertMatrix(xformMatrix, coilCount); + + double ts = isTrapezoidal() ? sim.timeStep / 2 : sim.timeStep; + for (i = 0; i != coilCount; i++) + for (j = 0; j != coilCount; j++) { + // multiply in dt/2 (or dt for backward euler) + xformMatrix[i][j] *= ts; + int ni = coilNodes[i]; + int nj = coilNodes[j]; + if (i == j) + sim.stampConductance(nodes[ni], nodes[ni + 1], xformMatrix[i][i]); + else + sim.stampVCCurrentSource(nodes[ni], nodes[ni + 1], nodes[nj], nodes[nj + 1], xformMatrix[i][j]); + } + for (i = 0; i != nodeCount; i++) + sim.stampRightSide(nodes[i]); + } + + void startIteration() { + int i; + for (i = 0; i != coilCount; i++) { + double val = coilCurrents[i]; + if (isTrapezoidal()) { + int j; + for (j = 0; j != coilCount; j++) { + int n = coilNodes[j]; + double voltdiff = volts[n] - volts[n + 1]; + val += voltdiff * xformMatrix[i][j]; + } + } + coilCurSourceValues[i] = val; + } + } + + void doStep() { + int i; + for (i = 0; i != coilCount; i++) { + int n = coilNodes[i]; + sim.stampCurrentSource(nodes[n], nodes[n + 1], coilCurSourceValues[i]); + } + } + + void calculateCurrent() { + int i; + for (i = 0; i != nodeCount; i++) + nodeCurrents[i] = 0; + for (i = 0; i != coilCount; i++) { + double val = coilCurSourceValues[i]; + if (xformMatrix != null) { + int j; + for (j = 0; j != coilCount; j++) { + int n = coilNodes[j]; + double voltdiff = volts[n] - volts[n + 1]; + val += voltdiff * xformMatrix[i][j]; + } + } + coilCurrents[i] = val; + int ni = coilNodes[i]; + nodeCurrents[ni] += val; + nodeCurrents[ni + 1] -= val; + } + } + + @Override + double getCurrentIntoNode(int n) { + return -nodeCurrents[n]; + } + + void getInfo(String arr[]) { + arr[0] = "transformer (custom)"; + arr[1] = "L = " + getUnitText(inductance, "H"); + int i; + for (i = 0; i != coilCount; i++) { + if (2 + i * 2 >= arr.length) + break; + int ni = coilNodes[i]; + arr[2 + i * 2] = "Vd" + (i + 1) + " = " + getVoltageText(volts[ni] - volts[ni + 1]); + arr[3 + i * 2] = "I" + (i + 1) + " = " + getCurrentText(coilCurrents[i]); + } + } + + boolean getConnection(int n1, int n2) { + int i; + for (i = 0; i != coilCount; i++) + if (comparePair(n1, n2, coilNodes[i], coilNodes[i] + 1)) + return true; + return false; + } + + public EditInfo getEditInfo(int n) { + if (n == 0) + return new EditInfo("Base Inductance (H)", inductance, .01, 5); + if (n == 1) { + EditInfo ei = new EditInfo(EditInfo.makeLink("customtransformer.html", "Description"), 0, -1, -1); + ei.text = description; + ei.disallowSliders(); + return ei; + } + if (n == 2) + return new EditInfo("Coupling Coefficient", couplingCoef, 0, 1). + setDimensionless(); + if (n == 3) { + 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) { + inductance = ei.value; + parseDescription(); + } + if (n == 1) { + String s = ei.textf.getText(); + if (s != description) { + if (!parseDescription(s)) { + parseDescription(description); + Window.alert("Parse error in description"); + } else + description = s; + setPoints(); + } + } + if (n == 2 && ei.value > 0 && ei.value < 1) { + couplingCoef = ei.value; + parseDescription(); + } + if (n == 3) { + if (ei.checkbox.getState()) + flags &= ~Inductor.FLAG_BACK_EULER; + else + flags |= Inductor.FLAG_BACK_EULER; + parseDescription(); + } } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/DACElm.java b/src/main/java/com/lushprojects/circuitjs1/client/DACElm.java index 89fed9b..22e924f 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/DACElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/DACElm.java @@ -19,51 +19,75 @@ package com.lushprojects.circuitjs1.client; -class DACElm extends ChipElm { - public DACElm(int xx, int yy) { super(xx, yy); } +public class DACElm extends ChipElm { + public DACElm(int xx, int yy) { + super(xx, yy); + } + public DACElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f, st); + StringTokenizer st) { + super(xa, ya, xb, yb, f, st); + } + + String getChipName() { + return "DAC"; } - String getChipName() { return "DAC"; } - boolean needsBits() { return true; } + + 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_W, "D" + i); - pins[bits] = new Pin(0, SIDE_E, "O"); - pins[bits].output = true; - pins[bits+1] = new Pin(sizeY-1, SIDE_E, "V+"); - allocNodes(); + 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_W, "D" + i); + pins[bits] = new Pin(0, SIDE_E, "O"); + pins[bits].output = true; + pins[bits + 1] = new Pin(sizeY - 1, SIDE_E, "V+"); + allocNodes(); } + void doStep() { - int ival = 0; - int i; - for (i = 0; i != bits; i++) - if (volts[i] > getThreshold()) - ival |= 1< getThreshold()) + ival |= 1 << i; + int ivalmax = (1 << bits) - 1; + double v = ival * volts[bits + 1] / ivalmax; + sim.updateVoltageSource(0, nodes[bits], pins[bits].voltSource, v); + } + + int getVoltageSourceCount() { + return 1; + } + + int getPostCount() { + return bits + 2; + } + + int getDumpType() { + return 166; } - int getVoltageSourceCount() { return 1; } - int getPostCount() { return bits+2; } - int getDumpType() { return 166; } // there's already a V+ pin, how does that relate to high logic voltage? figure out later - @Override boolean isDigitalChip() { return false; } + @Override + boolean isDigitalChip() { + return false; + } public EditInfo getChipEditInfo(int n) { if (n == 0) return new EditInfo("# of Bits", bits, 1, 1).setDimensionless(); return null; } + public void setChipEditValue(int n, EditInfo ei) { if (n == 0 && ei.value >= 2) { - bits = (int)ei.value; + bits = (int) ei.value; setupPins(); setPoints(); } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/DCMotorElm.java b/src/main/java/com/lushprojects/circuitjs1/client/DCMotorElm.java index cc0c252..897f4af 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/DCMotorElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/DCMotorElm.java @@ -5,7 +5,7 @@ // based on https://ctms.engin.umich.edu/CTMS/index.php?example=MotorPosition§ion=SystemModeling -class DCMotorElm extends CircuitElm { +public class DCMotorElm extends CircuitElm { Inductor ind, indInertia; // Electrical parameters @@ -16,94 +16,126 @@ class DCMotorElm extends CircuitElm { public double speed; - double coilCurrent; double inertiaCurrent; int[] voltSources = new int[2]; - public DCMotorElm(int xx, int yy) { - super(xx, yy); - ind = new Inductor(sim); - indInertia = new Inductor(sim); - inductance = .5; resistance = 1; angle = pi/2; speed = 0; K = 0.15; b= 0.05; J = 0.02; Kb = 0.15; gearRatio=1; tau=0; - ind.setup(inductance, 0, Inductor.FLAG_BACK_EULER); - indInertia.setup(J, 0, Inductor.FLAG_BACK_EULER); + + public DCMotorElm(int xx, int yy) { + super(xx, yy); + ind = new Inductor(sim); + indInertia = new Inductor(sim); + inductance = .5; + resistance = 1; + angle = pi / 2; + speed = 0; + K = 0.15; + b = 0.05; + J = 0.02; + Kb = 0.15; + gearRatio = 1; + tau = 0; + ind.setup(inductance, 0, Inductor.FLAG_BACK_EULER); + indInertia.setup(J, 0, Inductor.FLAG_BACK_EULER); } + public DCMotorElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f); - angle = pi/2;speed = 0; - //read: - // inductance; resistance, K, Kb, J, b, gearRatio, tau - inductance = new Double(st.nextToken()).doubleValue(); - resistance = new Double(st.nextToken()).doubleValue(); - K = new Double(st.nextToken()).doubleValue(); - Kb = new Double(st.nextToken()).doubleValue(); - J = new Double(st.nextToken()).doubleValue(); - b = new Double(st.nextToken()).doubleValue(); - gearRatio = new Double(st.nextToken()).doubleValue(); - tau = new Double(st.nextToken()).doubleValue(); - - ind = new Inductor(sim); - indInertia = new Inductor(sim); - ind.setup(inductance, 0, Inductor.FLAG_BACK_EULER); - indInertia.setup(J, 0, Inductor.FLAG_BACK_EULER); - } - int getDumpType() { return 415; } + StringTokenizer st) { + super(xa, ya, xb, yb, f); + angle = pi / 2; + speed = 0; + //read: + // inductance; resistance, K, Kb, J, b, gearRatio, tau + inductance = new Double(st.nextToken()).doubleValue(); + resistance = new Double(st.nextToken()).doubleValue(); + K = new Double(st.nextToken()).doubleValue(); + Kb = new Double(st.nextToken()).doubleValue(); + J = new Double(st.nextToken()).doubleValue(); + b = new Double(st.nextToken()).doubleValue(); + gearRatio = new Double(st.nextToken()).doubleValue(); + tau = new Double(st.nextToken()).doubleValue(); + + ind = new Inductor(sim); + indInertia = new Inductor(sim); + ind.setup(inductance, 0, Inductor.FLAG_BACK_EULER); + indInertia.setup(J, 0, Inductor.FLAG_BACK_EULER); + } + + int getDumpType() { + return 415; + } + String dump() { - // dump: inductance; resistance, K, Kb, J, b, gearRatio, tau - return super.dump() + " " + inductance + " " + resistance + " " + K + " " + Kb + " " + J + " " + b + " " + gearRatio + " " + tau; + // dump: inductance; resistance, K, Kb, J, b, gearRatio, tau + return super.dump() + " " + inductance + " " + resistance + " " + K + " " + Kb + " " + J + " " + b + " " + gearRatio + " " + tau; + } + + public double getAngle() { + return (angle); } - public double getAngle(){ return(angle);} Point motorCenter; void setPoints() { - super.setPoints(); - calcLeads(36); - motorCenter = interpPoint(point1, point2, .5); - allocNodes(); - } - int getPostCount() { return 2; } - int getInternalNodeCount() { return 4; } - int getVoltageSourceCount() { return 2; } - void setVoltageSource(int n, int v) { voltSources[n] = v; } + super.setPoints(); + calcLeads(36); + motorCenter = interpPoint(point1, point2, .5); + allocNodes(); + } + + int getPostCount() { + return 2; + } + + int getInternalNodeCount() { + return 4; + } + + int getVoltageSourceCount() { + return 2; + } + + void setVoltageSource(int n, int v) { + voltSources[n] = v; + } + void reset() { - super.reset(); - ind.reset(); - indInertia.reset(); - coilCurrent = 0; - inertiaCurrent = 0; + super.reset(); + ind.reset(); + indInertia.reset(); + coilCurrent = 0; + inertiaCurrent = 0; } void stamp() { - // stamp a bunch of internal parts to help us simulate the motor. It would be better to simulate this mini-circuit in code to reduce - // the size of the matrix. - - //nodes[0] nodes [1] are the external nodes - //Electrical part: - // inductor from motor nodes[0] to internal nodes[2] - ind.stamp(nodes[0], nodes[2]); - // resistor from internal nodes[2] to internal nodes[3] // motor post 2 - sim.stampResistor(nodes[2], nodes[3], resistance); - // Back emf voltage source from internal nodes[3] to external nodes [1] - sim.stampVoltageSource(nodes[3],nodes[1], voltSources[0]); // - - //Mechanical part: - // inertia inductor from internal nodes[4] to internal nodes[5] - indInertia.stamp(nodes[4], nodes[5]); - // resistor from internal nodes[5] to ground - sim.stampResistor(nodes[5], 0, b); - // Voltage Source from internal nodes[4] to ground - //System.out.println("doing stamp voltage"); - sim.stampVoltageSource(nodes[4], 0, voltSources[1]); - //System.out.println("doing stamp voltage "+voltSource); + // stamp a bunch of internal parts to help us simulate the motor. It would be better to simulate this mini-circuit in code to reduce + // the size of the matrix. + + //nodes[0] nodes [1] are the external nodes + //Electrical part: + // inductor from motor nodes[0] to internal nodes[2] + ind.stamp(nodes[0], nodes[2]); + // resistor from internal nodes[2] to internal nodes[3] // motor post 2 + sim.stampResistor(nodes[2], nodes[3], resistance); + // Back emf voltage source from internal nodes[3] to external nodes [1] + sim.stampVoltageSource(nodes[3], nodes[1], voltSources[0]); // + + //Mechanical part: + // inertia inductor from internal nodes[4] to internal nodes[5] + indInertia.stamp(nodes[4], nodes[5]); + // resistor from internal nodes[5] to ground + sim.stampResistor(nodes[5], 0, b); + // Voltage Source from internal nodes[4] to ground + //System.out.println("doing stamp voltage"); + sim.stampVoltageSource(nodes[4], 0, voltSources[1]); + //System.out.println("doing stamp voltage "+voltSource); } + void startIteration() { - ind.startIteration(volts[0]-volts[2]); - indInertia.startIteration(volts[4]-volts[5]); - // update angle: - angle= angle + speed*sim.timeStep; + ind.startIteration(volts[0] - volts[2]); + indInertia.startIteration(volts[4] - volts[5]); + // update angle: + angle = angle + speed * sim.timeStep; } /* boolean hasGroundConnection(int n1) { @@ -119,120 +151,124 @@ boolean getConnection(int n1, int n2) { */ void doStep() { - sim.updateVoltageSource(nodes[4],0, voltSources[1], - coilCurrent*K); - sim.updateVoltageSource(nodes[3],nodes[1], voltSources[0], - inertiaCurrent*Kb); - ind.doStep(volts[0]-volts[2]); - indInertia.doStep(volts[4]-volts[5]); + sim.updateVoltageSource(nodes[4], 0, voltSources[1], + coilCurrent * K); + sim.updateVoltageSource(nodes[3], nodes[1], voltSources[0], + inertiaCurrent * Kb); + ind.doStep(volts[0] - volts[2]); + indInertia.doStep(volts[4] - volts[5]); } + void calculateCurrent() { - coilCurrent = ind.calculateCurrent(volts[0]-volts[2]); - inertiaCurrent = indInertia.calculateCurrent(volts[4]-volts[5]); + coilCurrent = ind.calculateCurrent(volts[0] - volts[2]); + inertiaCurrent = indInertia.calculateCurrent(volts[4] - volts[5]); // current = (volts[2]-volts[3])/resistance; - speed=inertiaCurrent; + speed = inertiaCurrent; } // public double getCurrent() { current = (volts[2]-volts[3])/resistance; return current; } void setCurrent(int vn, double c) { - if (vn == voltSources[0]) - current = c; + if (vn == voltSources[0]) + current = c; } - + void draw(Graphics g) { - int cr = 18; - int hs = 8; - setBbox(point1, point2, cr); - draw2Leads(g); - //getCurrent(); - doDots(g); - setPowerColor(g, true); - Color cc = new Color((int) (165), (int) (165), (int) (165)); - g.setColor(cc); - g.fillOval(motorCenter.x-(cr), motorCenter.y-(cr), (cr)*2, (cr)*2); - cc = new Color((int) (10), (int) (10), (int) (10)); + int cr = 18; + int hs = 8; + setBbox(point1, point2, cr); + draw2Leads(g); + //getCurrent(); + doDots(g); + setPowerColor(g, true); + Color cc = new Color((int) (165), (int) (165), (int) (165)); + g.setColor(cc); + g.fillOval(motorCenter.x - (cr), motorCenter.y - (cr), (cr) * 2, (cr) * 2); + cc = new Color((int) (10), (int) (10), (int) (10)); - g.setColor(cc); - double angleAux = Math.round(angle*300.0)/300.0; - g.fillOval(motorCenter.x-(int)(cr/2.2), motorCenter.y-(int)(cr/2.2), (int)(2*cr/2.2), (int)(2*cr/2.2)); + g.setColor(cc); + double angleAux = Math.round(angle * 300.0) / 300.0; + g.fillOval(motorCenter.x - (int) (cr / 2.2), motorCenter.y - (int) (cr / 2.2), (int) (2 * cr / 2.2), (int) (2 * cr / 2.2)); - g.setColor(cc); - interpPointFix(lead1, lead2, ps1, 0.5 + .28*Math.cos(angleAux*gearRatio), .28*Math.sin(angleAux*gearRatio)); - interpPointFix(lead1, lead2, ps2, 0.5 - .28*Math.cos(angleAux*gearRatio), -.28*Math.sin(angleAux*gearRatio)); + g.setColor(cc); + interpPointFix(lead1, lead2, ps1, 0.5 + .28 * Math.cos(angleAux * gearRatio), .28 * Math.sin(angleAux * gearRatio)); + interpPointFix(lead1, lead2, ps2, 0.5 - .28 * Math.cos(angleAux * gearRatio), -.28 * Math.sin(angleAux * gearRatio)); - drawThickerLine(g, ps1, ps2); - interpPointFix(lead1, lead2, ps1, 0.5 + .28*Math.cos(angleAux*gearRatio+pi/3), .28*Math.sin(angleAux*gearRatio+pi/3)); - interpPointFix(lead1, lead2, ps2, 0.5 - .28*Math.cos(angleAux*gearRatio+pi/3), -.28*Math.sin(angleAux*gearRatio+pi/3)); + drawThickerLine(g, ps1, ps2); + interpPointFix(lead1, lead2, ps1, 0.5 + .28 * Math.cos(angleAux * gearRatio + pi / 3), .28 * Math.sin(angleAux * gearRatio + pi / 3)); + interpPointFix(lead1, lead2, ps2, 0.5 - .28 * Math.cos(angleAux * gearRatio + pi / 3), -.28 * Math.sin(angleAux * gearRatio + pi / 3)); - drawThickerLine(g, ps1, ps2); + drawThickerLine(g, ps1, ps2); - interpPointFix(lead1, lead2, ps1, 0.5 + .28*Math.cos(angleAux*gearRatio+2*pi/3), .28*Math.sin(angleAux*gearRatio+2*pi/3)); - interpPointFix(lead1, lead2, ps2, 0.5 - .28*Math.cos(angleAux*gearRatio+2*pi/3), -.28*Math.sin(angleAux*gearRatio+2*pi/3)); + interpPointFix(lead1, lead2, ps1, 0.5 + .28 * Math.cos(angleAux * gearRatio + 2 * pi / 3), .28 * Math.sin(angleAux * gearRatio + 2 * pi / 3)); + interpPointFix(lead1, lead2, ps2, 0.5 - .28 * Math.cos(angleAux * gearRatio + 2 * pi / 3), -.28 * Math.sin(angleAux * gearRatio + 2 * pi / 3)); - drawThickerLine(g, ps1, ps2); + drawThickerLine(g, ps1, ps2); - drawPosts(g); + drawPosts(g); } + static void drawThickerLine(Graphics g, Point pa, Point pb) { - g.setLineWidth(6.0); - g.drawLine(pa.x, pa.y, pb.x, pb.y); - g.setLineWidth(1.0); + g.setLineWidth(6.0); + g.drawLine(pa.x, pa.y, pb.x, pb.y); + g.setLineWidth(1.0); } void interpPointFix(Point a, Point b, Point c, double f, double g) { - int gx = b.y-a.y; - int gy = a.x-b.x; - c.x = (int) Math.round(a.x*(1-f)+b.x*f+g*gx); - c.y = (int) Math.round(a.y*(1-f)+b.y*f+g*gy); + int gx = b.y - a.y; + int gy = a.x - b.x; + c.x = (int) Math.round(a.x * (1 - f) + b.x * f + g * gx); + c.y = (int) Math.round(a.y * (1 - f) + b.y * f + g * gy); } void getInfo(String arr[]) { - arr[0] = "DC Motor"; - getBasicInfo(arr); - arr[3] = Locale.LS("speed") + " = " + getUnitText(60*Math.abs(speed)/(2*Math.PI), Locale.LS("RPM")); - arr[4] = "L = " + getUnitText(inductance, "H"); - arr[5] = "R = " + getUnitText(resistance, Locale.ohmString); - arr[6] = "P = " + getUnitText(getPower(), "W"); + arr[0] = "DC Motor"; + getBasicInfo(arr); + arr[3] = Locale.LS("speed") + " = " + getUnitText(60 * Math.abs(speed) / (2 * Math.PI), Locale.LS("RPM")); + arr[4] = "L = " + getUnitText(inductance, "H"); + arr[5] = "R = " + getUnitText(resistance, Locale.ohmString); + arr[6] = "P = " + getUnitText(getPower(), "W"); } + public EditInfo getEditInfo(int n) { - if (n == 0) - return new EditInfo("Armature inductance (H)", inductance, 0, 0); - if (n == 1) - return new EditInfo("Armature Resistance (ohms)", resistance, 0, 0); - if (n == 2) - return new EditInfo("Torque constant (Nm/A)", K, 0, 0); - if (n == 3) - return new EditInfo("Back emf constant (Vs/rad)", Kb, 0, 0); - if (n == 4) - return new EditInfo("Moment of inertia (Kg.m^2)", J, 0, 0); - if (n == 5) - return new EditInfo("Friction coefficient (Nms/rad)", b, 0, 0); - if (n == 6) - return new EditInfo("Gear Ratio", gearRatio, 0, 0); - return null; + if (n == 0) + return new EditInfo("Armature inductance (H)", inductance, 0, 0); + if (n == 1) + return new EditInfo("Armature Resistance (ohms)", resistance, 0, 0); + if (n == 2) + return new EditInfo("Torque constant (Nm/A)", K, 0, 0); + if (n == 3) + return new EditInfo("Back emf constant (Vs/rad)", Kb, 0, 0); + if (n == 4) + return new EditInfo("Moment of inertia (Kg.m^2)", J, 0, 0); + if (n == 5) + return new EditInfo("Friction coefficient (Nms/rad)", b, 0, 0); + if (n == 6) + return new EditInfo("Gear Ratio", gearRatio, 0, 0); + return null; } + public void setEditValue(int n, EditInfo ei) { - if (ei.value > 0 & n==0) { + if (ei.value > 0 & n == 0) { inductance = ei.value; ind.setup(inductance, current, Inductor.FLAG_BACK_EULER); } - if (ei.value > 0 & n==1) - resistance = ei.value; - if (ei.value > 0 & n==2) - K = ei.value; - if (ei.value > 0 & n==3) - Kb = ei.value; - if (ei.value > 0 & n==4) { + if (ei.value > 0 & n == 1) + resistance = ei.value; + if (ei.value > 0 & n == 2) + K = ei.value; + if (ei.value > 0 & n == 3) + Kb = ei.value; + if (ei.value > 0 & n == 4) { J = ei.value; indInertia.setup(J, inertiaCurrent, Inductor.FLAG_BACK_EULER); } - if (ei.value > 0 & n==5) - b = ei.value; - if (ei.value > 0 & n==6) - gearRatio = ei.value; + if (ei.value > 0 & n == 5) + b = ei.value; + if (ei.value > 0 & n == 6) + gearRatio = ei.value; } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/DCVoltageElm.java b/src/main/java/com/lushprojects/circuitjs1/client/DCVoltageElm.java index 6f86283..d878ef9 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/DCVoltageElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/DCVoltageElm.java @@ -20,8 +20,16 @@ package com.lushprojects.circuitjs1.client; -class DCVoltageElm extends VoltageElm { - public DCVoltageElm(int xx, int yy) { super(xx, yy, WF_DC); } - Class getDumpClass() { return VoltageElm.class; } - int getShortcut() { return 'v'; } +public class DCVoltageElm extends VoltageElm { + public DCVoltageElm(int xx, int yy) { + super(xx, yy, WF_DC); } + + Class getDumpClass() { + return VoltageElm.class; + } + + int getShortcut() { + return 'v'; + } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/DFlipFlopElm.java b/src/main/java/com/lushprojects/circuitjs1/client/DFlipFlopElm.java index 91a5263..154fb44 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/DFlipFlopElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/DFlipFlopElm.java @@ -19,118 +19,143 @@ package com.lushprojects.circuitjs1.client; - class DFlipFlopElm extends ChipElm { - final int FLAG_RESET = 2; - final int FLAG_SET = 4; - final int FLAG_INVERT_SET_RESET = 8; - boolean hasReset() { return (flags & FLAG_RESET) != 0 || hasSet(); } - boolean hasSet() { return (flags & FLAG_SET) != 0; } - boolean invertSetReset() { return (flags & FLAG_INVERT_SET_RESET) != 0; } - - boolean justLoaded; - - public DFlipFlopElm(int xx, int yy) { - super(xx, yy); - pins[2].value = !pins[1].value; - } - public DFlipFlopElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f, st); - pins[2].value = !pins[1].value; - justLoaded = true; - } - String getChipName() { return "D flip-flop"; } - void setupPins() { - sizeX = 2; - sizeY = 3; - pins = new Pin[getPostCount()]; - pins[0] = new Pin(0, SIDE_W, "D"); - pins[1] = new Pin(0, SIDE_E, "Q"); - pins[1].output = pins[1].state = true; - pins[2] = new Pin(hasSet()?1:2, SIDE_E, "Q"); - pins[2].output = true; - pins[2].lineOver = true; - pins[3] = new Pin(1, SIDE_W, ""); - pins[3].clock = true; - if (!hasSet()) { +public class DFlipFlopElm extends ChipElm { + final int FLAG_RESET = 2; + final int FLAG_SET = 4; + final int FLAG_INVERT_SET_RESET = 8; + + boolean hasReset() { + return (flags & FLAG_RESET) != 0 || hasSet(); + } + + boolean hasSet() { + return (flags & FLAG_SET) != 0; + } + + boolean invertSetReset() { + return (flags & FLAG_INVERT_SET_RESET) != 0; + } + + boolean justLoaded; + + public DFlipFlopElm(int xx, int yy) { + super(xx, yy); + pins[2].value = !pins[1].value; + } + + public DFlipFlopElm(int xa, int ya, int xb, int yb, int f, + StringTokenizer st) { + super(xa, ya, xb, yb, f, st); + pins[2].value = !pins[1].value; + justLoaded = true; + } + + String getChipName() { + return "D flip-flop"; + } + + void setupPins() { + sizeX = 2; + sizeY = 3; + pins = new Pin[getPostCount()]; + pins[0] = new Pin(0, SIDE_W, "D"); + pins[1] = new Pin(0, SIDE_E, "Q"); + pins[1].output = pins[1].state = true; + pins[2] = new Pin(hasSet() ? 1 : 2, SIDE_E, "Q"); + pins[2].output = true; + pins[2].lineOver = true; + pins[3] = new Pin(1, SIDE_W, ""); + pins[3].clock = true; + if (!hasSet()) { if (hasReset()) { - pins[4] = new Pin(2, SIDE_W, "R"); - pins[4].bubble = invertSetReset(); + pins[4] = new Pin(2, SIDE_W, "R"); + pins[4].bubble = invertSetReset(); } - } else { - pins[5] = new Pin(2, SIDE_W, "S"); - pins[4] = new Pin(2, SIDE_E, "R"); - pins[4].bubble = pins[5].bubble = invertSetReset(); - } - } - int getPostCount() { - return 4 + (hasReset() ? 1 : 0) + (hasSet() ? 1 : 0); - } - int getVoltageSourceCount() { return 2; } - void reset() { - super.reset(); - volts[2] = highVoltage; - pins[2].value = true; + } else { + pins[5] = new Pin(2, SIDE_W, "S"); + pins[4] = new Pin(2, SIDE_E, "R"); + pins[4].bubble = pins[5].bubble = invertSetReset(); } - void execute() { - // if we just loaded then the volts[] array is likely to be all zeroes, which might force us to do a reset, so defer execution until the next iteration - if (justLoaded) { - justLoaded = false; - return; - } - - if (pins[3].value && !lastClock) - writeOutput(1, pins[0].value); - if(hasSet() && pins[5].value != invertSetReset()) - writeOutput(1, true); - if(hasReset() && pins[4].value != invertSetReset()) - writeOutput(1, false); - writeOutput(2, !pins[1].value); - lastClock = pins[3].value; - } - int getDumpType() { return 155; } - public EditInfo getChipEditInfo(int n) { - if (n == 0) { - EditInfo ei = new EditInfo("", 0, -1, -1); - ei.checkbox = new Checkbox("Reset Pin", hasReset()); - return ei; - } - if (n == 1) { - EditInfo ei = new EditInfo("", 0, -1, -1); - ei.checkbox = new Checkbox("Set Pin", hasSet()); - return ei; - } - if (n == 2) { - EditInfo ei = new EditInfo("", 0, -1, -1); - ei.checkbox = new Checkbox("Invert Set/Reset", invertSetReset()); - return ei; - } - return super.getChipEditInfo(n); - } - public void setChipEditValue(int n, EditInfo ei) { - if (n == 0) { - if (ei.checkbox.getState()) - flags |= FLAG_RESET; - else - flags &= ~FLAG_RESET|FLAG_SET; - setupPins(); - allocNodes(); - setPoints(); - } - if (n == 1) { - if (ei.checkbox.getState()) - flags |= FLAG_SET; - else - flags &= ~FLAG_SET; - setupPins(); - allocNodes(); - setPoints(); - } - if (n == 2) { - flags = ei.changeFlag(flags, FLAG_INVERT_SET_RESET); - setupPins(); - setPoints(); - } - super.setChipEditValue(n, ei); - } } + + int getPostCount() { + return 4 + (hasReset() ? 1 : 0) + (hasSet() ? 1 : 0); + } + + int getVoltageSourceCount() { + return 2; + } + + void reset() { + super.reset(); + volts[2] = highVoltage; + pins[2].value = true; + } + + void execute() { + // if we just loaded then the volts[] array is likely to be all zeroes, which might force us to do a reset, so defer execution until the next iteration + if (justLoaded) { + justLoaded = false; + return; + } + + if (pins[3].value && !lastClock) + writeOutput(1, pins[0].value); + if (hasSet() && pins[5].value != invertSetReset()) + writeOutput(1, true); + if (hasReset() && pins[4].value != invertSetReset()) + writeOutput(1, false); + writeOutput(2, !pins[1].value); + lastClock = pins[3].value; + } + + int getDumpType() { + return 155; + } + + public EditInfo getChipEditInfo(int n) { + if (n == 0) { + EditInfo ei = new EditInfo("", 0, -1, -1); + ei.checkbox = new Checkbox("Reset Pin", hasReset()); + return ei; + } + if (n == 1) { + EditInfo ei = new EditInfo("", 0, -1, -1); + ei.checkbox = new Checkbox("Set Pin", hasSet()); + return ei; + } + if (n == 2) { + EditInfo ei = new EditInfo("", 0, -1, -1); + ei.checkbox = new Checkbox("Invert Set/Reset", invertSetReset()); + return ei; + } + return super.getChipEditInfo(n); + } + + public void setChipEditValue(int n, EditInfo ei) { + if (n == 0) { + if (ei.checkbox.getState()) + flags |= FLAG_RESET; + else + flags &= ~FLAG_RESET | FLAG_SET; + setupPins(); + allocNodes(); + setPoints(); + } + if (n == 1) { + if (ei.checkbox.getState()) + flags |= FLAG_SET; + else + flags &= ~FLAG_SET; + setupPins(); + allocNodes(); + setPoints(); + } + if (n == 2) { + flags = ei.changeFlag(flags, FLAG_INVERT_SET_RESET); + setupPins(); + setPoints(); + } + super.setChipEditValue(n, ei); + } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/DarlingtonElm.java b/src/main/java/com/lushprojects/circuitjs1/client/DarlingtonElm.java index 7d6c511..756bdde 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/DarlingtonElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/DarlingtonElm.java @@ -11,140 +11,136 @@ public class DarlingtonElm extends CompositeElm { private Polygon rectPoly, arrowPoly; private Point rect[], coll[], emit[], base, coll2[]; - + private int pnp; // +1 for NPN, -1 for PNP; private double curcount_c, curcount_e, curcount_b; private static String modelString = "NTransistorElm 1 2 4\rNTransistorElm 4 2 3"; private static int[] modelExternalNodes = {1, 2, 3}; - + DarlingtonElm(int xx, int yy, boolean pnpflag) { - super(xx, yy, modelString, modelExternalNodes); - pnp = (pnpflag) ? -1 : 1; - ((TransistorElm) compElmList.get(0)).pnp=pnp; - ((TransistorElm) compElmList.get(1)).pnp=pnp; - noDiagonal = true; - + super(xx, yy, modelString, modelExternalNodes); + pnp = (pnpflag) ? -1 : 1; + ((TransistorElm) compElmList.get(0)).pnp = pnp; + ((TransistorElm) compElmList.get(1)).pnp = pnp; + noDiagonal = true; + } - + public DarlingtonElm(int xa, int ya, int xb, int yb, int f, StringTokenizer st) { - super(xa, ya, xb, yb, f, st, modelString, modelExternalNodes); - pnp = new Integer(st.nextToken()).intValue(); - noDiagonal = true; + super(xa, ya, xb, yb, f, st, modelString, modelExternalNodes); + pnp = new Integer(st.nextToken()).intValue(); + noDiagonal = true; } public void reset() { - super.reset(); - curcount_c = curcount_e = curcount_b = 0; + super.reset(); + curcount_c = curcount_e = curcount_b = 0; } - + public int getDumpType() { - return 400; + return 400; } public String dump() { - return super.dump()+" "+pnp; + return super.dump() + " " + pnp; } - + void draw(Graphics g) { - setBbox(point1, point2, 16); - setPowerColor(g, true); - // draw collector - setVoltageColor(g, volts[1]); - drawThickLine(g, coll[0], coll[1]); - drawThickLine(g, coll2[0], coll2[1]); - drawThickLine(g, coll[0], coll2[0]); - // draw emitter - setVoltageColor(g, volts[2]); - drawThickLine(g, emit[0], emit[1]); - // draw arrow - g.setColor(lightGrayColor); - g.fillPolygon(arrowPoly); - // draw base - setVoltageColor(g, volts[0]); - if (sim.powerCheckItem.getState()) - g.setColor(Color.gray); - drawThickLine(g, point1, base); - // draw dots - curcount_b = updateDotCount(getCurrentIntoNode(0), curcount_b); - drawDots(g, base, point1, curcount_b); - curcount_c = updateDotCount(getCurrentIntoNode(1), curcount_c); - drawDots(g, coll[1], coll[0], curcount_c); - curcount_e = updateDotCount(getCurrentIntoNode(2), curcount_e); - drawDots(g, emit[1], emit[0], curcount_e); - // draw base rectangle - setVoltageColor(g, volts[0]); - setPowerColor(g, true); - g.fillPolygon(rectPoly); - - if ((needsHighlight() || sim.dragElm == this) && dy == 0) { - g.setColor(whiteColor); - // IES - // g.setFont(unitsFont); - int ds = sign(dx); - g.drawString("B", base.x - 10 * ds, base.y - 5); - g.drawString("C", coll[0].x - 3 + 9 * ds, coll[0].y + 4); // x+6 if - // ds=1, - // -12 if - // -1 - g.drawString("E", emit[0].x - 3 + 9 * ds, emit[0].y + 4); - } - drawPosts(g); + setBbox(point1, point2, 16); + setPowerColor(g, true); + // draw collector + setVoltageColor(g, volts[1]); + drawThickLine(g, coll[0], coll[1]); + drawThickLine(g, coll2[0], coll2[1]); + drawThickLine(g, coll[0], coll2[0]); + // draw emitter + setVoltageColor(g, volts[2]); + drawThickLine(g, emit[0], emit[1]); + // draw arrow + g.setColor(lightGrayColor); + g.fillPolygon(arrowPoly); + // draw base + setVoltageColor(g, volts[0]); + if (sim.powerCheckItem.getState()) + g.setColor(Color.gray); + drawThickLine(g, point1, base); + // draw dots + curcount_b = updateDotCount(getCurrentIntoNode(0), curcount_b); + drawDots(g, base, point1, curcount_b); + curcount_c = updateDotCount(getCurrentIntoNode(1), curcount_c); + drawDots(g, coll[1], coll[0], curcount_c); + curcount_e = updateDotCount(getCurrentIntoNode(2), curcount_e); + drawDots(g, emit[1], emit[0], curcount_e); + // draw base rectangle + setVoltageColor(g, volts[0]); + setPowerColor(g, true); + g.fillPolygon(rectPoly); + + if ((needsHighlight() || sim.dragElm == this) && dy == 0) { + g.setColor(whiteColor); + // IES + // g.setFont(unitsFont); + int ds = sign(dx); + g.drawString("B", base.x - 10 * ds, base.y - 5); + g.drawString("C", coll[0].x - 3 + 9 * ds, coll[0].y + 4); // x+6 if + // ds=1, + // -12 if + // -1 + g.drawString("E", emit[0].x - 3 + 9 * ds, emit[0].y + 4); + } + drawPosts(g); } - - void getInfo(String arr[]) { - arr[0] = Locale.LS("darlington pair") + " (" + ((pnp == -1) ? "PNP)" : "NPN)"); - double vbc = volts[0] - volts[1]; - double vbe = volts[0] - volts[2]; - double vce = volts[1] - volts[2]; - arr[1] = "Ic = " + getCurrentText(-getCurrentIntoNode(1)); - arr[2] = "Ib = " + getCurrentText(-getCurrentIntoNode(0)); - arr[3] = "Vbe = " + getVoltageText(vbe); - arr[4] = "Vbc = " + getVoltageText(vbc); - arr[5] = "Vce = " + getVoltageText(vce); + arr[0] = Locale.LS("darlington pair") + " (" + ((pnp == -1) ? "PNP)" : "NPN)"); + double vbc = volts[0] - volts[1]; + double vbe = volts[0] - volts[2]; + double vce = volts[1] - volts[2]; + arr[1] = "Ic = " + getCurrentText(-getCurrentIntoNode(1)); + arr[2] = "Ib = " + getCurrentText(-getCurrentIntoNode(0)); + arr[3] = "Vbe = " + getVoltageText(vbe); + arr[4] = "Vbc = " + getVoltageText(vbc); + arr[5] = "Vce = " + getVoltageText(vce); } void setPoints() { - super.setPoints(); - int hs = 16; - int hs2 = hs * dsign * pnp; - // calc collector, emitter posts - coll = newPointArray(2); - coll2 = newPointArray(2); - emit = newPointArray(2); - interpPoint2(point1, point2, coll[0], emit[0], 1, hs2); - coll2[0]=interpPoint(point1, point2, 1, hs2-5*dsign*pnp); - // calc rectangle edges - rect = newPointArray(4); - interpPoint2(point1, point2, rect[0], rect[1], 1 - 16 / dn, hs); - interpPoint2(point1, point2, rect[2], rect[3], 1 - 13 / dn, hs); - // calc points where collector/emitter leads contact rectangle - interpPoint2(point1, point2, coll[1], emit[1], 1 - 13 / dn, 6 * dsign * pnp); - coll2[1]=interpPoint(point1, point2, 1-13/dn, dsign*pnp); - // calc point where base lead contacts rectangle - base = new Point(); - interpPoint(point1, point2, base, 1 - 16 / dn); - // rectangle - rectPoly = createPolygon(rect[0], rect[2], rect[3], rect[1]); - // arrow - if (pnp == 1) - arrowPoly = calcArrow(emit[1], emit[0], 8, 4); - else { - Point pt = interpPoint(point1, point2, 1-11/dn, -5*dsign*pnp); - arrowPoly = calcArrow(emit[0], pt, 8, 4); - } - setPost(0, point1); - setPost(1,coll[0]); - setPost(2,emit[0]); + super.setPoints(); + int hs = 16; + int hs2 = hs * dsign * pnp; + // calc collector, emitter posts + coll = newPointArray(2); + coll2 = newPointArray(2); + emit = newPointArray(2); + interpPoint2(point1, point2, coll[0], emit[0], 1, hs2); + coll2[0] = interpPoint(point1, point2, 1, hs2 - 5 * dsign * pnp); + // calc rectangle edges + rect = newPointArray(4); + interpPoint2(point1, point2, rect[0], rect[1], 1 - 16 / dn, hs); + interpPoint2(point1, point2, rect[2], rect[3], 1 - 13 / dn, hs); + // calc points where collector/emitter leads contact rectangle + interpPoint2(point1, point2, coll[1], emit[1], 1 - 13 / dn, 6 * dsign * pnp); + coll2[1] = interpPoint(point1, point2, 1 - 13 / dn, dsign * pnp); + // calc point where base lead contacts rectangle + base = new Point(); + interpPoint(point1, point2, base, 1 - 16 / dn); + // rectangle + rectPoly = createPolygon(rect[0], rect[2], rect[3], rect[1]); + // arrow + if (pnp == 1) + arrowPoly = calcArrow(emit[1], emit[0], 8, 4); + else { + Point pt = interpPoint(point1, point2, 1 - 11 / dn, -5 * dsign * pnp); + arrowPoly = calcArrow(emit[0], pt, 8, 4); + } + setPost(0, point1); + setPost(1, coll[0]); + setPost(2, emit[0]); } - - } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/DataInputElm.java b/src/main/java/com/lushprojects/circuitjs1/client/DataInputElm.java index e6f906a..eb85aa8 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/DataInputElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/DataInputElm.java @@ -21,135 +21,143 @@ import java.util.ArrayList; import java.util.HashMap; + import com.google.gwt.dom.client.Element; import com.google.gwt.event.dom.client.ChangeEvent; import com.google.gwt.event.dom.client.ChangeHandler; import com.google.gwt.user.client.ui.FileUpload; -class DataFileEntry { +public class DataFileEntry { String fileName; ArrayList data; } -class DataInputElm extends RailElm { - ArrayList data; - double sampleLength; - double scaleFactor; - double timeOffset; - int fileNum; - String fileName; - final int FLAG_REPEAT = 1<<8; - - // cache to preserve data when doing cut/paste, or undo/redo - static int fileNumCounter = 1; - static HashMap dataFileMap = new HashMap(); - - public DataInputElm(int xx, int yy) { - super(xx, yy, WF_AC); - scaleFactor = 1; - sampleLength = 1e-3; - } - - public DataInputElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f, st); - waveform = WF_AC; - sampleLength = Double.parseDouble(st.nextToken()); - scaleFactor = Double.parseDouble(st.nextToken()); - fileNum = Integer.parseInt(st.nextToken()); - - DataFileEntry ent = dataFileMap.get(fileNum); - if (ent != null) { - fileName = ent.fileName; - data = ent.data; - } - } - - double fmphase; - - String dump() { - // add a file number to the dump so we can preserve the data when doing cut and paste, or undo/redo. - // we don't save the entire file in the dump because it would be huge. - if (data != null) { - if (fileNum == 0) - fileNum = fileNumCounter++; - DataFileEntry ent = new DataFileEntry(); - ent.fileName = fileName; - ent.data = data; - dataFileMap.put(fileNum, ent); - } - return super.dump() + " " + sampleLength + " " + scaleFactor + " " + fileNum; - } - - void reset() { - timeOffset = 0; - } - - void drawRail(Graphics g) { - drawRailText(g, fileName == null ? "No file" : fileName); - } - - String getRailText() { - return fileName == null ? "No file" : fileName; - } - - boolean doesRepeat() { return (flags & FLAG_REPEAT) != 0; } - - double getVoltage() { - if (data == null) - return 0; - int ptr = (int) (timeOffset / sampleLength); - if (ptr >= data.size()) { - if (doesRepeat()) { - ptr = 0; - timeOffset = 0; - } else - ptr = data.size()-1; - } - return data.get(ptr) * scaleFactor; - } - - void stepFinished() { - timeOffset += sim.timeStep; - } - - int getDumpType() { return 424; } - int getShortcut() { return 0; } - - public EditInfo getEditInfo(int n) { - if (n == 0) { - EditInfo ei = new EditInfo("", 0, -1, -1); - final DataInputElm thisElm = this; - final FileUpload file = new FileUpload(); - ei.widget = file; - file.addChangeHandler(new ChangeHandler() { - public void onChange(ChangeEvent event) { - fileName = file.getFilename().replaceAll("^.*\\\\", "").replaceAll("\\.[^.]*$", ""); - DataInputElm.fetchLoadFileData(thisElm, file.getElement()); - } - }); - return ei; - } - if (n == 1) - return new EditInfo("Scale Factor", scaleFactor); - if (n == 2) - return new EditInfo("Sample Length (s)", sampleLength); - if (n == 3) - return EditInfo.createCheckbox("Repeat", doesRepeat()); - return null; - } - - public void setEditValue(int n, EditInfo ei) { - if (n == 1) - scaleFactor = ei.value; - if (n == 2) - sampleLength = ei.value; - if (n == 3) - flags = ei.changeFlag(flags, FLAG_REPEAT); - } - - // fetch data for a selected file - static native String fetchLoadFileData(DataInputElm elm, Element uploadElement) /*-{ +public class DataInputElm extends RailElm { + ArrayList data; + double sampleLength; + double scaleFactor; + double timeOffset; + int fileNum; + String fileName; + final int FLAG_REPEAT = 1 << 8; + + // cache to preserve data when doing cut/paste, or undo/redo + static int fileNumCounter = 1; + static HashMap dataFileMap = new HashMap(); + + public DataInputElm(int xx, int yy) { + super(xx, yy, WF_AC); + scaleFactor = 1; + sampleLength = 1e-3; + } + + public DataInputElm(int xa, int ya, int xb, int yb, int f, + StringTokenizer st) { + super(xa, ya, xb, yb, f, st); + waveform = WF_AC; + sampleLength = Double.parseDouble(st.nextToken()); + scaleFactor = Double.parseDouble(st.nextToken()); + fileNum = Integer.parseInt(st.nextToken()); + + DataFileEntry ent = dataFileMap.get(fileNum); + if (ent != null) { + fileName = ent.fileName; + data = ent.data; + } + } + + double fmphase; + + String dump() { + // add a file number to the dump so we can preserve the data when doing cut and paste, or undo/redo. + // we don't save the entire file in the dump because it would be huge. + if (data != null) { + if (fileNum == 0) + fileNum = fileNumCounter++; + DataFileEntry ent = new DataFileEntry(); + ent.fileName = fileName; + ent.data = data; + dataFileMap.put(fileNum, ent); + } + return super.dump() + " " + sampleLength + " " + scaleFactor + " " + fileNum; + } + + void reset() { + timeOffset = 0; + } + + void drawRail(Graphics g) { + drawRailText(g, fileName == null ? "No file" : fileName); + } + + String getRailText() { + return fileName == null ? "No file" : fileName; + } + + boolean doesRepeat() { + return (flags & FLAG_REPEAT) != 0; + } + + double getVoltage() { + if (data == null) + return 0; + int ptr = (int) (timeOffset / sampleLength); + if (ptr >= data.size()) { + if (doesRepeat()) { + ptr = 0; + timeOffset = 0; + } else + ptr = data.size() - 1; + } + return data.get(ptr) * scaleFactor; + } + + void stepFinished() { + timeOffset += sim.timeStep; + } + + int getDumpType() { + return 424; + } + + int getShortcut() { + return 0; + } + + public EditInfo getEditInfo(int n) { + if (n == 0) { + EditInfo ei = new EditInfo("", 0, -1, -1); + final DataInputElm thisElm = this; + final FileUpload file = new FileUpload(); + ei.widget = file; + file.addChangeHandler(new ChangeHandler() { + public void onChange(ChangeEvent event) { + fileName = file.getFilename().replaceAll("^.*\\\\", "").replaceAll("\\.[^.]*$", ""); + DataInputElm.fetchLoadFileData(thisElm, file.getElement()); + } + }); + return ei; + } + if (n == 1) + return new EditInfo("Scale Factor", scaleFactor); + if (n == 2) + return new EditInfo("Sample Length (s)", sampleLength); + if (n == 3) + return EditInfo.createCheckbox("Repeat", doesRepeat()); + return null; + } + + public void setEditValue(int n, EditInfo ei) { + if (n == 1) + scaleFactor = ei.value; + if (n == 2) + sampleLength = ei.value; + if (n == 3) + flags = ei.changeFlag(flags, FLAG_REPEAT); + } + + // fetch data for a selected file + static native String fetchLoadFileData(DataInputElm elm, Element uploadElement) /*-{ var oFiles = uploadElement.files; if (oFiles.length >= 1) { var reader = new FileReader(); @@ -162,41 +170,41 @@ static native String fetchLoadFileData(DataInputElm elm, Element uploadElement) } }-*/; - void doLoadCallback(String s, String t) { - // parse data file. each line contains a single voltage value - String arr[] = s.split("\r*\n"); - data = new ArrayList(); - int i; - for (i = 0; i != arr.length; i++) { - // skip blank lines - if (arr[i].length() == 0) - continue; - - // skip comments - if (arr[i].charAt(0) == '#') - continue; - try { - double d = Double.parseDouble(arr[i]); - data.add(d); - } catch (Exception e) { - CirSim.console("parse error on line " + i); - } + void doLoadCallback(String s, String t) { + // parse data file. each line contains a single voltage value + String arr[] = s.split("\r*\n"); + data = new ArrayList(); + int i; + for (i = 0; i != arr.length; i++) { + // skip blank lines + if (arr[i].length() == 0) + continue; + + // skip comments + if (arr[i].charAt(0) == '#') + continue; + try { + double d = Double.parseDouble(arr[i]); + data.add(d); + } catch (Exception e) { + CirSim.console("parse error on line " + i); } - } - - void getInfo(String arr[]) { - arr[0] = "data input"; - if (data == null) { - arr[1] = "no file loaded"; - return; - } - arr[1] = "V = " + getVoltageText(volts[0]); - arr[2] = "pos = " + getUnitText(timeOffset, "s"); - double dur = data.size() * sampleLength; - arr[3] = "dur = " + getUnitText(dur, "s"); - } - - public static void clearCache() { - dataFileMap.clear(); - } + } } + + void getInfo(String arr[]) { + arr[0] = "data input"; + if (data == null) { + arr[1] = "no file loaded"; + return; + } + arr[1] = "V = " + getVoltageText(volts[0]); + arr[2] = "pos = " + getUnitText(timeOffset, "s"); + double dur = data.size() * sampleLength; + arr[3] = "dur = " + getUnitText(dur, "s"); + } + + public static void clearCache() { + dataFileMap.clear(); + } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/DataRecorderElm.java b/src/main/java/com/lushprojects/circuitjs1/client/DataRecorderElm.java index 37e4fcc..4a7d0da 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/DataRecorderElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/DataRecorderElm.java @@ -11,71 +11,87 @@ public class DataRecorderElm extends CircuitElm { int lastTimeStepCount; double data[]; boolean dataFull; - - public DataRecorderElm(int xx, int yy) { - super(xx, yy); - setDataCount(10240); - } - public DataRecorderElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f); - setDataCount(Integer.parseInt(st.nextToken())); - } - String dump() { - return super.dump() + " " + dataCount; - } - int getDumpType() { return 210; } - int getPostCount() { return 1; } - void reset() { - dataPtr = 0; - dataFull = false; - lastTimeStepCount = 0; - } - void setPoints() { - super.setPoints(); - lead1 = interpPoint(point1, point2, 1-8/dn); - } - void draw(Graphics g) { - g.save(); - boolean selected = (needsHighlight()); - Font f = new Font("SansSerif", selected ? Font.BOLD : 0, 14); - g.setFont(f); - g.setColor(selected ? selectColor : whiteColor); - setBbox(point1, lead1, 0); - String s = Locale.LS("export"); - drawLabeledNode(g, s, point1, lead1); - setVoltageColor(g, volts[0]); - if (selected) - g.setColor(selectColor); - drawThickLine(g, point1, lead1); - drawPosts(g); - g.restore(); - } - double getVoltageDiff() { return volts[0]; } - void getInfo(String arr[]) { - arr[0] = "data export"; - arr[1] = "V = " + getVoltageText(volts[0]); - arr[2] = (dataFull ? dataCount : dataPtr) + "/" + dataCount; - } - void stepFinished() { - if (lastTimeStepCount == sim.timeStepCount) - return; - data[dataPtr++] = volts[0]; - lastTimeStepCount = sim.timeStepCount; - if (dataPtr >= dataCount) { - dataPtr = 0; - dataFull = true; - } - } - - void setDataCount(int ct) { - dataCount = ct; - data = new double[dataCount]; - dataPtr = 0; - dataFull = false; - } - - static public final native String getBlobUrl(String data) + + public DataRecorderElm(int xx, int yy) { + super(xx, yy); + setDataCount(10240); + } + + public DataRecorderElm(int xa, int ya, int xb, int yb, int f, + StringTokenizer st) { + super(xa, ya, xb, yb, f); + setDataCount(Integer.parseInt(st.nextToken())); + } + + String dump() { + return super.dump() + " " + dataCount; + } + + int getDumpType() { + return 210; + } + + int getPostCount() { + return 1; + } + + void reset() { + dataPtr = 0; + dataFull = false; + lastTimeStepCount = 0; + } + + void setPoints() { + super.setPoints(); + lead1 = interpPoint(point1, point2, 1 - 8 / dn); + } + + void draw(Graphics g) { + g.save(); + boolean selected = (needsHighlight()); + Font f = new Font("SansSerif", selected ? Font.BOLD : 0, 14); + g.setFont(f); + g.setColor(selected ? selectColor : whiteColor); + setBbox(point1, lead1, 0); + String s = Locale.LS("export"); + drawLabeledNode(g, s, point1, lead1); + setVoltageColor(g, volts[0]); + if (selected) + g.setColor(selectColor); + drawThickLine(g, point1, lead1); + drawPosts(g); + g.restore(); + } + + double getVoltageDiff() { + return volts[0]; + } + + void getInfo(String arr[]) { + arr[0] = "data export"; + arr[1] = "V = " + getVoltageText(volts[0]); + arr[2] = (dataFull ? dataCount : dataPtr) + "/" + dataCount; + } + + void stepFinished() { + if (lastTimeStepCount == sim.timeStepCount) + return; + data[dataPtr++] = volts[0]; + lastTimeStepCount = sim.timeStepCount; + if (dataPtr >= dataCount) { + dataPtr = 0; + dataFull = true; + } + } + + void setDataCount(int ct) { + dataCount = ct; + data = new double[dataCount]; + dataPtr = 0; + dataFull = false; + } + + static public final native String getBlobUrl(String data) /*-{ var datain=[""]; datain[0]=data; @@ -89,38 +105,39 @@ static public final native String getBlobUrl(String data) return url; }-*/; - public EditInfo getEditInfo(int n) { - if (n == 0) { - EditInfo ei = new EditInfo("# of Data Points", dataCount, -1, -1).setDimensionless(); - return ei; - } - if (n == 1) { - EditInfo ei = new EditInfo("", 0, -1, -1); - String dataStr = "# time step = " + sim.timeStep + " sec\n"; - int i; - if (dataFull) { - for (i = 0; i != dataCount; i++) - dataStr += data[(i+dataPtr) % dataCount] + "\n"; - } else { - for (i = 0; i != dataPtr; i++) - dataStr += data[i] + "\n"; - } - String url=getBlobUrl(dataStr); - Date date = new Date(); - DateTimeFormat dtf = DateTimeFormat.getFormat("yyyyMMdd-HHmm"); - String fname = "data-"+ dtf.format(date) + ".circuitjs.txt"; - Anchor a=new Anchor(fname, url); - a.getElement().setAttribute("Download", fname); - ei.widget = a; - return ei; + public EditInfo getEditInfo(int n) { + if (n == 0) { + EditInfo ei = new EditInfo("# of Data Points", dataCount, -1, -1).setDimensionless(); + return ei; + } + if (n == 1) { + EditInfo ei = new EditInfo("", 0, -1, -1); + String dataStr = "# time step = " + sim.timeStep + " sec\n"; + int i; + if (dataFull) { + for (i = 0; i != dataCount; i++) + dataStr += data[(i + dataPtr) % dataCount] + "\n"; + } else { + for (i = 0; i != dataPtr; i++) + dataStr += data[i] + "\n"; } - return null; - } - public void setEditValue(int n, EditInfo ei) { - if (n == 0 && ei.value > 0) { - setDataCount((int)ei.value); - } - if (n == 1) - return; - } + String url = getBlobUrl(dataStr); + Date date = new Date(); + DateTimeFormat dtf = DateTimeFormat.getFormat("yyyyMMdd-HHmm"); + String fname = "data-" + dtf.format(date) + ".circuitjs.txt"; + Anchor a = new Anchor(fname, url); + a.getElement().setAttribute("Download", fname); + ei.widget = a; + return ei; + } + return null; + } + + public void setEditValue(int n, EditInfo ei) { + if (n == 0 && ei.value > 0) { + setDataCount((int) ei.value); + } + if (n == 1) + return; + } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/DeMultiplexerElm.java b/src/main/java/com/lushprojects/circuitjs1/client/DeMultiplexerElm.java index 2e336ee..92a759d 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/DeMultiplexerElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/DeMultiplexerElm.java @@ -21,72 +21,93 @@ // contributed by Edward Calver - class DeMultiplexerElm extends ChipElm { - int selectBitCount; - int outputCount; - int qPin; - boolean hasReset() {return false;} - public DeMultiplexerElm(int xx, int yy) { super(xx, yy); } - public DeMultiplexerElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f, st); - try { - selectBitCount = Integer.parseInt(st.nextToken()); - setupPins(); - allocNodes(); - } catch (Exception e) {} - } - String getChipName() { return "demultiplexer"; } - String dump() { return super.dump() + " " + selectBitCount; } - - void setupPins() { - if (selectBitCount == 0) - selectBitCount = 2; - outputCount = 1 << selectBitCount; - sizeX = 1+selectBitCount; - sizeY = 1+outputCount; - pins = new Pin[getPostCount()]; - int i; - for (i = 0; i != outputCount; i++) { - pins[i] = new Pin(i, SIDE_E, "Q" + i); - pins[i].output=true; - } - for (i = 0; i != selectBitCount; i++) { - int ii = i+outputCount; - pins[ii] = new Pin(i, SIDE_S, "S" + i); - } - qPin = outputCount+selectBitCount; - pins[qPin] = new Pin(0, SIDE_W, "Q"); - } - int getPostCount() { - return qPin+1; - } - int getVoltageSourceCount() { return outputCount; } - - void execute() { - int val = 0; - int i; - for (i = 0; i != selectBitCount; i++) - if (pins[i+outputCount].value) - val |= 1<= 1 && ei.value <= 6) { - selectBitCount = (int)ei.value; - setupPins(); - setPoints(); - } + for (i = 0; i != selectBitCount; i++) { + int ii = i + outputCount; + pins[ii] = new Pin(i, SIDE_S, "S" + i); } + qPin = outputCount + selectBitCount; + pins[qPin] = new Pin(0, SIDE_W, "Q"); + } + + int getPostCount() { + return qPin + 1; + } + + int getVoltageSourceCount() { + return outputCount; + } + + void execute() { + int val = 0; + int i; + for (i = 0; i != selectBitCount; i++) + if (pins[i + outputCount].value) + val |= 1 << i; + for (i = 0; i != outputCount; i++) + pins[i].value = false; + pins[val].value = pins[qPin].value; + } + + public EditInfo getChipEditInfo(int n) { + if (n == 0) + return new EditInfo("# of Select Bits", selectBitCount).setDimensionless(); + return null; + } - int getDumpType() { return 185; } + public void setChipEditValue(int n, EditInfo ei) { + if (n == 0 && ei.value >= 1 && ei.value <= 6) { + selectBitCount = (int) ei.value; + setupPins(); + setPoints(); + } + } + int getDumpType() { + return 185; } + +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/DecimalDisplayElm.java b/src/main/java/com/lushprojects/circuitjs1/client/DecimalDisplayElm.java index 2a82554..0b16065 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/DecimalDisplayElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/DecimalDisplayElm.java @@ -19,66 +19,84 @@ package com.lushprojects.circuitjs1.client; -class DecimalDisplayElm extends ChipElm { +public class DecimalDisplayElm extends ChipElm { int bitCount; - + public DecimalDisplayElm(int xx, int yy) { - super(xx, yy); - bitCount = 4; - setupPins(); + super(xx, yy); + bitCount = 4; + setupPins(); } + public DecimalDisplayElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f, st); - bitCount = 4; - try { - bitCount = Integer.parseInt(st.nextToken()); - } catch (Exception e) {} - setupPins(); + StringTokenizer st) { + super(xa, ya, xb, yb, f, st); + bitCount = 4; + try { + bitCount = Integer.parseInt(st.nextToken()); + } catch (Exception e) { + } + setupPins(); } - String getChipName() { return "decimal display"; } - + + String getChipName() { + return "decimal display"; + } + void draw(Graphics g) { drawChip(g); - int xl = x+cspc + flippedSizeX*cspc; - int yl = y-cspc + flippedSizeY*cspc; - if (isFlippedXY()) - yl += ((flags & FLAG_FLIP_Y) != 0) ? -cspc/2 : cspc/2; + int xl = x + cspc + flippedSizeX * cspc; + int yl = y - cspc + flippedSizeY * cspc; + if (isFlippedXY()) + yl += ((flags & FLAG_FLIP_Y) != 0) ? -cspc / 2 : cspc / 2; g.save(); - g.setFont(new Font("SansSerif", 0, 15*csize)); + g.setFont(new Font("SansSerif", 0, 15 * csize)); g.setColor(whiteColor); g.context.setTextBaseline("middle"); int i; int value = 0; for (i = 0; i != bitCount; i++) if (pins[i].value) - value |= 1<= 1 && ei.value <= 16) { bitCount = (int) ei.value; diff --git a/src/main/java/com/lushprojects/circuitjs1/client/DelayBufferElm.java b/src/main/java/com/lushprojects/circuitjs1/client/DelayBufferElm.java index b38bfab..8e272a7 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/DelayBufferElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/DelayBufferElm.java @@ -21,122 +21,144 @@ import com.lushprojects.circuitjs1.client.util.Locale; -class DelayBufferElm extends CircuitElm { - double delay, threshold, highVoltage; - - public DelayBufferElm(int xx, int yy) { - super(xx, yy); - noDiagonal = true; - threshold = 2.5; - highVoltage = 5; - } - public DelayBufferElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f); - noDiagonal = true; - delay = Double.parseDouble(st.nextToken()); - threshold = 2.5; - highVoltage = 5; - try { - threshold = Double.parseDouble(st.nextToken()); - highVoltage = Double.parseDouble(st.nextToken()); - } catch (Exception e) {} - } - String dump() { - return super.dump() + " " + delay + " " + threshold + " " + highVoltage; - } - - int getDumpType() { return 422; } - - Point center; - - void draw(Graphics g) { - drawPosts(g); - draw2Leads(g); - g.setColor(needsHighlight() ? selectColor : lightGrayColor); - drawThickPolygon(g, gatePoly); - if (GateElm.useEuroGates()) - drawCenteredText(g, "1", center.x, center.y-6, true); - curcount = updateDotCount(current, curcount); - drawDots(g, lead2, point2, curcount); - } - Polygon gatePoly; - void setPoints() { - super.setPoints(); - int hs = 16; - int ww = 16-2; - if (ww > dn/2) - ww = (int) (dn/2); - lead1 = interpPoint(point1, point2, .5-ww/dn); - lead2 = interpPoint(point1, point2, .5+ww/dn); - - if (GateElm.useEuroGates()) { - Point pts[] = newPointArray(4); - Point l2 = interpPoint(point1, point2, .5+(ww-5)/dn); - interpPoint2(lead1, l2, pts[0], pts[1], 0, hs); - interpPoint2(lead1, l2, pts[3], pts[2], 1, hs); - gatePoly = createPolygon(pts); - center = interpPoint(lead1, l2, .5); - } else { - Point triPoints[] = newPointArray(3); - interpPoint2(lead1, lead2, triPoints[0], triPoints[1], 0, hs); - triPoints[2] = interpPoint(point1, point2, .5+ww/dn); - gatePoly = createPolygon(triPoints); - } - setBbox(point1, point2, hs); - } - int getVoltageSourceCount() { return 1; } - void stamp() { - sim.stampVoltageSource(0, nodes[1], voltSource); - } - - double delayEndTime; - - void doStep() { - boolean inState = volts[0] > threshold; - boolean outState = volts[1] > threshold; - if (inState != outState) { - if (sim.t >= delayEndTime) - outState = inState; - } else - delayEndTime = sim.t + delay; - sim.updateVoltageSource(0, nodes[1], voltSource, outState ? highVoltage : 0); - } - double getVoltageDiff() { return volts[0]; } - void getInfo(String arr[]) { - arr[0] = Locale.LS("buffer"); - arr[1] = Locale.LS("delay = " )+ getUnitText(delay, "s"); - arr[2] = "Vi = " + getVoltageText(volts[0]); - arr[3] = "Vo = " + getVoltageText(volts[1]); - } - public EditInfo getEditInfo(int n) { - if (n == 0) - return new EditInfo("Delay (s)", delay, 0, 0); - if (n == 1) - return new EditInfo("Threshold (V)", threshold, 0, 0); - if (n == 2) - return new EditInfo("High Logic Voltage", highVoltage, 0, 0); - return null; - } - public void setEditValue(int n, EditInfo ei) { - if (n == 0) - delay = ei.value; - if (n == 1) - threshold = ei.value; - if (n == 2) - highVoltage = ei.value; - } - // there is no current path through the inverter input, but there - // is an indirect path through the output to ground. - boolean getConnection(int n1, int n2) { return false; } - boolean hasGroundConnection(int n1) { - return (n1 == 1); - } - - @Override double getCurrentIntoNode(int n) { - if (n == 1) - return current; - return 0; - } +public class DelayBufferElm extends CircuitElm { + double delay, threshold, highVoltage; + public DelayBufferElm(int xx, int yy) { + super(xx, yy); + noDiagonal = true; + threshold = 2.5; + highVoltage = 5; } + + public DelayBufferElm(int xa, int ya, int xb, int yb, int f, + StringTokenizer st) { + super(xa, ya, xb, yb, f); + noDiagonal = true; + delay = Double.parseDouble(st.nextToken()); + threshold = 2.5; + highVoltage = 5; + try { + threshold = Double.parseDouble(st.nextToken()); + highVoltage = Double.parseDouble(st.nextToken()); + } catch (Exception e) { + } + } + + String dump() { + return super.dump() + " " + delay + " " + threshold + " " + highVoltage; + } + + int getDumpType() { + return 422; + } + + Point center; + + void draw(Graphics g) { + drawPosts(g); + draw2Leads(g); + g.setColor(needsHighlight() ? selectColor : lightGrayColor); + drawThickPolygon(g, gatePoly); + if (GateElm.useEuroGates()) + drawCenteredText(g, "1", center.x, center.y - 6, true); + curcount = updateDotCount(current, curcount); + drawDots(g, lead2, point2, curcount); + } + + Polygon gatePoly; + + void setPoints() { + super.setPoints(); + int hs = 16; + int ww = 16 - 2; + if (ww > dn / 2) + ww = (int) (dn / 2); + lead1 = interpPoint(point1, point2, .5 - ww / dn); + lead2 = interpPoint(point1, point2, .5 + ww / dn); + + if (GateElm.useEuroGates()) { + Point pts[] = newPointArray(4); + Point l2 = interpPoint(point1, point2, .5 + (ww - 5) / dn); + interpPoint2(lead1, l2, pts[0], pts[1], 0, hs); + interpPoint2(lead1, l2, pts[3], pts[2], 1, hs); + gatePoly = createPolygon(pts); + center = interpPoint(lead1, l2, .5); + } else { + Point triPoints[] = newPointArray(3); + interpPoint2(lead1, lead2, triPoints[0], triPoints[1], 0, hs); + triPoints[2] = interpPoint(point1, point2, .5 + ww / dn); + gatePoly = createPolygon(triPoints); + } + setBbox(point1, point2, hs); + } + + int getVoltageSourceCount() { + return 1; + } + + void stamp() { + sim.stampVoltageSource(0, nodes[1], voltSource); + } + + double delayEndTime; + + void doStep() { + boolean inState = volts[0] > threshold; + boolean outState = volts[1] > threshold; + if (inState != outState) { + if (sim.t >= delayEndTime) + outState = inState; + } else + delayEndTime = sim.t + delay; + sim.updateVoltageSource(0, nodes[1], voltSource, outState ? highVoltage : 0); + } + + double getVoltageDiff() { + return volts[0]; + } + + void getInfo(String arr[]) { + arr[0] = Locale.LS("buffer"); + arr[1] = Locale.LS("delay = ") + getUnitText(delay, "s"); + arr[2] = "Vi = " + getVoltageText(volts[0]); + arr[3] = "Vo = " + getVoltageText(volts[1]); + } + + public EditInfo getEditInfo(int n) { + if (n == 0) + return new EditInfo("Delay (s)", delay, 0, 0); + if (n == 1) + return new EditInfo("Threshold (V)", threshold, 0, 0); + if (n == 2) + return new EditInfo("High Logic Voltage", highVoltage, 0, 0); + return null; + } + + public void setEditValue(int n, EditInfo ei) { + if (n == 0) + delay = ei.value; + if (n == 1) + threshold = ei.value; + if (n == 2) + highVoltage = ei.value; + } + + // there is no current path through the inverter input, but there + // is an indirect path through the output to ground. + boolean getConnection(int n1, int n2) { + return false; + } + + boolean hasGroundConnection(int n1) { + return (n1 == 1); + } + + @Override + double getCurrentIntoNode(int n) { + if (n == 1) + return current; + return 0; + } + +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/DiacElm.java b/src/main/java/com/lushprojects/circuitjs1/client/DiacElm.java index 2ff52cb..85d0299 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/DiacElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/DiacElm.java @@ -21,142 +21,159 @@ import com.lushprojects.circuitjs1.client.util.Locale; -class DiacElm extends CircuitElm { +public class DiacElm extends CircuitElm { // resistor from 0 to 2, 3 // diodes from 2, 3 to 1 double onresistance, offresistance, breakdown, holdcurrent; boolean state; Diode diode1, diode2; - + public DiacElm(int xx, int yy) { - super(xx, yy); - offresistance = 1e8; - onresistance = 500; - breakdown = 30; - holdcurrent = .01; - state = false; - createDiodes(); + super(xx, yy); + offresistance = 1e8; + onresistance = 500; + breakdown = 30; + holdcurrent = .01; + state = false; + createDiodes(); } + public DiacElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f); - onresistance = new Double(st.nextToken()).doubleValue(); - offresistance = new Double(st.nextToken()).doubleValue(); - breakdown = new Double(st.nextToken()).doubleValue(); - holdcurrent = new Double(st.nextToken()).doubleValue(); - createDiodes(); + StringTokenizer st) { + super(xa, ya, xb, yb, f); + onresistance = new Double(st.nextToken()).doubleValue(); + offresistance = new Double(st.nextToken()).doubleValue(); + breakdown = new Double(st.nextToken()).doubleValue(); + holdcurrent = new Double(st.nextToken()).doubleValue(); + createDiodes(); } - + void createDiodes() { - diode1 = new Diode(sim); - diode2 = new Diode(sim); - diode1.setupForDefaultModel(); - diode2.setupForDefaultModel(); + diode1 = new Diode(sim); + diode2 = new Diode(sim); + diode1.setupForDefaultModel(); + diode2.setupForDefaultModel(); + } + + boolean nonLinear() { + return true; } - boolean nonLinear() {return true;} - int getDumpType() { return 203; } + + int getDumpType() { + return 203; + } + String dump() { - return super.dump() + " " + onresistance + " " + offresistance + " " - + breakdown + " " + holdcurrent; + return super.dump() + " " + onresistance + " " + offresistance + " " + + breakdown + " " + holdcurrent; } - + Polygon arrows[]; Point plate1[], plate2[]; - + void setPoints() { - super.setPoints(); - calcLeads(16); - - plate1 = newPointArray(2); - plate2 = newPointArray(2); - interpPoint2(lead1, lead2, plate1[0], plate1[1], 0, 16); - interpPoint2(lead1, lead2, plate2[0], plate2[1], 1, 16); - - arrows = new Polygon[2]; - - int i; - for (i = 0; i != 2; i++) { - int sgn = -1+i*2; - Point p1 = interpPoint(lead1, lead2, i, 8*sgn); - Point p2 = interpPoint(lead1, lead2, 1-i, 16*sgn); - Point p3 = interpPoint(lead1, lead2, 1-i, 0*sgn); - arrows[i] = createPolygon(p1, p2, p3); - } + super.setPoints(); + calcLeads(16); + + plate1 = newPointArray(2); + plate2 = newPointArray(2); + interpPoint2(lead1, lead2, plate1[0], plate1[1], 0, 16); + interpPoint2(lead1, lead2, plate2[0], plate2[1], 1, 16); + + arrows = new Polygon[2]; + + int i; + for (i = 0; i != 2; i++) { + int sgn = -1 + i * 2; + Point p1 = interpPoint(lead1, lead2, i, 8 * sgn); + Point p2 = interpPoint(lead1, lead2, 1 - i, 16 * sgn); + Point p3 = interpPoint(lead1, lead2, 1 - i, 0 * sgn); + arrows[i] = createPolygon(p1, p2, p3); + } } - + void draw(Graphics g) { - double v1 = volts[0]; - double v2 = volts[1]; - setBbox(point1, point2, 6); - draw2Leads(g); - setVoltageColor(g, v1); - setPowerColor(g, true); - drawThickLine(g, plate1[0], plate1[1]); - setVoltageColor(g, v2); - setPowerColor(g, true); - drawThickLine(g, plate2[0], plate2[1]); - g.fillPolygon(arrows[0]); - setVoltageColor(g, v1); - setPowerColor(g, true); - g.fillPolygon(arrows[1]); - setPowerColor(g, true); - doDots(g); - drawPosts(g); + double v1 = volts[0]; + double v2 = volts[1]; + setBbox(point1, point2, 6); + draw2Leads(g); + setVoltageColor(g, v1); + setPowerColor(g, true); + drawThickLine(g, plate1[0], plate1[1]); + setVoltageColor(g, v2); + setPowerColor(g, true); + drawThickLine(g, plate2[0], plate2[1]); + g.fillPolygon(arrows[0]); + setVoltageColor(g, v1); + setPowerColor(g, true); + g.fillPolygon(arrows[1]); + setPowerColor(g, true); + doDots(g); + drawPosts(g); } - + void calculateCurrent() { - double r = (state) ? onresistance : offresistance; - current = (volts[0]-volts[2])/r + (volts[0]-volts[3])/r; + double r = (state) ? onresistance : offresistance; + current = (volts[0] - volts[2]) / r + (volts[0] - volts[3]) / r; } + void startIteration() { - double vd = volts[0] - volts[1]; - if(Math.abs(current) < holdcurrent) state = false; - if(Math.abs(vd) > breakdown) state = true; + double vd = volts[0] - volts[1]; + if (Math.abs(current) < holdcurrent) state = false; + if (Math.abs(vd) > breakdown) state = true; } + void doStep() { - double r = (state) ? onresistance : offresistance; - sim.stampResistor(nodes[0], nodes[2], r); - sim.stampResistor(nodes[0], nodes[3], r); - diode1.doStep(volts[2]-volts[1]); - diode2.doStep(volts[1]-volts[3]); + double r = (state) ? onresistance : offresistance; + sim.stampResistor(nodes[0], nodes[2], r); + sim.stampResistor(nodes[0], nodes[3], r); + diode1.doStep(volts[2] - volts[1]); + diode2.doStep(volts[1] - volts[3]); } + void stamp() { - sim.stampNonLinear(nodes[0]); - sim.stampNonLinear(nodes[1]); - diode1.stamp(nodes[2], nodes[1]); - diode2.stamp(nodes[1], nodes[3]); + sim.stampNonLinear(nodes[0]); + sim.stampNonLinear(nodes[1]); + diode1.stamp(nodes[2], nodes[1]); + diode2.stamp(nodes[1], nodes[3]); + } + + int getInternalNodeCount() { + return 2; } - int getInternalNodeCount() { return 2; } + void getInfo(String arr[]) { - arr[0] = "DIAC"; - getBasicInfo(arr); - arr[3] = state ? "on" : "off"; - arr[4] = "Ron = " + getUnitText(onresistance, Locale.ohmString); - arr[5] = "Roff = " + getUnitText(offresistance, Locale.ohmString); - arr[6] = "Vbrkdn = " + getUnitText(breakdown, "V"); - arr[7] = "Ihold = " + getUnitText(holdcurrent, "A"); + arr[0] = "DIAC"; + getBasicInfo(arr); + arr[3] = state ? "on" : "off"; + arr[4] = "Ron = " + getUnitText(onresistance, Locale.ohmString); + arr[5] = "Roff = " + getUnitText(offresistance, Locale.ohmString); + arr[6] = "Vbrkdn = " + getUnitText(breakdown, "V"); + arr[7] = "Ihold = " + getUnitText(holdcurrent, "A"); arr[8] = "P = " + getUnitText(getPower(), "W"); } + public EditInfo getEditInfo(int n) { - if (n == 0) - return new EditInfo("On resistance (ohms)", onresistance, 0, 0); - if (n == 1) - return new EditInfo("Off resistance (ohms)", offresistance, 0, 0); - if (n == 2) - return new EditInfo("Breakdown voltage (volts)", breakdown, 0, 0); - if (n == 3) - return new EditInfo("Hold current (amps)", holdcurrent, 0, 0); - return null; + if (n == 0) + return new EditInfo("On resistance (ohms)", onresistance, 0, 0); + if (n == 1) + return new EditInfo("Off resistance (ohms)", offresistance, 0, 0); + if (n == 2) + return new EditInfo("Breakdown voltage (volts)", breakdown, 0, 0); + if (n == 3) + return new EditInfo("Hold current (amps)", holdcurrent, 0, 0); + return null; } + public void setEditValue(int n, EditInfo ei) { - if (ei.value > 0 && n == 0) - onresistance = ei.value; - if (ei.value > 0 && n == 1) - offresistance = ei.value; - if (ei.value > 0 && n == 2) - breakdown = ei.value; - if (ei.value > 0 && n == 3) - holdcurrent = ei.value; + if (ei.value > 0 && n == 0) + onresistance = ei.value; + if (ei.value > 0 && n == 1) + offresistance = ei.value; + if (ei.value > 0 && n == 2) + breakdown = ei.value; + if (ei.value > 0 && n == 3) + holdcurrent = ei.value; } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/Dialog.java b/src/main/java/com/lushprojects/circuitjs1/client/Dialog.java index 98d7781..5d3bc2e 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/Dialog.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/Dialog.java @@ -22,30 +22,29 @@ import com.google.gwt.user.client.ui.DialogBox; -class Dialog extends DialogBox { - - boolean closeOnEnter; - - Dialog() { - closeOnEnter = true; - } - - public void closeDialog() - { - hide(); - if (CirSim.dialogShowing == this) - CirSim.dialogShowing = null; - } - - - public void enterPressed() { - if (closeOnEnter) { - apply(); - closeDialog(); - } - } - - void apply() { - } +public class Dialog extends DialogBox { + + boolean closeOnEnter; + + Dialog() { + closeOnEnter = true; + } + + public void closeDialog() { + hide(); + if (CirSim.dialogShowing == this) + CirSim.dialogShowing = null; + } + + + public void enterPressed() { + if (closeOnEnter) { + apply(); + closeDialog(); + } + } + + void apply() { + } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/Diode.java b/src/main/java/com/lushprojects/circuitjs1/client/Diode.java index be1bacb..ba03df4 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/Diode.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/Diode.java @@ -20,45 +20,46 @@ package com.lushprojects.circuitjs1.client; // diode that can be embedded in other elements. series resistance is handled in DiodeElm, not here. -class Diode { +public class Diode { int nodes[]; CirSim sim; - + Diode(CirSim s) { - sim = s; - nodes = new int[2]; + sim = s; + nodes = new int[2]; } + void setup(DiodeModel model) { - leakage = model.saturationCurrent; - zvoltage = model.breakdownVoltage; - vscale = model.vscale; - vdcoef = model.vdcoef; - + leakage = model.saturationCurrent; + zvoltage = model.breakdownVoltage; + vscale = model.vscale; + vdcoef = model.vdcoef; + // sim.console("setup " + leakage + " " + zvoltage + " " + model.emissionCoefficient + " " + vdcoef); - // critical voltage for limiting; current is vscale/sqrt(2) at - // this voltage - vcrit = vscale * Math.log(vscale/(Math.sqrt(2)*leakage)); - // translated, *positive* critical voltage for limiting in Zener breakdown region; - // limitstep() uses this with translated voltages in an analogous fashion to vcrit. - vzcrit = vt * Math.log(vt/(Math.sqrt(2)*leakage)); - if (zvoltage == 0) - zoffset = 0; - else { - // calculate offset which will give us 5mA at zvoltage - double i = -.005; - zoffset = zvoltage-Math.log(-(1+i/leakage))/vzcoef; - } + // critical voltage for limiting; current is vscale/sqrt(2) at + // this voltage + vcrit = vscale * Math.log(vscale / (Math.sqrt(2) * leakage)); + // translated, *positive* critical voltage for limiting in Zener breakdown region; + // limitstep() uses this with translated voltages in an analogous fashion to vcrit. + vzcrit = vt * Math.log(vt / (Math.sqrt(2) * leakage)); + if (zvoltage == 0) + zoffset = 0; + else { + // calculate offset which will give us 5mA at zvoltage + double i = -.005; + zoffset = zvoltage - Math.log(-(1 + i / leakage)) / vzcoef; + } } - + void setupForDefaultModel() { - setup(DiodeModel.getDefaultModel()); + setup(DiodeModel.getDefaultModel()); } - + void reset() { - lastvoltdiff = 0; + lastvoltdiff = 0; } - + // Electron thermal voltage at SPICE's default temperature of 27 C (300.15 K): static final double vt = 0.025865; // The diode's "scale voltage", the voltage increase which will raise current by a factor of e. @@ -79,125 +80,125 @@ void reset() { // Critical voltages for limiting the normal diode and Zener breakdown exponentials. double vcrit, vzcrit; double lastvoltdiff; - + double limitStep(double vnew, double vold) { - double arg; - double oo = vnew; - - // check new voltage; has current changed by factor of e^2? - if (vnew > vcrit && Math.abs(vnew - vold) > (vscale + vscale)) { - if(vold > 0) { - arg = 1 + (vnew - vold) / vscale; - if(arg > 0) { - // adjust vnew so that the current is the same - // as in linearized model from previous iteration. - // current at vnew = old current * arg - vnew = vold + vscale * Math.log(arg); - } else { - vnew = vcrit; - } - } else { - // adjust vnew so that the current is the same - // as in linearized model from previous iteration. - // (1/vscale = slope of load line) - vnew = vscale *Math.log(vnew/vscale); - } - sim.converged = false; - //System.out.println(vnew + " " + oo + " " + vold); - } else if (vnew < 0 && zoffset != 0) { - // for Zener breakdown, use the same logic but translate the values, - // and replace the normal values with the Zener-specific ones to - // account for the steeper exponential of our Zener breakdown curve. - vnew = -vnew - zoffset; - vold = -vold - zoffset; - - if (vnew > vzcrit && Math.abs(vnew - vold) > (vt + vt)) { - if(vold > 0) { - arg = 1 + (vnew - vold) / vt; - if(arg > 0) { - vnew = vold + vt * Math.log(arg); - //System.out.println(oo + " " + vnew); - } else { - vnew = vzcrit; - } - } else { - vnew = vt *Math.log(vnew/vt); - } - sim.converged = false; - } - vnew = -(vnew+zoffset); - } - return vnew; + double arg; + double oo = vnew; + + // check new voltage; has current changed by factor of e^2? + if (vnew > vcrit && Math.abs(vnew - vold) > (vscale + vscale)) { + if (vold > 0) { + arg = 1 + (vnew - vold) / vscale; + if (arg > 0) { + // adjust vnew so that the current is the same + // as in linearized model from previous iteration. + // current at vnew = old current * arg + vnew = vold + vscale * Math.log(arg); + } else { + vnew = vcrit; + } + } else { + // adjust vnew so that the current is the same + // as in linearized model from previous iteration. + // (1/vscale = slope of load line) + vnew = vscale * Math.log(vnew / vscale); + } + sim.converged = false; + //System.out.println(vnew + " " + oo + " " + vold); + } else if (vnew < 0 && zoffset != 0) { + // for Zener breakdown, use the same logic but translate the values, + // and replace the normal values with the Zener-specific ones to + // account for the steeper exponential of our Zener breakdown curve. + vnew = -vnew - zoffset; + vold = -vold - zoffset; + + if (vnew > vzcrit && Math.abs(vnew - vold) > (vt + vt)) { + if (vold > 0) { + arg = 1 + (vnew - vold) / vt; + if (arg > 0) { + vnew = vold + vt * Math.log(arg); + //System.out.println(oo + " " + vnew); + } else { + vnew = vzcrit; + } + } else { + vnew = vt * Math.log(vnew / vt); + } + sim.converged = false; + } + vnew = -(vnew + zoffset); + } + return vnew; } - + void stamp(int n0, int n1) { - nodes[0] = n0; - nodes[1] = n1; - sim.stampNonLinear(nodes[0]); - sim.stampNonLinear(nodes[1]); + nodes[0] = n0; + nodes[1] = n1; + sim.stampNonLinear(nodes[0]); + sim.stampNonLinear(nodes[1]); } - + void doStep(double voltdiff) { - // used to have .1 here, but needed .01 for peak detector - if (Math.abs(voltdiff-lastvoltdiff) > .01) - sim.converged = false; - voltdiff = limitStep(voltdiff, lastvoltdiff); - lastvoltdiff = voltdiff; - - // To prevent a possible singular matrix or other numeric issues, put a tiny conductance - // in parallel with each P-N junction. - double gmin = leakage * 0.01; - if (sim.subIterations > 100) { - // if we have trouble converging, put a conductance in parallel with the diode. - // Gradually increase the conductance value for each iteration. - gmin = Math.exp(-9*Math.log(10)*(1-sim.subIterations/3000.)); - if (gmin > .1) - gmin = .1; - } - - if (voltdiff >= 0 || zvoltage == 0) { - // regular diode or forward-biased zener - double eval = Math.exp(voltdiff*vdcoef); - double geq = vdcoef*leakage*eval + gmin; - double nc = (eval-1)*leakage - geq*voltdiff; - sim.stampConductance(nodes[0], nodes[1], geq); - sim.stampCurrentSource(nodes[0], nodes[1], nc); - } else { - // Zener diode - - // For reverse-biased Zener diodes, mimic the Zener breakdown curve with an - // exponential similar to the ideal Shockley curve. (The real breakdown curve - // isn't a simple exponential, but this approximation should be OK.) - - /* - * I(Vd) = Is * (exp[Vd*C] - exp[(-Vd-Vz)*Cz] - 1 ) - * - * geq is I'(Vd) - * nc is I(Vd) + I'(Vd)*(-Vd) - */ - - double geq = leakage* ( - vdcoef*Math.exp(voltdiff*vdcoef) + vzcoef*Math.exp((-voltdiff-zoffset)*vzcoef) - ) + gmin; - - double nc = leakage* ( - Math.exp(voltdiff*vdcoef) - - Math.exp((-voltdiff-zoffset)*vzcoef) - - 1 - ) + geq*(-voltdiff); - - sim.stampConductance(nodes[0], nodes[1], geq); - sim.stampCurrentSource(nodes[0], nodes[1], nc); - } + // used to have .1 here, but needed .01 for peak detector + if (Math.abs(voltdiff - lastvoltdiff) > .01) + sim.converged = false; + voltdiff = limitStep(voltdiff, lastvoltdiff); + lastvoltdiff = voltdiff; + + // To prevent a possible singular matrix or other numeric issues, put a tiny conductance + // in parallel with each P-N junction. + double gmin = leakage * 0.01; + if (sim.subIterations > 100) { + // if we have trouble converging, put a conductance in parallel with the diode. + // Gradually increase the conductance value for each iteration. + gmin = Math.exp(-9 * Math.log(10) * (1 - sim.subIterations / 3000.)); + if (gmin > .1) + gmin = .1; + } + + if (voltdiff >= 0 || zvoltage == 0) { + // regular diode or forward-biased zener + double eval = Math.exp(voltdiff * vdcoef); + double geq = vdcoef * leakage * eval + gmin; + double nc = (eval - 1) * leakage - geq * voltdiff; + sim.stampConductance(nodes[0], nodes[1], geq); + sim.stampCurrentSource(nodes[0], nodes[1], nc); + } else { + // Zener diode + + // For reverse-biased Zener diodes, mimic the Zener breakdown curve with an + // exponential similar to the ideal Shockley curve. (The real breakdown curve + // isn't a simple exponential, but this approximation should be OK.) + + /* + * I(Vd) = Is * (exp[Vd*C] - exp[(-Vd-Vz)*Cz] - 1 ) + * + * geq is I'(Vd) + * nc is I(Vd) + I'(Vd)*(-Vd) + */ + + double geq = leakage * ( + vdcoef * Math.exp(voltdiff * vdcoef) + vzcoef * Math.exp((-voltdiff - zoffset) * vzcoef) + ) + gmin; + + double nc = leakage * ( + Math.exp(voltdiff * vdcoef) + - Math.exp((-voltdiff - zoffset) * vzcoef) + - 1 + ) + geq * (-voltdiff); + + sim.stampConductance(nodes[0], nodes[1], geq); + sim.stampCurrentSource(nodes[0], nodes[1], nc); + } } - + double calculateCurrent(double voltdiff) { - if (voltdiff >= 0 || zvoltage == 0) - return leakage*(Math.exp(voltdiff*vdcoef)-1); - return leakage* ( - Math.exp(voltdiff*vdcoef) - - Math.exp((-voltdiff-zoffset)*vzcoef) - - 1 - ); + if (voltdiff >= 0 || zvoltage == 0) + return leakage * (Math.exp(voltdiff * vdcoef) - 1); + return leakage * ( + Math.exp(voltdiff * vdcoef) + - Math.exp((-voltdiff - zoffset) * vzcoef) + - 1 + ); } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/DiodeElm.java b/src/main/java/com/lushprojects/circuitjs1/client/DiodeElm.java index 18f1d72..b52f396 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/DiodeElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/DiodeElm.java @@ -25,7 +25,7 @@ import com.google.gwt.user.client.ui.Button; import com.lushprojects.circuitjs1.client.util.Locale; -class DiodeElm extends CircuitElm { +public class DiodeElm extends CircuitElm { Diode diode; static final int FLAG_FWDROP = 1; static final int FLAG_MODEL = 2; @@ -34,160 +34,171 @@ class DiodeElm extends CircuitElm { static String lastModelName = "default"; boolean hasResistance; int diodeEndNode; - + public DiodeElm(int xx, int yy) { - super(xx, yy); - modelName = lastModelName; - diode = new Diode(sim); - setup(); + super(xx, yy); + modelName = lastModelName; + diode = new Diode(sim); + setup(); } - + public DiodeElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f); - final double defaultdrop = .805904783; - diode = new Diode(sim); - double fwdrop = defaultdrop; - double zvoltage = 0; - if ((f & FLAG_MODEL) != 0) { - modelName = CustomLogicModel.unescape(st.nextToken()); - } else { - if ((f & FLAG_FWDROP) > 0) { - try { - fwdrop = new Double(st.nextToken()).doubleValue(); - } catch (Exception e) { - } - } - model = DiodeModel.getModelWithParameters(fwdrop, zvoltage); - modelName = model.name; + StringTokenizer st) { + super(xa, ya, xb, yb, f); + final double defaultdrop = .805904783; + diode = new Diode(sim); + double fwdrop = defaultdrop; + double zvoltage = 0; + if ((f & FLAG_MODEL) != 0) { + modelName = CustomLogicModel.unescape(st.nextToken()); + } else { + if ((f & FLAG_FWDROP) > 0) { + try { + fwdrop = new Double(st.nextToken()).doubleValue(); + } catch (Exception e) { + } + } + model = DiodeModel.getModelWithParameters(fwdrop, zvoltage); + modelName = model.name; // CirSim.console("model name wparams = " + modelName); - } - setup(); + } + setup(); } - boolean nonLinear() { return true; } - + + boolean nonLinear() { + return true; + } + void setup() { // CirSim.console("setting up for model " + modelName + " " + model); model = DiodeModel.getModelWithNameOrCopy(modelName, model); modelName = model.name; // in case we couldn't find that model - diode.setup(model); - hasResistance = (model.seriesResistance > 0); - diodeEndNode = (hasResistance) ? 2 : 1; - allocNodes(); + diode.setup(model); + hasResistance = (model.seriesResistance > 0); + diodeEndNode = (hasResistance) ? 2 : 1; + allocNodes(); } - - int getInternalNodeCount() { return hasResistance ? 1 : 0; } - + + int getInternalNodeCount() { + return hasResistance ? 1 : 0; + } + public void updateModels() { - setup(); + setup(); } - - int getDumpType() { return 'd'; } + + int getDumpType() { + return 'd'; + } + String dump() { - flags |= FLAG_MODEL; + flags |= FLAG_MODEL; /* if (modelName == null) { sim.console("model name is null??"); modelName = "default"; }*/ - return super.dump() + " " + CustomLogicModel.escape(modelName); + return super.dump() + " " + CustomLogicModel.escape(modelName); } - + String dumpModel() { - if (model.builtIn || model.dumped) - return null; - return model.dump(); + if (model.builtIn || model.dumped) + return null; + return model.dump(); } - + final int hs = 8; Polygon poly; Point cathode[]; - + void setPoints() { - super.setPoints(); - calcLeads(16); - cathode = newPointArray(2); - Point pa[] = newPointArray(2); - interpPoint2(lead1, lead2, pa[0], pa[1], 0, hs); - interpPoint2(lead1, lead2, cathode[0], cathode[1], 1, hs); - poly = createPolygon(pa[0], pa[1], lead2); + super.setPoints(); + calcLeads(16); + cathode = newPointArray(2); + Point pa[] = newPointArray(2); + interpPoint2(lead1, lead2, pa[0], pa[1], 0, hs); + interpPoint2(lead1, lead2, cathode[0], cathode[1], 1, hs); + poly = createPolygon(pa[0], pa[1], lead2); } - + void draw(Graphics g) { - drawDiode(g); - doDots(g); - drawPosts(g); + drawDiode(g); + doDots(g); + drawPosts(g); } - + void reset() { - diode.reset(); - volts[0] = volts[1] = curcount = 0; - if (hasResistance) - volts[2] = 0; + diode.reset(); + volts[0] = volts[1] = curcount = 0; + if (hasResistance) + volts[2] = 0; } - + void drawDiode(Graphics g) { - setBbox(point1, point2, hs); + setBbox(point1, point2, hs); - double v1 = volts[0]; - double v2 = volts[1]; + double v1 = volts[0]; + double v2 = volts[1]; - draw2Leads(g); + draw2Leads(g); - // draw arrow thingy - setVoltageColor(g, v1); - setPowerColor(g, true); - g.fillPolygon(poly); + // draw arrow thingy + setVoltageColor(g, v1); + setPowerColor(g, true); + g.fillPolygon(poly); - // draw thing arrow is pointing to - setVoltageColor(g, v2); - setPowerColor(g, true); - drawThickLine(g, cathode[0], cathode[1]); + // draw thing arrow is pointing to + setVoltageColor(g, v2); + setPowerColor(g, true); + drawThickLine(g, cathode[0], cathode[1]); } - + void stamp() { - if (hasResistance) { - // create diode from node 0 to internal node - diode.stamp(nodes[0], nodes[2]); - // create resistor from internal node to node 1 - sim.stampResistor(nodes[1], nodes[2], model.seriesResistance); - } else - // don't need any internal nodes if no series resistance - diode.stamp(nodes[0], nodes[1]); + if (hasResistance) { + // create diode from node 0 to internal node + diode.stamp(nodes[0], nodes[2]); + // create resistor from internal node to node 1 + sim.stampResistor(nodes[1], nodes[2], model.seriesResistance); + } else + // don't need any internal nodes if no series resistance + diode.stamp(nodes[0], nodes[1]); } + void doStep() { - diode.doStep(volts[0]-volts[diodeEndNode]); + diode.doStep(volts[0] - volts[diodeEndNode]); } + void calculateCurrent() { - current = diode.calculateCurrent(volts[0]-volts[diodeEndNode]); + current = diode.calculateCurrent(volts[0] - volts[diodeEndNode]); } + void getInfo(String arr[]) { - if (model.oldStyle) - arr[0] = "diode"; - else - arr[0] = Locale.LS("diode") + " (" + modelName + ")"; - arr[1] = "I = " + getCurrentText(getCurrent()); - arr[2] = "Vd = " + getVoltageText(getVoltageDiff()); - arr[3] = "P = " + getUnitText(getPower(), "W"); - if (model.oldStyle) - arr[4] = "Vf = " + getVoltageText(model.fwdrop); + if (model.oldStyle) + arr[0] = "diode"; + else + arr[0] = Locale.LS("diode") + " (" + modelName + ")"; + arr[1] = "I = " + getCurrentText(getCurrent()); + arr[2] = "Vd = " + getVoltageText(getVoltageDiff()); + arr[3] = "P = " + getUnitText(getPower(), "W"); + if (model.oldStyle) + arr[4] = "Vf = " + getVoltageText(model.fwdrop); } - + Vector models; - + public EditInfo getEditInfo(int n) { - if (n == 0) { - EditInfo ei = new EditInfo("Model", 0, -1, -1); - models = DiodeModel.getModelList(this instanceof ZenerElm); - ei.choice = new Choice(); - int i; - for (i = 0; i != models.size(); i++) { - DiodeModel dm = models.get(i); - ei.choice.add(dm.getDescription()); - if (dm == model) - ei.choice.select(i); - } - return ei; - } + if (n == 0) { + EditInfo ei = new EditInfo("Model", 0, -1, -1); + models = DiodeModel.getModelList(this instanceof ZenerElm); + ei.choice = new Choice(); + int i; + for (i = 0; i != models.size(); i++) { + DiodeModel dm = models.get(i); + ei.choice.add(dm.getDescription()); + if (dm == model) + ei.choice.select(i); + } + return ei; + } if (n == 1) { EditInfo ei = new EditInfo("", 0, -1, -1); ei.button = new Button(Locale.LS("Create New Simple Model")); @@ -200,33 +211,33 @@ public EditInfo getEditInfo(int n) { } if (n == 3) { if (model.readOnly) - return null; + return null; EditInfo ei = new EditInfo("", 0, -1, -1); ei.button = new Button(Locale.LS("Edit Model")); return ei; } return null; } - + public void newModelCreated(DiodeModel dm) { - model = dm; - modelName = model.name; - setup(); + model = dm; + modelName = model.name; + setup(); } - + public void setEditValue(int n, EditInfo ei) { - if (n == 0) { - model = models.get(ei.choice.getSelectedIndex()); - modelName = model.name; - setup(); - ei.newDialog = true; - return; - } + if (n == 0) { + model = models.get(ei.choice.getSelectedIndex()); + modelName = model.name; + setup(); + ei.newDialog = true; + return; + } if (n == 1 || n == 2) { DiodeModel newModel = new DiodeModel(model); newModel.setSimple(n == 1); if (newModel.isSimple()) - newModel.setForwardVoltage(); + newModel.setForwardVoltage(); EditDialog editDialog = new EditDiodeModelDialog(newModel, sim, this); CirSim.diodeModelEditDialog = editDialog; editDialog.show(); @@ -234,24 +245,27 @@ public void setEditValue(int n, EditInfo ei) { } if (n == 3) { if (model.readOnly) { - // probably never reached - Window.alert(Locale.LS("This model cannot be modified. Change the model name to allow customization.")); - return; + // probably never reached + Window.alert(Locale.LS("This model cannot be modified. Change the model name to allow customization.")); + return; } if (model.isSimple()) - model.setForwardVoltage(); + model.setForwardVoltage(); EditDialog editDialog = new EditDiodeModelDialog(model, sim, null); CirSim.diodeModelEditDialog = editDialog; editDialog.show(); return; } } - int getShortcut() { return 'd'; } - + + int getShortcut() { + return 'd'; + } + void setLastModelName(String n) { - lastModelName = n; + lastModelName = n; } - + void stepFinished() { // stop for huge currents that make simulator act weird if (Math.abs(current) > 1e12) diff --git a/src/main/java/com/lushprojects/circuitjs1/client/DiodeModel.java b/src/main/java/com/lushprojects/circuitjs1/client/DiodeModel.java index 15d441f..606ba66 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/DiodeModel.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/DiodeModel.java @@ -11,21 +11,21 @@ public class DiodeModel implements Editable, Comparable { static HashMap modelMap; - + int flags; String name, description; double saturationCurrent, seriesResistance, emissionCoefficient, breakdownVoltage; - + // used for UI code, not guaranteed to be set double forwardVoltage, forwardCurrent; - + boolean dumped; boolean readOnly; boolean builtIn; boolean oldStyle; boolean internal; - static final int FLAGS_SIMPLE = 1; - + static final int FLAGS_SIMPLE = 1; + // Electron thermal voltage at SPICE's default temperature of 27 C (300.15 K): static final double vt = 0.025865; // The diode's "scale voltage", the voltage increase which will raise current by a factor of e. @@ -34,128 +34,128 @@ public class DiodeModel implements Editable, Comparable { double vdcoef; // voltage drop @ 1A double fwdrop; - + protected DiodeModel(double sc, double sr, double ec, double bv, String d) { - saturationCurrent = sc; - seriesResistance = sr; - emissionCoefficient = ec; - breakdownVoltage = bv; - description = d; + saturationCurrent = sc; + seriesResistance = sr; + emissionCoefficient = ec; + breakdownVoltage = bv; + description = d; // CirSim.console("creating diode model " + this); // CirSim.debugger(); - updateModel(); + updateModel(); } - + static DiodeModel getModelWithName(String name) { - createModelMap(); - DiodeModel lm = modelMap.get(name); - if (lm != null) - return lm; - lm = new DiodeModel(); - lm.name = name; - modelMap.put(name, lm); - return lm; + createModelMap(); + DiodeModel lm = modelMap.get(name); + if (lm != null) + return lm; + lm = new DiodeModel(); + lm.name = name; + modelMap.put(name, lm); + return lm; } - + static DiodeModel getModelWithNameOrCopy(String name, DiodeModel oldmodel) { - createModelMap(); - DiodeModel lm = modelMap.get(name); - if (lm != null) - return lm; - if (oldmodel == null) { - CirSim.console("model not found: " + name); - return getDefaultModel(); - } + createModelMap(); + DiodeModel lm = modelMap.get(name); + if (lm != null) + return lm; + if (oldmodel == null) { + CirSim.console("model not found: " + name); + return getDefaultModel(); + } // CirSim.console("copying to " + name); - lm = new DiodeModel(oldmodel); - lm.name = name; - modelMap.put(name, lm); - return lm; + lm = new DiodeModel(oldmodel); + lm.name = name; + modelMap.put(name, lm); + return lm; } - + static void createModelMap() { - if (modelMap != null) - return; - modelMap = new HashMap(); - addDefaultModel("spice-default", new DiodeModel(1e-14, 0, 1, 0, null)); - addDefaultModel("default", new DiodeModel(1.7143528192808883e-7, 0, 2, 0, null)); - addDefaultModel("default-zener", new DiodeModel(1.7143528192808883e-7, 0, 2, 5.6, null)); - - // old default LED with saturation current that is way too small (causes numerical errors) - addDefaultModel("old-default-led", new DiodeModel(2.2349907006671927e-18, 0, 2, 0, null).setInternal()); - - // default for newly created LEDs, https://www.diyaudio.com/forums/software-tools/25884-spice-models-led.html - addDefaultModel("default-led", new DiodeModel(93.2e-12, .042, 3.73, 0, null)); - - // https://www.allaboutcircuits.com/textbook/semiconductors/chpt-3/spice-models/ - addDefaultModel("1N5711", new DiodeModel(315e-9, 2.8, 2.03, 70, "Schottky")); - addDefaultModel("1N5712", new DiodeModel(680e-12, 12, 1.003, 20, "Schottky")); - addDefaultModel("1N34", new DiodeModel(200e-12, 84e-3, 2.19, 60, "germanium")); - addDefaultModel("1N4004", new DiodeModel(18.8e-9, 28.6e-3, 2, 400, "general purpose")); + if (modelMap != null) + return; + modelMap = new HashMap(); + addDefaultModel("spice-default", new DiodeModel(1e-14, 0, 1, 0, null)); + addDefaultModel("default", new DiodeModel(1.7143528192808883e-7, 0, 2, 0, null)); + addDefaultModel("default-zener", new DiodeModel(1.7143528192808883e-7, 0, 2, 5.6, null)); + + // old default LED with saturation current that is way too small (causes numerical errors) + addDefaultModel("old-default-led", new DiodeModel(2.2349907006671927e-18, 0, 2, 0, null).setInternal()); + + // default for newly created LEDs, https://www.diyaudio.com/forums/software-tools/25884-spice-models-led.html + addDefaultModel("default-led", new DiodeModel(93.2e-12, .042, 3.73, 0, null)); + + // https://www.allaboutcircuits.com/textbook/semiconductors/chpt-3/spice-models/ + addDefaultModel("1N5711", new DiodeModel(315e-9, 2.8, 2.03, 70, "Schottky")); + addDefaultModel("1N5712", new DiodeModel(680e-12, 12, 1.003, 20, "Schottky")); + addDefaultModel("1N34", new DiodeModel(200e-12, 84e-3, 2.19, 60, "germanium")); + addDefaultModel("1N4004", new DiodeModel(18.8e-9, 28.6e-3, 2, 400, "general purpose")); // addDefaultModel("1N3891", new DiodeModel(63e-9, 9.6e-3, 2, 0)); // doesn't match datasheet very well - - // http://users.skynet.be/hugocoolens/spice/diodes/1n4148.htm - addDefaultModel("1N4148", new DiodeModel(4.352e-9, .6458, 1.906, 75, "switching")); - addDefaultModel("x2n2646-emitter", new DiodeModel(2.13e-11, 0, 1.8, 0, null).setInternal()); - - // for TL431 - loadInternalModel("~tl431ed-d_ed 0 1e-14 5 1 0 0"); - - // for LM317 - loadInternalModel("~lm317-dz 0 1e-14 0 1 6.3 0"); + + // http://users.skynet.be/hugocoolens/spice/diodes/1n4148.htm + addDefaultModel("1N4148", new DiodeModel(4.352e-9, .6458, 1.906, 75, "switching")); + addDefaultModel("x2n2646-emitter", new DiodeModel(2.13e-11, 0, 1.8, 0, null).setInternal()); + + // for TL431 + loadInternalModel("~tl431ed-d_ed 0 1e-14 5 1 0 0"); + + // for LM317 + loadInternalModel("~lm317-dz 0 1e-14 0 1 6.3 0"); } static void addDefaultModel(String name, DiodeModel dm) { - modelMap.put(name, dm); - dm.readOnly = dm.builtIn = true; - dm.name = name; + modelMap.put(name, dm); + dm.readOnly = dm.builtIn = true; + dm.name = name; } DiodeModel setInternal() { - internal = true; - return this; + internal = true; + return this; } - + // create a new model using given parameters, keeping backward compatibility. The method we use has problems, but we don't want to // change circuit behavior. We don't do this anymore because we discovered that changing the leakage current to get a given fwdrop // does not work well; the leakage currents can be way too high or low. static DiodeModel getModelWithParameters(double fwdrop, double zvoltage) { - createModelMap(); - - final double emcoef = 2; - - // look for existing model with same parameters - Iterator it = modelMap.entrySet().iterator(); - while (it.hasNext()) { - Map.Entry pair = (Map.Entry)it.next(); - DiodeModel dm = pair.getValue(); - if (Math.abs(dm.fwdrop-fwdrop) < 1e-8 && dm.seriesResistance == 0 && Math.abs(dm.breakdownVoltage-zvoltage) < 1e-8 && dm.emissionCoefficient == emcoef) - return dm; - } - - // create a new one, converting to new parameter values - final double vscale = emcoef * vt; - final double vdcoef = 1 / vscale; - double leakage = 1 / (Math.exp(fwdrop * vdcoef) - 1); - String name = "fwdrop=" + fwdrop; - if (zvoltage != 0) - name = name + " zvoltage=" + zvoltage; - DiodeModel dm = getModelWithName(name); + createModelMap(); + + final double emcoef = 2; + + // look for existing model with same parameters + Iterator it = modelMap.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry pair = (Map.Entry) it.next(); + DiodeModel dm = pair.getValue(); + if (Math.abs(dm.fwdrop - fwdrop) < 1e-8 && dm.seriesResistance == 0 && Math.abs(dm.breakdownVoltage - zvoltage) < 1e-8 && dm.emissionCoefficient == emcoef) + return dm; + } + + // create a new one, converting to new parameter values + final double vscale = emcoef * vt; + final double vdcoef = 1 / vscale; + double leakage = 1 / (Math.exp(fwdrop * vdcoef) - 1); + String name = "fwdrop=" + fwdrop; + if (zvoltage != 0) + name = name + " zvoltage=" + zvoltage; + DiodeModel dm = getModelWithName(name); // CirSim.console("got model with name " + name); - dm.saturationCurrent = leakage; - dm.emissionCoefficient = emcoef; - dm.breakdownVoltage = zvoltage; - dm.readOnly = dm.oldStyle = true; + dm.saturationCurrent = leakage; + dm.emissionCoefficient = emcoef; + dm.breakdownVoltage = zvoltage; + dm.readOnly = dm.oldStyle = true; // CirSim.console("at drop current is " + (leakage*(Math.exp(fwdrop*vdcoef)-1))); // CirSim.console("sat " + leakage + " em " + emcoef); - dm.updateModel(); - return dm; + dm.updateModel(); + return dm; } - + static DiodeModel getDefaultModel() { - return getModelWithName("default"); + return getModelWithName("default"); } - + static void loadInternalModel(String s) { StringTokenizer st = new StringTokenizer(s); DiodeModel dm = undumpModel(st); @@ -163,178 +163,179 @@ static void loadInternalModel(String s) { } static void clearDumpedFlags() { - if (modelMap == null) - return; - Iterator it = modelMap.entrySet().iterator(); - while (it.hasNext()) { - Map.Entry pair = (Map.Entry)it.next(); - pair.getValue().dumped = false; - } + if (modelMap == null) + return; + Iterator it = modelMap.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry pair = (Map.Entry) it.next(); + pair.getValue().dumped = false; + } } - + static Vector getModelList(boolean zener) { - Vector vector = new Vector(); - Iterator it = modelMap.entrySet().iterator(); - while (it.hasNext()) { - Map.Entry pair = (Map.Entry)it.next(); - DiodeModel dm = pair.getValue(); - if (dm.internal) - continue; - if (zener && dm.breakdownVoltage == 0) - continue; - if (!vector.contains(dm)) - vector.add(dm); - } - Collections.sort(vector); - return vector; + Vector vector = new Vector(); + Iterator it = modelMap.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry pair = (Map.Entry) it.next(); + DiodeModel dm = pair.getValue(); + if (dm.internal) + continue; + if (zener && dm.breakdownVoltage == 0) + continue; + if (!vector.contains(dm)) + vector.add(dm); + } + Collections.sort(vector); + return vector; } public int compareTo(DiodeModel dm) { - return name.compareTo(dm.name); + return name.compareTo(dm.name); } - + String getDescription() { - if (description == null) - return name; - return name + " (" + Locale.LS(description) + ")"; + if (description == null) + return name; + return name + " (" + Locale.LS(description) + ")"; } - + DiodeModel() { - saturationCurrent = 1e-14; - seriesResistance = 0; - emissionCoefficient = 1; - breakdownVoltage = 0; - updateModel(); + saturationCurrent = 1e-14; + seriesResistance = 0; + emissionCoefficient = 1; + breakdownVoltage = 0; + updateModel(); } - + DiodeModel(DiodeModel copy) { - flags = copy.flags; - saturationCurrent = copy.saturationCurrent; - seriesResistance = copy.seriesResistance; - emissionCoefficient = copy.emissionCoefficient; - breakdownVoltage = copy.breakdownVoltage; - forwardCurrent = copy.forwardCurrent; - updateModel(); + flags = copy.flags; + saturationCurrent = copy.saturationCurrent; + seriesResistance = copy.seriesResistance; + emissionCoefficient = copy.emissionCoefficient; + breakdownVoltage = copy.breakdownVoltage; + forwardCurrent = copy.forwardCurrent; + updateModel(); } static DiodeModel undumpModel(StringTokenizer st) { - String name = CustomLogicModel.unescape(st.nextToken()); - DiodeModel dm = DiodeModel.getModelWithName(name); - dm.undump(st); - return dm; + String name = CustomLogicModel.unescape(st.nextToken()); + DiodeModel dm = DiodeModel.getModelWithName(name); + dm.undump(st); + return dm; } - + void undump(StringTokenizer st) { - flags = new Integer(st.nextToken()).intValue(); - saturationCurrent = Double.parseDouble(st.nextToken()); - seriesResistance = Double.parseDouble(st.nextToken()); - emissionCoefficient = Double.parseDouble(st.nextToken()); - breakdownVoltage = Double.parseDouble(st.nextToken()); - try { - forwardCurrent = Double.parseDouble(st.nextToken()); - } catch (Exception e) {} - updateModel(); + flags = new Integer(st.nextToken()).intValue(); + saturationCurrent = Double.parseDouble(st.nextToken()); + seriesResistance = Double.parseDouble(st.nextToken()); + emissionCoefficient = Double.parseDouble(st.nextToken()); + breakdownVoltage = Double.parseDouble(st.nextToken()); + try { + forwardCurrent = Double.parseDouble(st.nextToken()); + } catch (Exception e) { + } + updateModel(); } - + public EditInfo getEditInfo(int n) { - if (n == 0) { - EditInfo ei = new EditInfo("Model Name", 0); - ei.text = name == null ? "" : name; - return ei; - } - if (n == 1) - return new EditInfo("Saturation Current", saturationCurrent, -1, -1); - if (isSimple()) { - if (n == 2) - return new EditInfo("Forward Voltage", forwardVoltage, -1, -1); - if (n == 3) - return new EditInfo("Current At Above Voltage (A)", forwardCurrent, -1, -1); - } else { - if (n == 2) - return new EditInfo("Series Resistance", seriesResistance, -1, -1); - if (n == 3) - return new EditInfo(EditInfo.makeLink("diodecalc.html", "Emission Coefficient"), emissionCoefficient, -1, -1); - } - if (n == 4) - return new EditInfo("Breakdown Voltage", breakdownVoltage, -1, -1); - return null; + if (n == 0) { + EditInfo ei = new EditInfo("Model Name", 0); + ei.text = name == null ? "" : name; + return ei; + } + if (n == 1) + return new EditInfo("Saturation Current", saturationCurrent, -1, -1); + if (isSimple()) { + if (n == 2) + return new EditInfo("Forward Voltage", forwardVoltage, -1, -1); + if (n == 3) + return new EditInfo("Current At Above Voltage (A)", forwardCurrent, -1, -1); + } else { + if (n == 2) + return new EditInfo("Series Resistance", seriesResistance, -1, -1); + if (n == 3) + return new EditInfo(EditInfo.makeLink("diodecalc.html", "Emission Coefficient"), emissionCoefficient, -1, -1); + } + if (n == 4) + return new EditInfo("Breakdown Voltage", breakdownVoltage, -1, -1); + return null; } public void setEditValue(int n, EditInfo ei) { - if (n == 0) { - name = ei.textf.getText(); - if (name.length() > 0) - modelMap.put(name, this); - } - if (n == 1) - saturationCurrent = ei.value; - if (isSimple()) { - if (n == 2) - forwardVoltage = ei.value; - if (n == 3) - forwardCurrent = ei.value; - setEmissionCoefficient(); - } else { - if (n == 2) - seriesResistance = ei.value; - if (n == 3) - emissionCoefficient = ei.value; - } - if (n == 4) - breakdownVoltage = Math.abs(ei.value); - updateModel(); - CirSim.theSim.updateModels(); + if (n == 0) { + name = ei.textf.getText(); + if (name.length() > 0) + modelMap.put(name, this); + } + if (n == 1) + saturationCurrent = ei.value; + if (isSimple()) { + if (n == 2) + forwardVoltage = ei.value; + if (n == 3) + forwardCurrent = ei.value; + setEmissionCoefficient(); + } else { + if (n == 2) + seriesResistance = ei.value; + if (n == 3) + emissionCoefficient = ei.value; + } + if (n == 4) + breakdownVoltage = Math.abs(ei.value); + updateModel(); + CirSim.theSim.updateModels(); } // set emission coefficient for simple mode if we have enough data void setEmissionCoefficient() { - if (forwardCurrent > 0 && forwardVoltage > 0) - emissionCoefficient = (forwardVoltage/Math.log(forwardCurrent/saturationCurrent+1)) / vt; + if (forwardCurrent > 0 && forwardVoltage > 0) + emissionCoefficient = (forwardVoltage / Math.log(forwardCurrent / saturationCurrent + 1)) / vt; - seriesResistance = 0; + seriesResistance = 0; } - + public void setForwardVoltage() { - if (forwardCurrent == 0) - forwardCurrent = 1; - forwardVoltage = emissionCoefficient*vt * Math.log(forwardCurrent/saturationCurrent+1); + if (forwardCurrent == 0) + forwardCurrent = 1; + forwardVoltage = emissionCoefficient * vt * Math.log(forwardCurrent / saturationCurrent + 1); } - + void updateModel() { - vscale = emissionCoefficient * vt; - vdcoef = 1/vscale; - fwdrop = Math.log(1/saturationCurrent + 1) * emissionCoefficient * vt; + vscale = emissionCoefficient * vt; + vdcoef = 1 / vscale; + fwdrop = Math.log(1 / saturationCurrent + 1) * emissionCoefficient * vt; } - + String dump() { - dumped = true; - return "34 " + CustomLogicModel.escape(name) + " " + flags + " " + saturationCurrent + " " + seriesResistance + " " + emissionCoefficient + " " + breakdownVoltage + " " + forwardCurrent; + dumped = true; + return "34 " + CustomLogicModel.escape(name) + " " + flags + " " + saturationCurrent + " " + seriesResistance + " " + emissionCoefficient + " " + breakdownVoltage + " " + forwardCurrent; } - + boolean isSimple() { - return (flags & FLAGS_SIMPLE) != 0; + return (flags & FLAGS_SIMPLE) != 0; } - + void setSimple(boolean s) { - flags = (s) ? FLAGS_SIMPLE : 0; + flags = (s) ? FLAGS_SIMPLE : 0; } - + void pickName() { - if (breakdownVoltage > 0 && breakdownVoltage < 20) - name = "zener-" + CircuitElm.showFormat.format(breakdownVoltage); - else if (isSimple()) - name = "fwdrop=" + CircuitElm.showFormat.format(forwardVoltage); - else - name = "diodemodel"; - if (modelMap.get(name) != null) { - int num = 2; - for (; ; num++) { - String n = name + "-" + num; - if (modelMap.get(n) == null) { - name = n; - break; - } - } - } + if (breakdownVoltage > 0 && breakdownVoltage < 20) + name = "zener-" + CircuitElm.showFormat.format(breakdownVoltage); + else if (isSimple()) + name = "fwdrop=" + CircuitElm.showFormat.format(forwardVoltage); + else + name = "diodemodel"; + if (modelMap.get(name) != null) { + int num = 2; + for (; ; num++) { + String n = name + "-" + num; + if (modelMap.get(n) == null) { + name = n; + break; + } + } + } } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/EditCompositeModelDialog.java b/src/main/java/com/lushprojects/circuitjs1/client/EditCompositeModelDialog.java index 8982411..5d2eb4a 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/EditCompositeModelDialog.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/EditCompositeModelDialog.java @@ -23,6 +23,7 @@ import java.util.Comparator; import java.util.Date; import java.util.HashSet; + import com.google.gwt.user.client.ui.HasHorizontalAlignment; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.Window; @@ -51,309 +52,305 @@ import com.google.gwt.user.client.ui.TextBox; public class EditCompositeModelDialog extends Dialog implements MouseDownHandler, MouseMoveHandler, MouseUpHandler, MouseOutHandler, MouseOverHandler { - - VerticalPanel vp; - boolean error; - CustomCompositeChipElm chip; - int postCount; - Context2d context; - CustomCompositeModel model; - - void setModel(CustomCompositeModel m) { model = m; } - - boolean createModel() { - HashSet nodeSet = new HashSet(); - model = CirSim.theSim.getCircuitAsComposite(); - if (model == null) - return false; - if (model.extList.size() == 0) { - Window.alert(Locale.LS("Device has no external inputs/outputs!")); - return false; + + VerticalPanel vp; + boolean error; + CustomCompositeChipElm chip; + int postCount; + Context2d context; + CustomCompositeModel model; + + void setModel(CustomCompositeModel m) { + model = m; + } + + boolean createModel() { + HashSet nodeSet = new HashSet(); + model = CirSim.theSim.getCircuitAsComposite(); + if (model == null) return false; + if (model.extList.size() == 0) { + Window.alert(Locale.LS("Device has no external inputs/outputs!")); + return false; + } + Collections.sort(model.extList, new Comparator() { + public int compare(ExtListEntry a, ExtListEntry b) { + return a.name.toLowerCase().compareTo(b.name.toLowerCase()); } - Collections.sort(model.extList, new Comparator() { - public int compare(ExtListEntry a, ExtListEntry b) { - return a.name.toLowerCase().compareTo(b.name.toLowerCase()); - } - }); - int i; - int postCount = model.extList.size(); - int sideCounts[] = new int[] { 0, 0, 0, 0 }; - for (i = 0; i != postCount; i++) { - ExtListEntry pin = model.extList.get(i); - sideCounts[pin.side] += 1; - - if (nodeSet.contains(pin.node)) { - Window.alert(Locale.LS("Can't have two input/output nodes connected!")); - return false; - } - nodeSet.add(pin.node); + }); + int i; + int postCount = model.extList.size(); + int sideCounts[] = new int[]{0, 0, 0, 0}; + for (i = 0; i != postCount; i++) { + ExtListEntry pin = model.extList.get(i); + sideCounts[pin.side] += 1; + + if (nodeSet.contains(pin.node)) { + Window.alert(Locale.LS("Can't have two input/output nodes connected!")); + return false; } + nodeSet.add(pin.node); + } - int xOffsetLeft = (sideCounts[ChipElm.SIDE_W] > 0) ? 1 : 0; - int xOffsetRight = (sideCounts[ChipElm.SIDE_E] > 0) ? 1 : 0; - for (i = 0; i != postCount; i++) { - ExtListEntry pin = model.extList.get(i); - if (pin.side == ChipElm.SIDE_N || pin.side == ChipElm.SIDE_S) { - pin.pos += xOffsetLeft; + int xOffsetLeft = (sideCounts[ChipElm.SIDE_W] > 0) ? 1 : 0; + int xOffsetRight = (sideCounts[ChipElm.SIDE_E] > 0) ? 1 : 0; + for (i = 0; i != postCount; i++) { + ExtListEntry pin = model.extList.get(i); + if (pin.side == ChipElm.SIDE_N || pin.side == ChipElm.SIDE_S) { + pin.pos += xOffsetLeft; + } + } + + int minHeight = (sideCounts[ChipElm.SIDE_N] > 0 && sideCounts[ChipElm.SIDE_S] > 0) ? 2 : 1; + int minWidth = 2; + int pinsNS = Math.max(sideCounts[ChipElm.SIDE_N], sideCounts[ChipElm.SIDE_S]); + int pinsWE = Math.max(sideCounts[ChipElm.SIDE_W], sideCounts[ChipElm.SIDE_E]); + model.sizeX = Math.max(minWidth, pinsNS + xOffsetLeft + xOffsetRight); + model.sizeY = Math.max(minHeight, pinsWE); + + model.modelCircuit = CirSim.theSim.dumpCircuit(); + return true; + } + + public EditCompositeModelDialog() { + super(); + closeOnEnter = true; + } + + TextBox modelNameTextBox = null; + Checkbox saveCheck = null; + Checkbox labelCheck = null; + + void createDialog() { + Button okButton; + Anchor a; + vp = new VerticalPanel(); + setWidget(vp); + setText(Locale.LS("Edit Subcircuit Pin Layout")); + vp.add(new Label(Locale.LS("Drag the pins to the desired position"))); + Date date = new Date(); + + Canvas canvas = Canvas.createIfSupported(); + canvas.setWidth("400 px"); + canvas.setHeight("400 px"); + canvas.setCoordinateSpaceWidth(400); + canvas.setCoordinateSpaceHeight(400); + vp.add(canvas); + CirSim.doTouchHandlers(null, canvas.getCanvasElement()); + context = canvas.getContext2d(); + + chip = new CustomCompositeChipElm(50, 50); + chip.x2 = 200; + chip.y2 = 50; + selectedPin = -1; + createPinsFromModel(); + + if (model.name == null) { + vp.add(new Label(Locale.LS("Model Name"))); + modelNameTextBox = new TextBox(); + vp.add(modelNameTextBox); + modelNameTextBox.addValueChangeHandler(new ValueChangeHandler() { + @Override + public void onValueChange(ValueChangeEvent event) { + drawChip(); } + }); +// modelNameTextBox.setText(model.name); + } + + HorizontalPanel hp = new HorizontalPanel(); + hp.add(new Label(Locale.LS("Width"))); + Button b; + hp.add(b = new Button("+")); + b.addClickHandler(new ClickHandler() { + public void onClick(ClickEvent event) { + adjustChipSize(1, 0); + } + }); + hp.add(b = new Button("-")); + b.addClickHandler(new ClickHandler() { + public void onClick(ClickEvent event) { + adjustChipSize(-1, 0); + } + }); + hp.add(new Label(Locale.LS("Height"))); + hp.add(b = new Button("+")); + b.addClickHandler(new ClickHandler() { + public void onClick(ClickEvent event) { + adjustChipSize(0, 1); + } + }); + hp.add(b = new Button("-")); + b.addClickHandler(new ClickHandler() { + public void onClick(ClickEvent event) { + adjustChipSize(0, -1); } + }); + vp.add(hp); + hp.addStyleName("topSpace"); + vp.add(labelCheck = new Checkbox(Locale.LS("Show Label"))); + labelCheck.setState(model.showLabel()); + labelCheck.addClickHandler(new ClickHandler() { + public void onClick(ClickEvent event) { + model.setShowLabel(labelCheck.getValue()); + drawChip(); + } + }); + vp.add(saveCheck = new Checkbox(Locale.LS("Save Across Sessions"))); + saveCheck.setState(model.isSaved()); - int minHeight = (sideCounts[ChipElm.SIDE_N] > 0 && sideCounts[ChipElm.SIDE_S] > 0) ? 2 : 1; - int minWidth = 2; - int pinsNS = Math.max(sideCounts[ChipElm.SIDE_N], sideCounts[ChipElm.SIDE_S]); - int pinsWE = Math.max(sideCounts[ChipElm.SIDE_W], sideCounts[ChipElm.SIDE_E]); - model.sizeX = Math.max(minWidth, pinsNS + xOffsetLeft + xOffsetRight); - model.sizeY = Math.max(minHeight, pinsWE); + canvas.addMouseDownHandler(this); + canvas.addMouseUpHandler(this); + canvas.addMouseMoveHandler(this); + canvas.addMouseOutHandler(this); + canvas.addMouseOverHandler(this); - model.modelCircuit = CirSim.theSim.dumpCircuit(); - return true; + hp = new HorizontalPanel(); + hp.setWidth("100%"); + hp.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_LEFT); + hp.setStyleName("topSpace"); + vp.add(hp); + hp.add(okButton = new Button(Locale.LS("OK"))); + hp.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_RIGHT); + Button cancelButton; + if (model.name == null) { + hp.add(cancelButton = new Button(Locale.LS("Cancel"))); + cancelButton.addClickHandler(new ClickHandler() { + public void onClick(ClickEvent event) { + closeDialog(); + } + }); } - - public EditCompositeModelDialog() { - super(); - closeOnEnter = true; - } - - TextBox modelNameTextBox = null; - Checkbox saveCheck = null; - Checkbox labelCheck = null; - - void createDialog() { - Button okButton; - Anchor a; - vp=new VerticalPanel(); - setWidget(vp); - setText(Locale.LS("Edit Subcircuit Pin Layout")); - vp.add(new Label(Locale.LS("Drag the pins to the desired position"))); - Date date = new Date(); - - Canvas canvas = Canvas.createIfSupported(); - canvas.setWidth("400 px"); - canvas.setHeight("400 px"); - canvas.setCoordinateSpaceWidth(400); - canvas.setCoordinateSpaceHeight(400); - vp.add(canvas); - CirSim.doTouchHandlers(null, canvas.getCanvasElement()); - context = canvas.getContext2d(); - - chip = new CustomCompositeChipElm(50, 50); - chip.x2 = 200; - chip.y2 = 50; - selectedPin = -1; - createPinsFromModel(); - - if (model.name == null) { - vp.add(new Label(Locale.LS("Model Name"))); - modelNameTextBox = new TextBox(); - vp.add(modelNameTextBox); - modelNameTextBox.addValueChangeHandler(new ValueChangeHandler() { - @Override - public void onValueChange(ValueChangeEvent event) { - drawChip(); - } - }); -// modelNameTextBox.setText(model.name); - } - - HorizontalPanel hp = new HorizontalPanel(); - hp.add(new Label(Locale.LS("Width"))); - Button b; - hp.add(b = new Button("+")); - b.addClickHandler(new ClickHandler() { - public void onClick(ClickEvent event) { - adjustChipSize(1, 0); - } - }); - hp.add(b = new Button("-")); - b.addClickHandler(new ClickHandler() { - public void onClick(ClickEvent event) { - adjustChipSize(-1, 0); - } - }); - hp.add(new Label(Locale.LS("Height"))); - hp.add(b = new Button("+")); - b.addClickHandler(new ClickHandler() { - public void onClick(ClickEvent event) { - adjustChipSize(0, 1); - } - }); - hp.add(b = new Button("-")); - b.addClickHandler(new ClickHandler() { - public void onClick(ClickEvent event) { - adjustChipSize(0, -1); + okButton.addClickHandler(new ClickHandler() { + public void onClick(ClickEvent event) { + if (modelNameTextBox != null) { + String name = modelNameTextBox.getText(); + if (name.length() == 0) { + Window.alert(Locale.LS("Please enter a model name.")); + return; } - }); - vp.add(hp); - hp.addStyleName("topSpace"); - vp.add(labelCheck = new Checkbox(Locale.LS("Show Label"))); - labelCheck.setState(model.showLabel()); - labelCheck.addClickHandler(new ClickHandler() { - public void onClick(ClickEvent event) { - model.setShowLabel(labelCheck.getValue()); - drawChip(); - } - }); - vp.add(saveCheck = new Checkbox(Locale.LS("Save Across Sessions"))); - saveCheck.setState(model.isSaved()); - - canvas.addMouseDownHandler(this); - canvas.addMouseUpHandler(this); - canvas.addMouseMoveHandler(this); - canvas.addMouseOutHandler(this); - canvas.addMouseOverHandler(this); - - hp = new HorizontalPanel(); - hp.setWidth("100%"); - hp.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_LEFT); - hp.setStyleName("topSpace"); - vp.add(hp); - hp.add(okButton = new Button(Locale.LS("OK"))); - hp.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_RIGHT); - Button cancelButton; - if (model.name == null) { - hp.add(cancelButton = new Button(Locale.LS("Cancel"))); - cancelButton.addClickHandler(new ClickHandler() { - public void onClick(ClickEvent event) { - closeDialog(); - } - }); - } - okButton.addClickHandler(new ClickHandler() { - public void onClick(ClickEvent event) { - if (modelNameTextBox != null) { - String name = modelNameTextBox.getText(); - if (name.length() == 0) { - Window.alert(Locale.LS("Please enter a model name.")); - return; - } - model.setName(CustomCompositeElm.lastModelName = name); - } - model.setSaved(saveCheck.getState()); - CirSim.theSim.updateModels(); - CirSim.theSim.needAnalyze(); // will get singular matrix if we don't do this - closeDialog(); - } - }); - this.center(); - } - - void createPinsFromModel() { - postCount = model.extList.size(); - chip.allocPins(postCount); - chip.sizeX = model.sizeX; - chip.sizeY = model.sizeY; - for (int i = 0; i != postCount; i++) { - ExtListEntry pin = model.extList.get(i); - chip.setPin(i, pin.pos, pin.side, pin.name); - chip.volts[i] = 0; - if (i == selectedPin) - chip.pins[i].selected = true; - } - chip.setPoints(); - } - - double scale; - - void drawChip() { - Graphics g = new Graphics(context); - double scalew = context.getCanvas().getWidth() / (double)(chip.boundingBox.width + chip.boundingBox.x*2); - double scaleh = context.getCanvas().getHeight() / (double)(chip.boundingBox.height + chip.boundingBox.y*2); - scale = 1/Math.min(scalew, scaleh); - context.setFillStyle(CirSim.theSim.getBackgroundColor().getHexValue()); - context.setTransform(1, 0, 0, 1, 0, 0); - context.fillRect(0, 0, context.getCanvas().getWidth(), context.getCanvas().getHeight()); - context.setTransform(1/scale, 0, 0, 1/scale, 0, 0); - chip.setLabel(!labelCheck.getValue() ? null : (modelNameTextBox != null) ? modelNameTextBox.getText() : model.name); - chip.draw(g); - } - - void adjustChipSize(int dx, int dy) { - if (dx < 0 || dy < 0) { - for (int i = 0; i != postCount; i++) { - Pin p = chip.pins[i]; - if (dx < 0 && (p.side == ChipElm.SIDE_N || p.side == ChipElm.SIDE_S) && p.pos >= chip.sizeX+dx) - return; - if (dy < 0 && (p.side == ChipElm.SIDE_E || p.side == ChipElm.SIDE_W) && p.pos >= chip.sizeY+dy) - return; - } - } - if (chip.sizeX + dx < 1 || chip.sizeY + dy < 1) - return; - model.sizeX += dx; - model.sizeY += dy; - createPinsFromModel(); - drawChip(); - } - - boolean dragging; - - public void onMouseOver(MouseOverEvent event) { - // TODO Auto-generated method stub - - } - - public void onMouseOut(MouseOutEvent event) { - // TODO Auto-generated method stub - - } - - public void onMouseUp(MouseUpEvent event) { - dragging = false; - } - - int selectedPin; - - public void onMouseMove(MouseMoveEvent event) { - mouseMoved(event.getX(), event.getY()); - } - - void mouseMoved(int x, int y) { - if (dragging) { - if (selectedPin < 0) - return; - int pos[] = new int[2]; - if (!chip.getPinPos((int)(x*scale), (int)(y*scale), selectedPin, pos)) - return; - ExtListEntry p = model.extList.get(selectedPin); - int pn = chip.getOverlappingPin(pos[0], pos[1], selectedPin); - if (pn != -1) { - // swap positions with overlapping pin - ExtListEntry p2 = model.extList.get(pn); - p2.pos = p.pos; - p2.side = p.side; - } - p.pos = pos[0]; - p.side = pos[1]; - createPinsFromModel(); - drawChip(); - } else { - int i; - double bestdist = 20; - selectedPin = -1; - for (i = 0; i != postCount; i++) { - Pin p = chip.pins[i]; - int dx = (int)(x*scale) - p.textloc.x; - int dy = (int)(y*scale) - p.textloc.y; - double dist = Math.hypot(dx, dy); - if (dist < bestdist) { - bestdist = dist; - selectedPin = i; - } - p.selected = false; - } - if (selectedPin >= 0) - chip.pins[selectedPin].selected = true; - drawChip(); - } - } - - public void onMouseDown(MouseDownEvent event) { - mouseMoved(event.getX(), event.getY()); - dragging = true; - } - - public void show() { - super.show(); - drawChip(); - } + model.setName(CustomCompositeElm.lastModelName = name); + } + model.setSaved(saveCheck.getState()); + CirSim.theSim.updateModels(); + CirSim.theSim.needAnalyze(); // will get singular matrix if we don't do this + closeDialog(); + } + }); + this.center(); + } + + void createPinsFromModel() { + postCount = model.extList.size(); + chip.allocPins(postCount); + chip.sizeX = model.sizeX; + chip.sizeY = model.sizeY; + for (int i = 0; i != postCount; i++) { + ExtListEntry pin = model.extList.get(i); + chip.setPin(i, pin.pos, pin.side, pin.name); + chip.volts[i] = 0; + if (i == selectedPin) chip.pins[i].selected = true; + } + chip.setPoints(); + } + + double scale; + + void drawChip() { + Graphics g = new Graphics(context); + double scalew = context.getCanvas().getWidth() / (double) (chip.boundingBox.width + chip.boundingBox.x * 2); + double scaleh = context.getCanvas().getHeight() / (double) (chip.boundingBox.height + chip.boundingBox.y * 2); + scale = 1 / Math.min(scalew, scaleh); + context.setFillStyle(CirSim.theSim.getBackgroundColor().getHexValue()); + context.setTransform(1, 0, 0, 1, 0, 0); + context.fillRect(0, 0, context.getCanvas().getWidth(), context.getCanvas().getHeight()); + context.setTransform(1 / scale, 0, 0, 1 / scale, 0, 0); + chip.setLabel(!labelCheck.getValue() ? null : (modelNameTextBox != null) ? modelNameTextBox.getText() : model.name); + chip.draw(g); + } + + void adjustChipSize(int dx, int dy) { + if (dx < 0 || dy < 0) { + for (int i = 0; i != postCount; i++) { + Pin p = chip.pins[i]; + if (dx < 0 && (p.side == ChipElm.SIDE_N || p.side == ChipElm.SIDE_S) && p.pos >= chip.sizeX + dx) + return; + if (dy < 0 && (p.side == ChipElm.SIDE_E || p.side == ChipElm.SIDE_W) && p.pos >= chip.sizeY + dy) + return; + } + } + if (chip.sizeX + dx < 1 || chip.sizeY + dy < 1) return; + model.sizeX += dx; + model.sizeY += dy; + createPinsFromModel(); + drawChip(); + } + + boolean dragging; + + public void onMouseOver(MouseOverEvent event) { + // TODO Auto-generated method stub + + } + + public void onMouseOut(MouseOutEvent event) { + // TODO Auto-generated method stub + + } + + public void onMouseUp(MouseUpEvent event) { + dragging = false; + } + + int selectedPin; + + public void onMouseMove(MouseMoveEvent event) { + mouseMoved(event.getX(), event.getY()); + } + + void mouseMoved(int x, int y) { + if (dragging) { + if (selectedPin < 0) return; + int pos[] = new int[2]; + if (!chip.getPinPos((int) (x * scale), (int) (y * scale), selectedPin, pos)) return; + ExtListEntry p = model.extList.get(selectedPin); + int pn = chip.getOverlappingPin(pos[0], pos[1], selectedPin); + if (pn != -1) { + // swap positions with overlapping pin + ExtListEntry p2 = model.extList.get(pn); + p2.pos = p.pos; + p2.side = p.side; + } + p.pos = pos[0]; + p.side = pos[1]; + createPinsFromModel(); + drawChip(); + } else { + int i; + double bestdist = 20; + selectedPin = -1; + for (i = 0; i != postCount; i++) { + Pin p = chip.pins[i]; + int dx = (int) (x * scale) - p.textloc.x; + int dy = (int) (y * scale) - p.textloc.y; + double dist = Math.hypot(dx, dy); + if (dist < bestdist) { + bestdist = dist; + selectedPin = i; + } + p.selected = false; + } + if (selectedPin >= 0) chip.pins[selectedPin].selected = true; + drawChip(); + } + } + + public void onMouseDown(MouseDownEvent event) { + mouseMoved(event.getX(), event.getY()); + dragging = true; + } + + public void show() { + super.show(); + drawChip(); + } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/EditDialog.java b/src/main/java/com/lushprojects/circuitjs1/client/EditDialog.java index 3512960..39c22ac 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/EditDialog.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/EditDialog.java @@ -39,283 +39,305 @@ interface Editable { EditInfo getEditInfo(int n); + void setEditValue(int n, EditInfo ei); } -class EditDialog extends Dialog { - Editable elm; - CirSim cframe; - Button applyButton, okButton, cancelButton; - EditInfo einfos[]; - int einfocount; - final int barmax = 1000; - VerticalPanel vp; - HorizontalPanel hp; - static NumberFormat noCommaFormat = NumberFormat.getFormat("####.##########"); +public class EditDialog extends Dialog { + Editable elm; + CirSim cframe; + Button applyButton, okButton, cancelButton; + EditInfo einfos[]; + int einfocount; + final int barmax = 1000; + VerticalPanel vp; + HorizontalPanel hp; + static NumberFormat noCommaFormat = NumberFormat.getFormat("####.##########"); - EditDialog(Editable ce, CirSim f) { + EditDialog(Editable ce, CirSim f) { // super(f, "Edit Component", false); - super(); // Do we need this? - setText(Locale.LS("Edit Component")); - cframe = f; - elm = ce; + super(); // Do we need this? + setText(Locale.LS("Edit Component")); + cframe = f; + elm = ce; // setLayout(new EditDialogLayout()); - vp=new VerticalPanel(); - setWidget(vp); - einfos = new EditInfo[10]; + vp = new VerticalPanel(); + setWidget(vp); + einfos = new EditInfo[10]; // noCommaFormat = DecimalFormat.getInstance(); // noCommaFormat.setMaximumFractionDigits(10); // noCommaFormat.setGroupingUsed(false); - hp=new HorizontalPanel(); - hp.setWidth("100%"); - hp.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_LEFT); - hp.setStyleName("topSpace"); - vp.add(hp); - applyButton = new Button(Locale.LS("Apply")); - hp.add(applyButton); - applyButton.addClickHandler(new ClickHandler() { - public void onClick(ClickEvent event) { - apply(); - } - }); - hp.add(okButton = new Button(Locale.LS("OK"))); - okButton.addClickHandler(new ClickHandler() { - public void onClick(ClickEvent event) { - apply(); - closeDialog(); - } - }); - hp.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_RIGHT); - hp.add(cancelButton = new Button(Locale.LS("Cancel"))); - cancelButton.addClickHandler(new ClickHandler() { - public void onClick(ClickEvent event) { - closeDialog(); - } - }); - buildDialog(); - this.center(); - } - - void buildDialog() { - int i; - int idx; - for (i = 0; ; i++) { - Label l = null; - einfos[i] = elm.getEditInfo(i); - if (einfos[i] == null) - break; - final EditInfo ei = einfos[i]; - idx = vp.getWidgetIndex(hp); - String name = Locale.LS(ei.name); - if (ei.name.startsWith("<")) - vp.insert(l = new HTML(name),idx); - else - vp.insert(l = new Label(name),idx); - if (i!=0 && l != null) - l.setStyleName("topSpace"); - idx = vp.getWidgetIndex(hp); - if (ei.choice != null) { - vp.insert(ei.choice,idx); - ei.choice.addChangeHandler( new ChangeHandler() { - public void onChange(ChangeEvent e){ - itemStateChanged(e); - } - }); - } else if (ei.checkbox != null) { - vp.insert(ei.checkbox,idx); - ei.checkbox.addValueChangeHandler( new ValueChangeHandler() { - public void onValueChange(ValueChangeEvent e){ - itemStateChanged(e); - } - }); - } else if (ei.button != null) { - vp.insert(ei.button, idx); - if (ei.loadFile != null) { - //Open file dialog - vp.add(ei.loadFile); - ei.button.addClickHandler( new ClickHandler() { - public void onClick(ClickEvent event) { - ei.loadFile.open(); - } - }); - } else { - //Normal button press - ei.button.addClickHandler( new ClickHandler() { - public void onClick(ClickEvent event) { - itemStateChanged(event); - } - }); - } - } else if (ei.textArea != null) { - vp.insert(ei.textArea, idx); - closeOnEnter = false; - } else if (ei.widget != null) { - vp.insert(ei.widget, idx); - } else { - vp.insert(ei.textf = new TextBox(), idx); - if (ei.text != null) { - ei.textf.setText(ei.text); - ei.textf.setVisibleLength(50); - } - if (ei.text == null) { - ei.textf.setText(unitString(ei)); - } - } - } - einfocount = i; - } + hp = new HorizontalPanel(); + hp.setWidth("100%"); + hp.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_LEFT); + hp.setStyleName("topSpace"); + vp.add(hp); + applyButton = new Button(Locale.LS("Apply")); + hp.add(applyButton); + applyButton.addClickHandler(new ClickHandler() { + public void onClick(ClickEvent event) { + apply(); + } + }); + hp.add(okButton = new Button(Locale.LS("OK"))); + okButton.addClickHandler(new ClickHandler() { + public void onClick(ClickEvent event) { + apply(); + closeDialog(); + } + }); + hp.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_RIGHT); + hp.add(cancelButton = new Button(Locale.LS("Cancel"))); + cancelButton.addClickHandler(new ClickHandler() { + public void onClick(ClickEvent event) { + closeDialog(); + } + }); + buildDialog(); + this.center(); + } + + void buildDialog() { + int i; + int idx; + for (i = 0; ; i++) { + Label l = null; + einfos[i] = elm.getEditInfo(i); + if (einfos[i] == null) + break; + final EditInfo ei = einfos[i]; + idx = vp.getWidgetIndex(hp); + String name = Locale.LS(ei.name); + if (ei.name.startsWith("<")) + vp.insert(l = new HTML(name), idx); + else + vp.insert(l = new Label(name), idx); + if (i != 0 && l != null) + l.setStyleName("topSpace"); + idx = vp.getWidgetIndex(hp); + if (ei.choice != null) { + vp.insert(ei.choice, idx); + ei.choice.addChangeHandler(new ChangeHandler() { + public void onChange(ChangeEvent e) { + itemStateChanged(e); + } + }); + } else if (ei.checkbox != null) { + vp.insert(ei.checkbox, idx); + ei.checkbox.addValueChangeHandler(new ValueChangeHandler() { + public void onValueChange(ValueChangeEvent e) { + itemStateChanged(e); + } + }); + } else if (ei.button != null) { + vp.insert(ei.button, idx); + if (ei.loadFile != null) { + //Open file dialog + vp.add(ei.loadFile); + ei.button.addClickHandler(new ClickHandler() { + public void onClick(ClickEvent event) { + ei.loadFile.open(); + } + }); + } else { + //Normal button press + ei.button.addClickHandler(new ClickHandler() { + public void onClick(ClickEvent event) { + itemStateChanged(event); + } + }); + } + } else if (ei.textArea != null) { + vp.insert(ei.textArea, idx); + closeOnEnter = false; + } else if (ei.widget != null) { + vp.insert(ei.widget, idx); + } else { + vp.insert(ei.textf = new TextBox(), idx); + if (ei.text != null) { + ei.textf.setText(ei.text); + ei.textf.setVisibleLength(50); + } + if (ei.text == null) { + ei.textf.setText(unitString(ei)); + } + } + } + einfocount = i; + } + + static final double ROOT2 = 1.41421356237309504880; + + double diffFromInteger(double x) { + return Math.abs(x - Math.round(x)); + } + + String unitString(EditInfo ei) { + // for voltage elements, express values in rms if that would be shorter + if (elm != null && elm instanceof VoltageElm && + Math.abs(ei.value) > 1e-4 && + diffFromInteger(ei.value * 1e4) > diffFromInteger(ei.value * 1e4 / ROOT2)) + return unitString(ei, ei.value / ROOT2) + "rms"; + return unitString(ei, ei.value); + } + + static String unitString(EditInfo ei, double v) { + double va = Math.abs(v); + if (ei != null && ei.dimensionless) + return noCommaFormat.format(v); + if (Double.isInfinite(va)) + return noCommaFormat.format(v); + if (v == 0) return "0"; + if (va < 1e-12) + return noCommaFormat.format(v * 1e15) + "f"; + if (va < 1e-9) + return noCommaFormat.format(v * 1e12) + "p"; + if (va < 1e-6) + return noCommaFormat.format(v * 1e9) + "n"; + if (va < 1e-3) + return noCommaFormat.format(v * 1e6) + "u"; + if (va < 1 /*&& !ei.forceLargeM*/) + return noCommaFormat.format(v * 1e3) + "m"; + if (va < 1e3) + return noCommaFormat.format(v); + if (va < 1e6) + return noCommaFormat.format(v * 1e-3) + "k"; + if (va < 1e9) + return noCommaFormat.format(v * 1e-6) + "M"; + return noCommaFormat.format(v * 1e-9) + "G"; + } + + double parseUnits(EditInfo ei) throws java.text.ParseException { + String s = ei.textf.getText(); + return parseUnits(s); + } + + static double parseUnits(String s) throws java.text.ParseException { + s = s.trim(); + double rmsMult = 1; + if (s.endsWith("rms")) { + s = s.substring(0, s.length() - 3).trim(); + rmsMult = ROOT2; + } + // rewrite shorthand (eg "2k2") in to normal format (eg 2.2k) using regex + s = s.replaceAll("([0-9]+)([pPnNuUmMkKgG])([0-9]+)", "$1.$3$2"); + // rewrite meg to M + s = s.replaceAll("[mM][eE][gG]$", "M"); + int len = s.length(); + char uc = s.charAt(len - 1); + double mult = 1; + switch (uc) { + case 'f': + case 'F': + mult = 1e-15; + break; + case 'p': + case 'P': + mult = 1e-12; + break; + case 'n': + case 'N': + mult = 1e-9; + break; + case 'u': + case 'U': + mult = 1e-6; + break; + + // for ohm values, we used to assume mega for lowercase m, otherwise milli + case 'm': + mult = /*(ei.forceLargeM) ? 1e6 : */ 1e-3; + break; + + case 'k': + case 'K': + mult = 1e3; + break; + case 'M': + mult = 1e6; + break; + case 'G': + case 'g': + mult = 1e9; + break; + } + if (mult != 1) + s = s.substring(0, len - 1).trim(); + return noCommaFormat.parse(s) * mult * rmsMult; + } + + void apply() { + int i; + for (i = 0; i != einfocount; i++) { + EditInfo ei = einfos[i]; + if (ei.textf != null && ei.text == null) { + try { + double d = parseUnits(ei); + ei.value = d; + } catch (Exception ex) { /* ignored */ } + } + if (ei.button != null) + continue; + elm.setEditValue(i, ei); + + // update slider if any + if (elm instanceof CircuitElm) { + Adjustable adj = cframe.findAdjustable((CircuitElm) elm, i); + if (adj != null) + adj.setSliderValue(ei.value); + } + } + cframe.needAnalyze(); + } - static final double ROOT2 = 1.41421356237309504880; - - double diffFromInteger(double x) { - return Math.abs(x-Math.round(x)); - } - - String unitString(EditInfo ei) { - // for voltage elements, express values in rms if that would be shorter - if (elm != null && elm instanceof VoltageElm && - Math.abs(ei.value) > 1e-4 && - diffFromInteger(ei.value*1e4) > diffFromInteger(ei.value*1e4/ROOT2)) - return unitString(ei, ei.value/ROOT2) + "rms"; - return unitString(ei, ei.value); - } + public void itemStateChanged(GwtEvent e) { + Object src = e.getSource(); + int i; + boolean changed = false; + boolean applied = false; + for (i = 0; i != einfocount; i++) { + EditInfo ei = einfos[i]; + if (ei.choice == src || ei.checkbox == src || ei.button == src) { - static String unitString(EditInfo ei, double v) { - double va = Math.abs(v); - if (ei != null && ei.dimensionless) - return noCommaFormat.format(v); - if (Double.isInfinite(va)) - return noCommaFormat.format(v); - if (v == 0) return "0"; - if (va < 1e-12) - return noCommaFormat.format(v*1e15) + "f"; - if (va < 1e-9) - return noCommaFormat.format(v*1e12) + "p"; - if (va < 1e-6) - return noCommaFormat.format(v*1e9) + "n"; - if (va < 1e-3) - return noCommaFormat.format(v*1e6) + "u"; - if (va < 1 /*&& !ei.forceLargeM*/) - return noCommaFormat.format(v*1e3) + "m"; - if (va < 1e3) - return noCommaFormat.format(v); - if (va < 1e6) - return noCommaFormat.format(v*1e-3) + "k"; - if (va < 1e9) - return noCommaFormat.format(v*1e-6) + "M"; - return noCommaFormat.format(v*1e-9) + "G"; - } + // if we're pressing a button, make sure to apply changes first + if (ei.button == src && !ei.newDialog) { + apply(); + applied = true; + } - double parseUnits(EditInfo ei) throws java.text.ParseException { - String s = ei.textf.getText(); - return parseUnits(s); - } - - static double parseUnits(String s) throws java.text.ParseException { - s = s.trim(); - double rmsMult = 1; - if (s.endsWith("rms")) { - s = s.substring(0, s.length()-3).trim(); - rmsMult = ROOT2; - } - // rewrite shorthand (eg "2k2") in to normal format (eg 2.2k) using regex - s=s.replaceAll("([0-9]+)([pPnNuUmMkKgG])([0-9]+)", "$1.$3$2"); - // rewrite meg to M - s=s.replaceAll("[mM][eE][gG]$", "M"); - int len = s.length(); - char uc = s.charAt(len-1); - double mult = 1; - switch (uc) { - case 'f': case 'F': mult = 1e-15; break; - case 'p': case 'P': mult = 1e-12; break; - case 'n': case 'N': mult = 1e-9; break; - case 'u': case 'U': mult = 1e-6; break; + elm.setEditValue(i, ei); + if (ei.newDialog) + changed = true; + cframe.needAnalyze(); + } + } + if (changed) { + // apply changes before we reset everything + // (need to check if we already applied changes; otherwise Diode create simple model button doesn't work) + if (!applied) + apply(); - // for ohm values, we used to assume mega for lowercase m, otherwise milli - case 'm': mult = /*(ei.forceLargeM) ? 1e6 : */ 1e-3; break; + clearDialog(); + buildDialog(); + } + } - case 'k': case 'K': mult = 1e3; break; - case 'M': mult = 1e6; break; - case 'G': case 'g': mult = 1e9; break; - } - if (mult != 1) - s = s.substring(0, len-1).trim(); - return noCommaFormat.parse(s) * mult * rmsMult; - } + public void resetDialog() { + clearDialog(); + buildDialog(); + } - void apply() { - int i; - for (i = 0; i != einfocount; i++) { - EditInfo ei = einfos[i]; - if (ei.textf!=null && ei.text==null) { - try { - double d = parseUnits(ei); - ei.value = d; - } catch (Exception ex) { /* ignored */ } - } - if (ei.button != null) - continue; - elm.setEditValue(i, ei); - - // update slider if any - if (elm instanceof CircuitElm) { - Adjustable adj = cframe.findAdjustable((CircuitElm)elm, i); - if (adj != null) - adj.setSliderValue(ei.value); - } - } - cframe.needAnalyze(); - } + public void clearDialog() { + while (vp.getWidget(0) != hp) + vp.remove(0); + } - public void itemStateChanged(GwtEvent e) { - Object src = e.getSource(); - int i; - boolean changed = false; - boolean applied = false; - for (i = 0; i != einfocount; i++) { - EditInfo ei = einfos[i]; - if (ei.choice == src || ei.checkbox == src || ei.button == src) { - - // if we're pressing a button, make sure to apply changes first - if (ei.button == src && !ei.newDialog) { - apply(); - applied = true; - } - - elm.setEditValue(i, ei); - if (ei.newDialog) - changed = true; - cframe.needAnalyze(); - } - } - if (changed) { - // apply changes before we reset everything - // (need to check if we already applied changes; otherwise Diode create simple model button doesn't work) - if (!applied) - apply(); - - clearDialog(); - buildDialog(); - } - } - - public void resetDialog() { - clearDialog(); - buildDialog(); - } - - public void clearDialog() { - while (vp.getWidget(0)!=hp) - vp.remove(0); - } - - public void closeDialog() - { - super.closeDialog(); - if (CirSim.editDialog == this) - CirSim.editDialog = null; - if (CirSim.customLogicEditDialog == this) - CirSim.customLogicEditDialog = null; - } + public void closeDialog() { + super.closeDialog(); + if (CirSim.editDialog == this) + CirSim.editDialog = null; + if (CirSim.customLogicEditDialog == this) + CirSim.customLogicEditDialog = null; + } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/EditDialogLoadFile.java b/src/main/java/com/lushprojects/circuitjs1/client/EditDialogLoadFile.java index 8822a70..029d95d 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/EditDialogLoadFile.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/EditDialogLoadFile.java @@ -28,31 +28,33 @@ /* * An abstract class for circuitjs which allows components to prompt for files from the user. */ -public abstract class EditDialogLoadFile extends FileUpload implements ChangeHandler { - - static public final boolean isSupported() { return LoadFile.isSupported(); } - - static public void doErrorCallback(String msg) { - Window.alert(Locale.LS(msg)); - } - - EditDialogLoadFile() { - super(); - this.setName(Locale.LS("Load File")); - this.getElement().setId("EditDialogLoadFileElement"); - this.addChangeHandler(this); - this.addStyleName("offScreen"); - this.setPixelSize(0, 0); - } - - public void onChange(ChangeEvent e) { - handle(); - } - - public final native void open() +public abstract class EditDialogLoadFile extends FileUpload implements ChangeHandler { + + static public final boolean isSupported() { + return LoadFile.isSupported(); + } + + static public void doErrorCallback(String msg) { + Window.alert(Locale.LS(msg)); + } + + EditDialogLoadFile() { + super(); + this.setName(Locale.LS("Load File")); + this.getElement().setId("EditDialogLoadFileElement"); + this.addChangeHandler(this); + this.addStyleName("offScreen"); + this.setPixelSize(0, 0); + } + + public void onChange(ChangeEvent e) { + handle(); + } + + public final native void open() /*-{ $doc.getElementById("EditDialogLoadFileElement").click(); }-*/; - - public abstract void handle(); + + public abstract void handle(); } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/EditDiodeModelDialog.java b/src/main/java/com/lushprojects/circuitjs1/client/EditDiodeModelDialog.java index a6ec08c..62d809a 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/EditDiodeModelDialog.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/EditDiodeModelDialog.java @@ -4,28 +4,28 @@ public class EditDiodeModelDialog extends EditDialog { DiodeModel model; DiodeElm diodeElm; - + public EditDiodeModelDialog(DiodeModel dm, CirSim f, DiodeElm de) { - super(dm, f); - model = dm; - diodeElm = de; - applyButton.removeFromParent(); + super(dm, f); + model = dm; + diodeElm = de; + applyButton.removeFromParent(); } void apply() { - super.apply(); - if (model.name == null || model.name.length() == 0) - model.pickName(); - if (diodeElm != null) - diodeElm.newModelCreated(model); + super.apply(); + if (model.name == null || model.name.length() == 0) + model.pickName(); + if (diodeElm != null) + diodeElm.newModelCreated(model); } - + public void closeDialog() { - super.closeDialog(); - EditDialog edlg = CirSim.editDialog; - CirSim.console("resetting dialog " + edlg); - if (edlg != null) - edlg.resetDialog(); - CirSim.diodeModelEditDialog = null; + super.closeDialog(); + EditDialog edlg = CirSim.editDialog; + CirSim.console("resetting dialog " + edlg); + if (edlg != null) + edlg.resetDialog(); + CirSim.diodeModelEditDialog = null; } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/EditInfo.java b/src/main/java/com/lushprojects/circuitjs1/client/EditInfo.java index 77fec21..24c5e6f 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/EditInfo.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/EditInfo.java @@ -18,70 +18,79 @@ */ package com.lushprojects.circuitjs1.client; + import com.google.gwt.user.client.ui.TextBox; import com.google.gwt.user.client.ui.Widget; import com.lushprojects.circuitjs1.client.util.Locale; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.TextArea; -class EditInfo { - - EditInfo(String n, double val, double mn, double mx) { - name = n; - value = val; - dimensionless = false; - minVal = mn; - maxVal = mx; - } - - EditInfo(String n, double val) { - name = n; - value = val; - dimensionless = false; - } - - EditInfo(String n, String txt) { - name = n; - text = txt; - } - - static EditInfo createCheckbox(String name, boolean flag) { - EditInfo ei = new EditInfo("", 0, -1, -1); - ei.checkbox = new Checkbox(name, flag); - return ei; - } - - EditInfo setDimensionless() { dimensionless = true; return this; } - EditInfo disallowSliders() { noSliders = true; return this; } - int changeFlag(int flags, int bit) { - if (checkbox.getState()) - return flags | bit; - return flags & ~bit; - } - - String name, text; - double value; - TextBox textf; - Choice choice; - Checkbox checkbox; - Button button; - EditDialogLoadFile loadFile = null; //if non-null, the button will open a file dialog - TextArea textArea; - Widget widget; - boolean newDialog; - boolean dimensionless; - boolean noSliders; - double minVal, maxVal; - - // for slider dialog - TextBox minBox, maxBox, labelBox; - - boolean canCreateAdjustable() { - return choice == null && checkbox == null && button == null && textArea == null && - widget == null && !noSliders; - } - - static String makeLink(String file, String text) { - return "" + Locale.LS(text) + ""; - } +public class EditInfo { + + EditInfo(String n, double val, double mn, double mx) { + name = n; + value = val; + dimensionless = false; + minVal = mn; + maxVal = mx; + } + + EditInfo(String n, double val) { + name = n; + value = val; + dimensionless = false; + } + + EditInfo(String n, String txt) { + name = n; + text = txt; + } + + static EditInfo createCheckbox(String name, boolean flag) { + EditInfo ei = new EditInfo("", 0, -1, -1); + ei.checkbox = new Checkbox(name, flag); + return ei; + } + + EditInfo setDimensionless() { + dimensionless = true; + return this; + } + + EditInfo disallowSliders() { + noSliders = true; + return this; + } + + int changeFlag(int flags, int bit) { + if (checkbox.getState()) + return flags | bit; + return flags & ~bit; + } + + String name, text; + double value; + TextBox textf; + Choice choice; + Checkbox checkbox; + Button button; + EditDialogLoadFile loadFile = null; //if non-null, the button will open a file dialog + TextArea textArea; + Widget widget; + boolean newDialog; + boolean dimensionless; + boolean noSliders; + double minVal, maxVal; + + // for slider dialog + TextBox minBox, maxBox, labelBox; + + boolean canCreateAdjustable() { + return choice == null && checkbox == null && button == null && textArea == null && + widget == null && !noSliders; + } + + static String makeLink(String file, String text) { + return "" + Locale.LS(text) + ""; + } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/EditOptions.java b/src/main/java/com/lushprojects/circuitjs1/client/EditOptions.java index 0194359..3eadac9 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/EditOptions.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/EditOptions.java @@ -23,145 +23,173 @@ import com.google.gwt.user.client.Window; import com.lushprojects.circuitjs1.client.util.Locale; -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.maxTimeStep, 0, 0); - if (n == 1) - return new EditInfo("Range for voltage color (V)", - CircuitElm.voltageRange, 0, 0); - if (n == 2) { - EditInfo ei = new EditInfo("Change Language", 0, -1, -1); - ei.choice = new Choice(); - ei.choice.add("(no change)"); - ei.choice.add("Čeština"); - ei.choice.add("Dansk"); - ei.choice.add("Deutsch"); - ei.choice.add("English"); - ei.choice.add("Español"); - ei.choice.add("Français"); - ei.choice.add("Italiano"); - ei.choice.add("Norsk bokmål"); - ei.choice.add("Polski"); - ei.choice.add("Português"); - ei.choice.add("\u0420\u0443\u0441\u0441\u043a\u0438\u0439"); // Russian - ei.choice.add("\u4e2d\u6587 (\u4e2d\u56fd\u5927\u9646)"); // Chinese - ei.choice.add("\u4e2d\u6587 (\u53f0\u6e7e)"); // Chinese (tw) - return ei; - } - - if (n == 3) - return new EditInfo("Positive Color", CircuitElm.positiveColor.getHexValue()); - if (n == 4) - return new EditInfo("Negative Color", CircuitElm.negativeColor.getHexValue()); - if (n == 5) - return new EditInfo("Neutral Color", CircuitElm.neutralColor.getHexValue()); - if (n == 6) - return new EditInfo("Selection Color", CircuitElm.selectColor.getHexValue()); - if (n == 7) - return new EditInfo("Current Color", CircuitElm.currentColor.getHexValue()); - if (n == 8) - return new EditInfo("# of Decimal Digits (short format)", CircuitElm.shortDecimalDigits); - if (n == 9) - return new EditInfo("# of Decimal Digits (long format)", CircuitElm.decimalDigits); - if (n == 10) { - EditInfo ei = new EditInfo("", 0, -1, -1); - ei.checkbox = new Checkbox("Developer Mode", sim.developerMode); - return ei; - } - if (n == 11) { - EditInfo ei = new EditInfo("", 0, -1, -1); - ei.checkbox = new Checkbox("Auto-Adjust Timestep", sim.adjustTimeStep); - return ei; - } - if (n == 12 && sim.adjustTimeStep) - return new EditInfo("Minimum time step size (s)", sim.minTimeStep, 0, 0); +public class EditOptions implements Editable { + CirSim sim; - return null; - } - - public void setEditValue(int n, EditInfo ei) { - if (n == 0 && ei.value > 0) { - sim.maxTimeStep = ei.value; + public EditOptions(CirSim s) { + sim = s; + } - // if timestep changed manually, prompt before changing it again - AudioOutputElm.okToChangeTimeStep = false; - } - if (n == 1 && ei.value > 0) - CircuitElm.voltageRange = ei.value; - if (n == 2) { - int lang = ei.choice.getSelectedIndex(); - if (lang == 0) - return; - String langString = null; - switch (lang) { - // Czech is csx instead of cs because we are not ready to use it automatically yet - case 1: langString = "csx"; break; - case 2: langString = "da"; break; - case 3: langString = "de"; break; - case 4: langString = "en"; break; - case 5: langString = "es"; break; - case 6: langString = "fr"; break; - case 7: langString = "it"; break; - case 8: langString = "nb"; break; - case 9: langString = "pl"; break; - case 10: langString = "pt"; break; - case 11: langString = "ru"; break; - case 12: langString = "zh"; break; - case 13: langString = "zh-tw"; break; - } - if (langString == null) - return; - Storage stor = Storage.getLocalStorageIfSupported(); - if (stor == null) { - Window.alert(Locale.LS("Can't set language")); - return; - } - stor.setItem("language", langString); - if (Window.confirm(Locale.LS("Must restart to set language. Restart now?"))) - Window.Location.reload(); - } - if (n == 3) { - CircuitElm.positiveColor = setColor("positiveColor", ei, Color.green); - CircuitElm.setColorScale(); - } - if (n == 4) { - CircuitElm.negativeColor = setColor("negativeColor", ei, Color.red); - CircuitElm.setColorScale(); - } - if (n == 5) { - CircuitElm.neutralColor = setColor("neutralColor", ei, Color.gray); - CircuitElm.setColorScale(); - } - if (n == 6) - CircuitElm.selectColor = setColor("selectColor", ei, Color.cyan); - if (n == 7) - CircuitElm.currentColor = setColor("currentColor", ei, Color.yellow); - if (n == 8) - CircuitElm.setDecimalDigits((int)ei.value, true, true); - if (n == 9) - CircuitElm.setDecimalDigits((int)ei.value, false, true); - if (n == 10) - sim.developerMode = ei.checkbox.getState(); - if (n == 11) { - sim.adjustTimeStep = ei.checkbox.getState(); - ei.newDialog = true; - } - if (n == 12 && ei.value > 0) - sim.minTimeStep = ei.value; - } - - Color setColor(String name, EditInfo ei, Color def) { - String val = ei.textf.getText(); - if (val.length() == 0) - val = def.getHexValue(); - Storage stor = Storage.getLocalStorageIfSupported(); - if (stor != null) - stor.setItem(name, val); - return new Color(val); - } + public EditInfo getEditInfo(int n) { + if (n == 0) + return new EditInfo("Time step size (s)", sim.maxTimeStep, 0, 0); + if (n == 1) + return new EditInfo("Range for voltage color (V)", + CircuitElm.voltageRange, 0, 0); + if (n == 2) { + EditInfo ei = new EditInfo("Change Language", 0, -1, -1); + ei.choice = new Choice(); + ei.choice.add("(no change)"); + ei.choice.add("Čeština"); + ei.choice.add("Dansk"); + ei.choice.add("Deutsch"); + ei.choice.add("English"); + ei.choice.add("Español"); + ei.choice.add("Français"); + ei.choice.add("Italiano"); + ei.choice.add("Norsk bokmål"); + ei.choice.add("Polski"); + ei.choice.add("Português"); + ei.choice.add("\u0420\u0443\u0441\u0441\u043a\u0438\u0439"); // Russian + ei.choice.add("\u4e2d\u6587 (\u4e2d\u56fd\u5927\u9646)"); // Chinese + ei.choice.add("\u4e2d\u6587 (\u53f0\u6e7e)"); // Chinese (tw) + return ei; + } + + if (n == 3) + return new EditInfo("Positive Color", CircuitElm.positiveColor.getHexValue()); + if (n == 4) + return new EditInfo("Negative Color", CircuitElm.negativeColor.getHexValue()); + if (n == 5) + return new EditInfo("Neutral Color", CircuitElm.neutralColor.getHexValue()); + if (n == 6) + return new EditInfo("Selection Color", CircuitElm.selectColor.getHexValue()); + if (n == 7) + return new EditInfo("Current Color", CircuitElm.currentColor.getHexValue()); + if (n == 8) + return new EditInfo("# of Decimal Digits (short format)", CircuitElm.shortDecimalDigits); + if (n == 9) + return new EditInfo("# of Decimal Digits (long format)", CircuitElm.decimalDigits); + if (n == 10) { + EditInfo ei = new EditInfo("", 0, -1, -1); + ei.checkbox = new Checkbox("Developer Mode", sim.developerMode); + return ei; + } + if (n == 11) { + EditInfo ei = new EditInfo("", 0, -1, -1); + ei.checkbox = new Checkbox("Auto-Adjust Timestep", sim.adjustTimeStep); + return ei; + } + if (n == 12 && sim.adjustTimeStep) + return new EditInfo("Minimum time step size (s)", sim.minTimeStep, 0, 0); + + return null; + } + + public void setEditValue(int n, EditInfo ei) { + if (n == 0 && ei.value > 0) { + sim.maxTimeStep = ei.value; + + // if timestep changed manually, prompt before changing it again + AudioOutputElm.okToChangeTimeStep = false; + } + if (n == 1 && ei.value > 0) + CircuitElm.voltageRange = ei.value; + if (n == 2) { + int lang = ei.choice.getSelectedIndex(); + if (lang == 0) + return; + String langString = null; + switch (lang) { + // Czech is csx instead of cs because we are not ready to use it automatically yet + case 1: + langString = "csx"; + break; + case 2: + langString = "da"; + break; + case 3: + langString = "de"; + break; + case 4: + langString = "en"; + break; + case 5: + langString = "es"; + break; + case 6: + langString = "fr"; + break; + case 7: + langString = "it"; + break; + case 8: + langString = "nb"; + break; + case 9: + langString = "pl"; + break; + case 10: + langString = "pt"; + break; + case 11: + langString = "ru"; + break; + case 12: + langString = "zh"; + break; + case 13: + langString = "zh-tw"; + break; + } + if (langString == null) + return; + Storage stor = Storage.getLocalStorageIfSupported(); + if (stor == null) { + Window.alert(Locale.LS("Can't set language")); + return; + } + stor.setItem("language", langString); + if (Window.confirm(Locale.LS("Must restart to set language. Restart now?"))) + Window.Location.reload(); + } + if (n == 3) { + CircuitElm.positiveColor = setColor("positiveColor", ei, Color.green); + CircuitElm.setColorScale(); + } + if (n == 4) { + CircuitElm.negativeColor = setColor("negativeColor", ei, Color.red); + CircuitElm.setColorScale(); + } + if (n == 5) { + CircuitElm.neutralColor = setColor("neutralColor", ei, Color.gray); + CircuitElm.setColorScale(); + } + if (n == 6) + CircuitElm.selectColor = setColor("selectColor", ei, Color.cyan); + if (n == 7) + CircuitElm.currentColor = setColor("currentColor", ei, Color.yellow); + if (n == 8) + CircuitElm.setDecimalDigits((int) ei.value, true, true); + if (n == 9) + CircuitElm.setDecimalDigits((int) ei.value, false, true); + if (n == 10) + sim.developerMode = ei.checkbox.getState(); + if (n == 11) { + sim.adjustTimeStep = ei.checkbox.getState(); + ei.newDialog = true; + } + if (n == 12 && ei.value > 0) + sim.minTimeStep = ei.value; + } + + Color setColor(String name, EditInfo ei, Color def) { + String val = ei.textf.getText(); + if (val.length() == 0) + val = def.getHexValue(); + Storage stor = Storage.getLocalStorageIfSupported(); + if (stor != null) + stor.setItem(name, val); + return new Color(val); + } }; diff --git a/src/main/java/com/lushprojects/circuitjs1/client/EditTransistorModelDialog.java b/src/main/java/com/lushprojects/circuitjs1/client/EditTransistorModelDialog.java index ef732ef..0ab1c0b 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/EditTransistorModelDialog.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/EditTransistorModelDialog.java @@ -4,28 +4,28 @@ public class EditTransistorModelDialog extends EditDialog { TransistorModel model; TransistorElm transistorElm; - + public EditTransistorModelDialog(TransistorModel dm, CirSim f, TransistorElm te) { - super(dm, f); - model = dm; + super(dm, f); + model = dm; transistorElm = te; - applyButton.removeFromParent(); + applyButton.removeFromParent(); } void apply() { - super.apply(); + super.apply(); // if (model.name == null || model.name.length() == 0) // model.pickName(); - if (transistorElm != null) - transistorElm.newModelCreated(model); + if (transistorElm != null) + transistorElm.newModelCreated(model); } - + public void closeDialog() { - super.closeDialog(); - EditDialog edlg = CirSim.editDialog; - CirSim.console("resetting dialog " + edlg); - if (edlg != null) - edlg.resetDialog(); - CirSim.diodeModelEditDialog = null; + super.closeDialog(); + EditDialog edlg = CirSim.editDialog; + CirSim.console("resetting dialog " + edlg); + if (edlg != null) + edlg.resetDialog(); + CirSim.diodeModelEditDialog = null; } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/ExportAsImageDialog.java b/src/main/java/com/lushprojects/circuitjs1/client/ExportAsImageDialog.java index 4495019..3bcf6df 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/ExportAsImageDialog.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/ExportAsImageDialog.java @@ -20,6 +20,7 @@ package com.lushprojects.circuitjs1.client; import java.util.Date; + import com.google.gwt.user.client.ui.Anchor; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.VerticalPanel; @@ -30,43 +31,43 @@ import com.google.gwt.i18n.client.DateTimeFormat; public class ExportAsImageDialog extends Dialog { - - VerticalPanel vp; - - private static native String b64encode(String a) /*-{ + + VerticalPanel vp; + + private static native String b64encode(String a) /*-{ // string may have unicode text strings in it, so we don't just call btoa() return window.btoa(unescape(encodeURIComponent(a))); }-*/; - public ExportAsImageDialog(int type) { - super(); - Button okButton; - Anchor a; - vp=new VerticalPanel(); - setWidget(vp); - setText(Locale.LS("Export as Image")); - vp.add(new Label(Locale.LS("Click on the link below to save your image"))); - Date date = new Date(); - DateTimeFormat dtf = DateTimeFormat.getFormat("yyyyMMdd-HHmm"); - String dataURL; - String ext = ".png"; - if (type == CirSim.CAC_IMAGE) { - dataURL = CirSim.theSim.getCircuitAsCanvas(type).toDataUrl(); - } else { - String data = CirSim.theSim.getCircuitAsSVG(); - dataURL = "data:text/plain;base64," + b64encode(data); - ext = ".svg"; - } - a=new Anchor("image" + ext, dataURL); - String fname = "circuit-"+ dtf.format(date) + ext; - a.getElement().setAttribute("Download", fname); - vp.add(a); - vp.add(okButton = new Button(Locale.LS("OK"))); - okButton.addClickHandler(new ClickHandler() { - public void onClick(ClickEvent event) { - closeDialog(); - } - }); - this.center(); - } + public ExportAsImageDialog(int type) { + super(); + Button okButton; + Anchor a; + vp = new VerticalPanel(); + setWidget(vp); + setText(Locale.LS("Export as Image")); + vp.add(new Label(Locale.LS("Click on the link below to save your image"))); + Date date = new Date(); + DateTimeFormat dtf = DateTimeFormat.getFormat("yyyyMMdd-HHmm"); + String dataURL; + String ext = ".png"; + if (type == CirSim.CAC_IMAGE) { + dataURL = CirSim.theSim.getCircuitAsCanvas(type).toDataUrl(); + } else { + String data = CirSim.theSim.getCircuitAsSVG(); + dataURL = "data:text/plain;base64," + b64encode(data); + ext = ".svg"; + } + a = new Anchor("image" + ext, dataURL); + String fname = "circuit-" + dtf.format(date) + ext; + a.getElement().setAttribute("Download", fname); + vp.add(a); + vp.add(okButton = new Button(Locale.LS("OK"))); + okButton.addClickHandler(new ClickHandler() { + public void onClick(ClickEvent event) { + closeDialog(); + } + }); + this.center(); + } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/ExportAsLocalFileDialog.java b/src/main/java/com/lushprojects/circuitjs1/client/ExportAsLocalFileDialog.java index 2d0653f..78c7d7a 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/ExportAsLocalFileDialog.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/ExportAsLocalFileDialog.java @@ -20,6 +20,7 @@ package com.lushprojects.circuitjs1.client; import java.util.Date; + import com.google.gwt.user.client.ui.HasHorizontalAlignment; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Anchor; @@ -36,15 +37,15 @@ import com.google.gwt.i18n.client.DateTimeFormat; public class ExportAsLocalFileDialog extends Dialog implements ValueChangeHandler { - - VerticalPanel vp; - - static public final native boolean downloadIsSupported() + + VerticalPanel vp; + + static public final native boolean downloadIsSupported() /*-{ return !!(("download" in $doc.createElement("a"))); }-*/; - - static public final native String getBlobUrl(String data) + + static public final native String getBlobUrl(String data) /*-{ var datain=[""]; datain[0]=data; @@ -56,83 +57,78 @@ static public final native String getBlobUrl(String data) $doc.exportBlob = url; return url; }-*/; - - TextBox textBox; - static String lastFileName; - String url; - - public static void setLastFileName(String s) { - // remember filename for use when saving a new file. - // if s is null or automatically generated then just clear out old filename. - if (s == null || (s.startsWith("circuit-") && s.contains(".circuitjs"))) - lastFileName = null; - else - lastFileName = s; - } - - public ExportAsLocalFileDialog(String data) { - super(); - Button okButton, cancelButton; - vp=new VerticalPanel(); - setWidget(vp); - setText(Locale.LS("Export as Local File")); - vp.add(new Label(Locale.LS("File name:"))); - textBox = new TextBox(); - textBox.addValueChangeHandler(this); - textBox.setWidth("250px"); // "90%"); - vp.add(textBox); - url=getBlobUrl(data); - Date date = new Date(); - String fname; - if (lastFileName != null) - fname = lastFileName; - else { - DateTimeFormat dtf = DateTimeFormat.getFormat("yyyyMMdd-HHmm"); - fname = "circuit-"+ dtf.format(date) + ".circuitjs.txt"; - } - textBox.setText(fname); - - HorizontalPanel hp = new HorizontalPanel(); - hp.setWidth("100%"); - hp.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_LEFT); - hp.setStyleName("topSpace"); - vp.add(hp); - hp.add(okButton = new Button(Locale.LS("OK"))); - hp.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_RIGHT); - hp.add(cancelButton = new Button(Locale.LS("Cancel"))); - okButton.addClickHandler(new ClickHandler() { - public void onClick(ClickEvent event) { - apply(); - closeDialog(); - } - }); - cancelButton.addClickHandler(new ClickHandler() { - public void onClick(ClickEvent event) { - closeDialog(); - } - }); - this.center(); - } - - static native void click(Element elem) /*-{ + + TextBox textBox; + static String lastFileName; + String url; + + public static void setLastFileName(String s) { + // remember filename for use when saving a new file. + // if s is null or automatically generated then just clear out old filename. + if (s == null || (s.startsWith("circuit-") && s.contains(".circuitjs"))) lastFileName = null; + else lastFileName = s; + } + + public ExportAsLocalFileDialog(String data) { + super(); + Button okButton, cancelButton; + vp = new VerticalPanel(); + setWidget(vp); + setText(Locale.LS("Export as Local File")); + vp.add(new Label(Locale.LS("File name:"))); + textBox = new TextBox(); + textBox.addValueChangeHandler(this); + textBox.setWidth("250px"); // "90%"); + vp.add(textBox); + url = getBlobUrl(data); + Date date = new Date(); + String fname; + if (lastFileName != null) fname = lastFileName; + else { + DateTimeFormat dtf = DateTimeFormat.getFormat("yyyyMMdd-HHmm"); + fname = "circuit-" + dtf.format(date) + ".circuitjs.txt"; + } + textBox.setText(fname); + + HorizontalPanel hp = new HorizontalPanel(); + hp.setWidth("100%"); + hp.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_LEFT); + hp.setStyleName("topSpace"); + vp.add(hp); + hp.add(okButton = new Button(Locale.LS("OK"))); + hp.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_RIGHT); + hp.add(cancelButton = new Button(Locale.LS("Cancel"))); + okButton.addClickHandler(new ClickHandler() { + public void onClick(ClickEvent event) { + apply(); + closeDialog(); + } + }); + cancelButton.addClickHandler(new ClickHandler() { + public void onClick(ClickEvent event) { + closeDialog(); + } + }); + this.center(); + } + + static native void click(Element elem) /*-{ elem.click(); }-*/; - - void apply() { - String fname = textBox.getText(); - if (!fname.contains(".")) - fname += ".txt"; - Anchor a = new Anchor(fname, url); - a.getElement().setAttribute("Download", fname); - vp.add(a); - click(a.getElement()); - } - - public void onValueChange(ValueChangeEvent event) { - // update filename - String fname = textBox.getText(); - if (fname.length() == 0) - return; - lastFileName = fname; - } + + void apply() { + String fname = textBox.getText(); + if (!fname.contains(".")) fname += ".txt"; + Anchor a = new Anchor(fname, url); + a.getElement().setAttribute("Download", fname); + vp.add(a); + click(a.getElement()); + } + + public void onValueChange(ValueChangeEvent event) { + // update filename + String fname = textBox.getText(); + if (fname.length() == 0) return; + lastFileName = fname; + } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/ExportAsTextDialog.java b/src/main/java/com/lushprojects/circuitjs1/client/ExportAsTextDialog.java index 3630098..ad7795a 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/ExportAsTextDialog.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/ExportAsTextDialog.java @@ -31,75 +31,75 @@ import com.google.gwt.safehtml.shared.SafeHtml; public class ExportAsTextDialog extends Dialog { - - VerticalPanel vp; - CirSim sim; - TextArea textArea; - - public ExportAsTextDialog(CirSim asim, String s) { - super(); - closeOnEnter = false; - sim = asim; - // RichTextArea tb; - TextArea ta; - Button okButton, importButton, copyButton; - Label la2; - SafeHtml html; - vp=new VerticalPanel(); - setWidget(vp); - setText(Locale.LS("Export as Text")); - vp.add(new Label(Locale.LS("Text file for this circuit is..."))); + + VerticalPanel vp; + CirSim sim; + TextArea textArea; + + public ExportAsTextDialog(CirSim asim, String s) { + super(); + closeOnEnter = false; + sim = asim; + // RichTextArea tb; + TextArea ta; + Button okButton, importButton, copyButton; + Label la2; + SafeHtml html; + vp = new VerticalPanel(); + setWidget(vp); + setText(Locale.LS("Export as Text")); + vp.add(new Label(Locale.LS("Text file for this circuit is..."))); // vp.add(tb = new RichTextArea()); // html=SafeHtmlUtils.fromString(s); // html=SafeHtmlUtils.fromTrustedString(html.asString().replace("\n", "
")); // tb.setHTML(html); - vp.add(ta= new TextArea()); - ta.setWidth("400px"); - ta.setHeight("300px"); - ta.setText(s); - textArea = ta; - // vp.add(la2 = new Label(sim.LS("To save this file select it all (eg click in text and type control-A) and copy to your clipboard (eg control-C) before pasting to an empty text file (eg on Windows Notepad) and saving as a new file."), true)); - // la2.setWidth("300px"); - HorizontalPanel hp = new HorizontalPanel(); - hp.setWidth("100%"); - hp.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_LEFT); - hp.setStyleName("topSpace"); - vp.add(hp); - hp.add(okButton = new Button(Locale.LS("OK"))); - hp.add(copyButton = new Button(Locale.LS("Copy to Clipboard"))); - hp.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_RIGHT); - hp.add(importButton = new Button(Locale.LS("Re-Import"))); - okButton.addClickHandler(new ClickHandler() { - public void onClick(ClickEvent event) { - closeDialog(); - } - }); - importButton.addClickHandler(new ClickHandler() { - public void onClick(ClickEvent event) { - String s; - sim.pushUndo(); - closeDialog(); + vp.add(ta = new TextArea()); + ta.setWidth("400px"); + ta.setHeight("300px"); + ta.setText(s); + textArea = ta; + // vp.add(la2 = new Label(sim.LS("To save this file select it all (eg click in text and type control-A) and copy to your clipboard (eg control-C) before pasting to an empty text file (eg on Windows Notepad) and saving as a new file."), true)); + // la2.setWidth("300px"); + HorizontalPanel hp = new HorizontalPanel(); + hp.setWidth("100%"); + hp.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_LEFT); + hp.setStyleName("topSpace"); + vp.add(hp); + hp.add(okButton = new Button(Locale.LS("OK"))); + hp.add(copyButton = new Button(Locale.LS("Copy to Clipboard"))); + hp.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_RIGHT); + hp.add(importButton = new Button(Locale.LS("Re-Import"))); + okButton.addClickHandler(new ClickHandler() { + public void onClick(ClickEvent event) { + closeDialog(); + } + }); + importButton.addClickHandler(new ClickHandler() { + public void onClick(ClickEvent event) { + String s; + sim.pushUndo(); + closeDialog(); // s=textBox.getHTML(); // s=s.replace("
", "\r"); - s=textArea.getText(); - if (s!=null) { - sim.readCircuit(s); - sim.allowSave(false); - } - } - }); - copyButton.addClickHandler(new ClickHandler() { - public void onClick(ClickEvent event) { - textArea.setFocus(true); - textArea.selectAll(); - copyToClipboard(); - textArea.setSelectionRange(0,0); - } - }); - this.center(); - } - - private static native boolean copyToClipboard() /*-{ + s = textArea.getText(); + if (s != null) { + sim.readCircuit(s); + sim.allowSave(false); + } + } + }); + copyButton.addClickHandler(new ClickHandler() { + public void onClick(ClickEvent event) { + textArea.setFocus(true); + textArea.selectAll(); + copyToClipboard(); + textArea.setSelectionRange(0, 0); + } + }); + this.center(); + } + + private static native boolean copyToClipboard() /*-{ return $doc.execCommand('copy'); }-*/; diff --git a/src/main/java/com/lushprojects/circuitjs1/client/ExportAsUrlDialog.java b/src/main/java/com/lushprojects/circuitjs1/client/ExportAsUrlDialog.java index 0fd62f9..c387a1a 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/ExportAsUrlDialog.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/ExportAsUrlDialog.java @@ -38,120 +38,118 @@ import com.google.gwt.http.client.Request; public class ExportAsUrlDialog extends Dialog { - - VerticalPanel vp; - Button shortButton; - static TextArea textArea; - String requrl; - - public boolean shortIsSupported() { - return circuitjs1.shortRelaySupported; - } - + + VerticalPanel vp; + Button shortButton; + static TextArea textArea; + String requrl; + + public boolean shortIsSupported() { + return circuitjs1.shortRelaySupported; + } + // static public final native boolean bitlyIsSupported() // /*-{ // return !!($wnd.bitlytoken !==undefined && $wnd.bitlytoken !==null); // }-*/; // - - static public void createShort(String urlin) - { - String url; - url = "shortrelay.php"+"?v="+urlin; - textArea.setText("Waiting for short URL for web service..."); - RequestBuilder requestBuilder = new RequestBuilder(RequestBuilder.GET, url); - try { - requestBuilder.sendRequest(null, new RequestCallback() { - public void onError(Request request, Throwable exception) { - GWT.log("File Error Response", exception); - } - - public void onResponseReceived(Request request, Response response) { - // processing goes here - if (response.getStatusCode()==Response.SC_OK) { - String text = response.getText(); - textArea.setText(text); - // end or processing - } - else { - String text="Shortner error:"+response.getStatusText(); - textArea.setText(text); - GWT.log(text ); - } - } - }); - } catch (RequestException e) { - GWT.log("failed file reading", e); - } + + static public void createShort(String urlin) { + String url; + url = "shortrelay.php" + "?v=" + urlin; + textArea.setText("Waiting for short URL for web service..."); + RequestBuilder requestBuilder = new RequestBuilder(RequestBuilder.GET, url); + try { + requestBuilder.sendRequest(null, new RequestCallback() { + public void onError(Request request, Throwable exception) { + GWT.log("File Error Response", exception); + } + + public void onResponseReceived(Request request, Response response) { + // processing goes here + if (response.getStatusCode() == Response.SC_OK) { + String text = response.getText(); + textArea.setText(text); + // end or processing + } else { + String text = "Shortner error:" + response.getStatusText(); + textArea.setText(text); + GWT.log(text); + } + } + }); + } catch (RequestException e) { + GWT.log("failed file reading", e); + } } - - native String compress(String dump) /*-{ + + native String compress(String dump) /*-{ return $wnd.LZString.compressToEncodedURIComponent(dump); }-*/; - - public ExportAsUrlDialog( String dump) { - super(); - closeOnEnter = false; - String start = "https://www.falstad.com/circuit/circuitjs.html"; - String query="?ctz=" + compress(dump); - dump = start + query; - requrl = URL.encodeQueryString(query); - Button okButton, copyButton; - - Label la1, la2; - vp=new VerticalPanel(); - setWidget(vp); - setText(Locale.LS("Export as URL")); - vp.add(new Label(Locale.LS("URL for this circuit is..."))); - if (dump.length()>2000) { - vp.add( la1= new Label(Locale.LS("Warning: this URL is longer than 2000 characters and may not work in some browsers."), true)); - la1.setWidth("300px"); - } - vp.add(textArea = new TextArea()); - textArea.setWidth("400px"); - textArea.setHeight("300px"); - textArea.setText(dump); + + public ExportAsUrlDialog(String dump) { + super(); + closeOnEnter = false; + String start = "https://www.falstad.com/circuit/circuitjs.html"; + String query = "?ctz=" + compress(dump); + dump = start + query; + requrl = URL.encodeQueryString(query); + Button okButton, copyButton; + + Label la1, la2; + vp = new VerticalPanel(); + setWidget(vp); + setText(Locale.LS("Export as URL")); + vp.add(new Label(Locale.LS("URL for this circuit is..."))); + if (dump.length() > 2000) { + vp.add(la1 = new Label(Locale.LS("Warning: this URL is longer than 2000 characters and may not work in some browsers."), true)); + la1.setWidth("300px"); + } + vp.add(textArea = new TextArea()); + textArea.setWidth("400px"); + textArea.setHeight("300px"); + textArea.setText(dump); // tb.setMaxLength(s.length()); // tb.setVisibleLength(s.length()); // vp.add(la2 = new Label(CirSim.LS("To save this URL select it all (eg click in text and type control-A) and copy to your clipboard (eg control-C) before pasting to a suitable place."), true)); // la2.setWidth("300px"); - - HorizontalPanel hp = new HorizontalPanel(); - hp.setWidth("100%"); - hp.setStyleName("topSpace"); - hp.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_LEFT); - hp.add(okButton = new Button(Locale.LS("OK"))); - hp.add(copyButton = new Button(Locale.LS("Copy to Clipboard"))); - vp.add(hp); - if (shortIsSupported()) { - hp.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_RIGHT); - - hp.add(shortButton = new Button(Locale.LS("Create short URL"))); - shortButton.addClickHandler(new ClickHandler() { - public void onClick(ClickEvent event) { - shortButton.setVisible(false); - createShort(requrl); - } - }); - } - okButton.addClickHandler(new ClickHandler() { - public void onClick(ClickEvent event) { - closeDialog(); - } - }); - copyButton.addClickHandler(new ClickHandler() { - public void onClick(ClickEvent event) { - textArea.setFocus(true); - textArea.selectAll(); - copyToClipboard(); - textArea.setSelectionRange(0,0); - } - }); - this.center(); - } - - private static native boolean copyToClipboard() /*-{ + + HorizontalPanel hp = new HorizontalPanel(); + hp.setWidth("100%"); + hp.setStyleName("topSpace"); + hp.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_LEFT); + hp.add(okButton = new Button(Locale.LS("OK"))); + hp.add(copyButton = new Button(Locale.LS("Copy to Clipboard"))); + vp.add(hp); + if (shortIsSupported()) { + hp.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_RIGHT); + + hp.add(shortButton = new Button(Locale.LS("Create short URL"))); + shortButton.addClickHandler(new ClickHandler() { + public void onClick(ClickEvent event) { + shortButton.setVisible(false); + createShort(requrl); + } + }); + } + okButton.addClickHandler(new ClickHandler() { + public void onClick(ClickEvent event) { + closeDialog(); + } + }); + copyButton.addClickHandler(new ClickHandler() { + public void onClick(ClickEvent event) { + textArea.setFocus(true); + textArea.selectAll(); + copyToClipboard(); + textArea.setSelectionRange(0, 0); + } + }); + this.center(); + } + + private static native boolean copyToClipboard() /*-{ return $doc.execCommand('copy'); }-*/; diff --git a/src/main/java/com/lushprojects/circuitjs1/client/Expr.java b/src/main/java/com/lushprojects/circuitjs1/client/Expr.java index dc38d0c..c82026c 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/Expr.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/Expr.java @@ -2,180 +2,217 @@ import java.util.Vector; -class ExprState { +public class ExprState { //int n; double values[]; double lastValues[]; double lastOutput; double t; + ExprState(int xx) { - //n = xx; - values = new double[9]; - lastValues = new double[9]; - values[4] = Math.E; + //n = xx; + values = new double[9]; + lastValues = new double[9]; + values[4] = Math.E; } - + void updateLastValues(double lastOut) { - lastOutput = lastOut; - int i; - for (i = 0; i != values.length; i++) - lastValues[i] = values[i]; + lastOutput = lastOut; + int i; + for (i = 0; i != values.length; i++) + lastValues[i] = values[i]; } - + void reset() { - for (int i = 0; i != values.length; i++) - lastValues[i] = 0; - lastOutput = 0; + for (int i = 0; i != values.length; i++) + lastValues[i] = 0; + lastOutput = 0; } } -class Expr { +public class Expr { Expr(Expr e1, Expr e2, int v) { - children = new Vector(); - children.add(e1); - if (e2 != null) - children.add(e2); - type = v; + children = new Vector(); + children.add(e1); + if (e2 != null) + children.add(e2); + type = v; } + Expr(int v, double vv) { - type = v; - value = vv; + type = v; + value = vv; } + Expr(int v) { - type = v; + type = v; } + double eval(ExprState es) { - Expr left = null; - Expr right = null; - if (children != null && children.size() > 0) { - left = children.firstElement(); - if (children.size() == 2) - right = children.lastElement(); - } - switch (type) { - case E_ADD: return left.eval(es)+right.eval(es); - case E_SUB: return left.eval(es)-right.eval(es); - case E_MUL: return left.eval(es)*right.eval(es); - case E_DIV: return left.eval(es)/right.eval(es); - case E_POW: return Math.pow(left.eval(es), right.eval(es)); - case E_OR: return (left.eval(es) != 0 || right.eval(es) != 0) ? 1 : 0; - case E_AND: return (left.eval(es) != 0 && right.eval(es) != 0) ? 1 : 0; - case E_EQUALS: return (left.eval(es) == right.eval(es)) ? 1 : 0; - case E_NEQ: return (left.eval(es) != right.eval(es)) ? 1 : 0; - case E_LEQ: return (left.eval(es) <= right.eval(es)) ? 1 : 0; - case E_GEQ: return (left.eval(es) >= right.eval(es)) ? 1 : 0; - case E_LESS: return (left.eval(es) < right.eval(es)) ? 1 : 0; - case E_GREATER: return (left.eval(es) > right.eval(es)) ? 1 : 0; - case E_TERNARY: return children.get(left.eval(es) != 0 ? 1 : 2).eval(es); - case E_UMINUS: return -left.eval(es); - case E_NOT: return left.eval(es) == 0 ? 1 : 0; - case E_VAL: return value; - case E_T: return es.t; - case E_SIN: return Math.sin(left.eval(es)); - case E_COS: return Math.cos(left.eval(es)); - case E_ABS: return Math.abs(left.eval(es)); - case E_EXP: return Math.exp(left.eval(es)); - case E_LOG: return Math.log(left.eval(es)); - case E_SQRT: return Math.sqrt(left.eval(es)); - case E_TAN: return Math.tan(left.eval(es)); - case E_ASIN: return Math.asin(left.eval(es)); - case E_ACOS: return Math.acos(left.eval(es)); - case E_ATAN: return Math.atan(left.eval(es)); - case E_SINH: return Math.sinh(left.eval(es)); - case E_COSH: return Math.cosh(left.eval(es)); - case E_TANH: return Math.tanh(left.eval(es)); - case E_FLOOR: return Math.floor(left.eval(es)); - case E_CEIL: return Math.ceil(left.eval(es)); - case E_MIN: { - int i; - double x = left.eval(es); - for (i = 1; i < children.size(); i++) - x = Math.min(x, children.get(i).eval(es)); - return x; - } - case E_MAX: { - int i; - double x = left.eval(es); - for (i = 1; i < children.size(); i++) - x = Math.max(x, children.get(i).eval(es)); - return x; - } - case E_CLAMP: - return Math.min(Math.max(left.eval(es), children.get(1).eval(es)), children.get(2).eval(es)); - case E_STEP: { - double x = left.eval(es); - if (right == null) - return (x < 0) ? 0 : 1; - return (x > right.eval(es)) ? 0 : (x < 0) ? 0 : 1; - } - case E_SELECT: { - double x = left.eval(es); - return children.get(x > 0 ? 2 : 1).eval(es); - } - case E_TRIANGLE: { - double x = posmod(left.eval(es), Math.PI*2)/Math.PI; - return (x < 1) ? -1+x*2 : 3-x*2; - } - case E_SAWTOOTH: { - double x = posmod(left.eval(es), Math.PI*2)/Math.PI; - return x-1; - } - case E_MOD: - return left.eval(es) % right.eval(es); - case E_PWL: - return pwl(es, children); - case E_PWR: - return Math.pow(Math.abs(left.eval(es)), right.eval(es)); - case E_PWRS: { - double x = left.eval(es); - if (x < 0) - return -Math.pow(-x, right.eval(es)); - return Math.pow(x, right.eval(es)); - } - case E_LASTOUTPUT: - return es.lastOutput; - case E_TIMESTEP: - return CirSim.theSim.timeStep; - default: - if (type >= E_LASTA) - return es.lastValues[type-E_LASTA]; - if (type >= E_DADT) - return (es.values[type-E_DADT]-es.lastValues[type-E_DADT])/CirSim.theSim.timeStep; - if (type >= E_A) - return es.values[type-E_A]; - CirSim.console("unknown\n"); - } - return 0; + Expr left = null; + Expr right = null; + if (children != null && children.size() > 0) { + left = children.firstElement(); + if (children.size() == 2) + right = children.lastElement(); + } + switch (type) { + case E_ADD: + return left.eval(es) + right.eval(es); + case E_SUB: + return left.eval(es) - right.eval(es); + case E_MUL: + return left.eval(es) * right.eval(es); + case E_DIV: + return left.eval(es) / right.eval(es); + case E_POW: + return Math.pow(left.eval(es), right.eval(es)); + case E_OR: + return (left.eval(es) != 0 || right.eval(es) != 0) ? 1 : 0; + case E_AND: + return (left.eval(es) != 0 && right.eval(es) != 0) ? 1 : 0; + case E_EQUALS: + return (left.eval(es) == right.eval(es)) ? 1 : 0; + case E_NEQ: + return (left.eval(es) != right.eval(es)) ? 1 : 0; + case E_LEQ: + return (left.eval(es) <= right.eval(es)) ? 1 : 0; + case E_GEQ: + return (left.eval(es) >= right.eval(es)) ? 1 : 0; + case E_LESS: + return (left.eval(es) < right.eval(es)) ? 1 : 0; + case E_GREATER: + return (left.eval(es) > right.eval(es)) ? 1 : 0; + case E_TERNARY: + return children.get(left.eval(es) != 0 ? 1 : 2).eval(es); + case E_UMINUS: + return -left.eval(es); + case E_NOT: + return left.eval(es) == 0 ? 1 : 0; + case E_VAL: + return value; + case E_T: + return es.t; + case E_SIN: + return Math.sin(left.eval(es)); + case E_COS: + return Math.cos(left.eval(es)); + case E_ABS: + return Math.abs(left.eval(es)); + case E_EXP: + return Math.exp(left.eval(es)); + case E_LOG: + return Math.log(left.eval(es)); + case E_SQRT: + return Math.sqrt(left.eval(es)); + case E_TAN: + return Math.tan(left.eval(es)); + case E_ASIN: + return Math.asin(left.eval(es)); + case E_ACOS: + return Math.acos(left.eval(es)); + case E_ATAN: + return Math.atan(left.eval(es)); + case E_SINH: + return Math.sinh(left.eval(es)); + case E_COSH: + return Math.cosh(left.eval(es)); + case E_TANH: + return Math.tanh(left.eval(es)); + case E_FLOOR: + return Math.floor(left.eval(es)); + case E_CEIL: + return Math.ceil(left.eval(es)); + case E_MIN: { + int i; + double x = left.eval(es); + for (i = 1; i < children.size(); i++) + x = Math.min(x, children.get(i).eval(es)); + return x; + } + case E_MAX: { + int i; + double x = left.eval(es); + for (i = 1; i < children.size(); i++) + x = Math.max(x, children.get(i).eval(es)); + return x; + } + case E_CLAMP: + return Math.min(Math.max(left.eval(es), children.get(1).eval(es)), children.get(2).eval(es)); + case E_STEP: { + double x = left.eval(es); + if (right == null) + return (x < 0) ? 0 : 1; + return (x > right.eval(es)) ? 0 : (x < 0) ? 0 : 1; + } + case E_SELECT: { + double x = left.eval(es); + return children.get(x > 0 ? 2 : 1).eval(es); + } + case E_TRIANGLE: { + double x = posmod(left.eval(es), Math.PI * 2) / Math.PI; + return (x < 1) ? -1 + x * 2 : 3 - x * 2; + } + case E_SAWTOOTH: { + double x = posmod(left.eval(es), Math.PI * 2) / Math.PI; + return x - 1; + } + case E_MOD: + return left.eval(es) % right.eval(es); + case E_PWL: + return pwl(es, children); + case E_PWR: + return Math.pow(Math.abs(left.eval(es)), right.eval(es)); + case E_PWRS: { + double x = left.eval(es); + if (x < 0) + return -Math.pow(-x, right.eval(es)); + return Math.pow(x, right.eval(es)); + } + case E_LASTOUTPUT: + return es.lastOutput; + case E_TIMESTEP: + return CirSim.theSim.timeStep; + default: + if (type >= E_LASTA) + return es.lastValues[type - E_LASTA]; + if (type >= E_DADT) + return (es.values[type - E_DADT] - es.lastValues[type - E_DADT]) / CirSim.theSim.timeStep; + if (type >= E_A) + return es.values[type - E_A]; + CirSim.console("unknown\n"); + } + return 0; } - + double pwl(ExprState es, Vector args) { - double x = args.get(0).eval(es); - double x0 = args.get(1).eval(es); - double y0 = args.get(2).eval(es); - if (x < x0) - return y0; - double x1 = args.get(3).eval(es); - double y1 = args.get(4).eval(es); - int i = 5; - while (true) { - if (x < x1) - return y0+(x-x0)*(y1-y0)/(x1-x0); - if (i+1 >= args.size()) - break; - x0 = x1; - y0 = y1; - x1 = args.get(i ).eval(es); - y1 = args.get(i+1).eval(es); - i += 2; - } - return y1; + double x = args.get(0).eval(es); + double x0 = args.get(1).eval(es); + double y0 = args.get(2).eval(es); + if (x < x0) + return y0; + double x1 = args.get(3).eval(es); + double y1 = args.get(4).eval(es); + int i = 5; + while (true) { + if (x < x1) + return y0 + (x - x0) * (y1 - y0) / (x1 - x0); + if (i + 1 >= args.size()) + break; + x0 = x1; + y0 = y1; + x1 = args.get(i).eval(es); + y1 = args.get(i + 1).eval(es); + i += 2; + } + return y1; } double posmod(double x, double y) { - x %= y; - return (x >= 0) ? x : x+y; + x %= y; + return (x >= 0) ? x : x + y; } - + Vector children; double value; int type; @@ -227,11 +264,11 @@ class Expr { static final int E_COSH = 48; static final int E_TANH = 49; static final int E_A = 50; - static final int E_DADT = E_A+10; // must be E_A+10 - static final int E_LASTA = E_DADT+10; // should be at end and equal to E_DADT+10 + static final int E_DADT = E_A + 10; // must be E_A+10 + static final int E_LASTA = E_DADT + 10; // should be at end and equal to E_DADT+10 }; -class ExprParser { +public class ExprParser { String text; String token; int pos; @@ -239,302 +276,304 @@ class ExprParser { String err; void getToken() { - while (pos < tlen && text.charAt(pos) == ' ') - pos++; - if (pos == tlen) { - token = ""; - return; - } - int i = pos; - int c = text.charAt(i); - if ((c >= '0' && c <= '9') || c == '.') { - for (i = pos; i != tlen; i++) { - if (text.charAt(i) == 'e' || text.charAt(i) == 'E') { - i++; - if (i < tlen && (text.charAt(i) == '+' || text.charAt(i) == '-')) - i++; - } - if (!((text.charAt(i) >= '0' && text.charAt(i) <= '9') || - text.charAt(i) == '.')) - break; - } - } else if (c >= 'a' && c <= 'z') { - for (i = pos; i != tlen; i++) { - if (!(text.charAt(i) >= 'a' && text.charAt(i) <= 'z')) - break; - } - } else { - i++; - if (i < tlen) { - // ||, &&, <<, >>, == - if (text.charAt(i) == c && (c == '|' || c == '&' || c == '<' || c == '>' || c == '=')) - i++; - // <=, >= - else if ((c == '<' || c == '>' || c == '!') && text.charAt(i) == '=') - i++; - } - } - token = text.substring(pos, i); - pos = i; + while (pos < tlen && text.charAt(pos) == ' ') + pos++; + if (pos == tlen) { + token = ""; + return; + } + int i = pos; + int c = text.charAt(i); + if ((c >= '0' && c <= '9') || c == '.') { + for (i = pos; i != tlen; i++) { + if (text.charAt(i) == 'e' || text.charAt(i) == 'E') { + i++; + if (i < tlen && (text.charAt(i) == '+' || text.charAt(i) == '-')) + i++; + } + if (!((text.charAt(i) >= '0' && text.charAt(i) <= '9') || + text.charAt(i) == '.')) + break; + } + } else if (c >= 'a' && c <= 'z') { + for (i = pos; i != tlen; i++) { + if (!(text.charAt(i) >= 'a' && text.charAt(i) <= 'z')) + break; + } + } else { + i++; + if (i < tlen) { + // ||, &&, <<, >>, == + if (text.charAt(i) == c && (c == '|' || c == '&' || c == '<' || c == '>' || c == '=')) + i++; + // <=, >= + else if ((c == '<' || c == '>' || c == '!') && text.charAt(i) == '=') + i++; + } + } + token = text.substring(pos, i); + pos = i; } boolean skip(String s) { - if (token.compareTo(s) != 0) - return false; - getToken(); - return true; + if (token.compareTo(s) != 0) + return false; + getToken(); + return true; } void setError(String s) { - if (err == null) - err = s; + if (err == null) + err = s; } - + void skipOrError(String s) { - if (!skip(s)) { - setError("expected " + s + ", got " + token); - } + if (!skip(s)) { + setError("expected " + s + ", got " + token); + } } Expr parseExpression() { - if (token.length() == 0) - return new Expr(Expr.E_VAL, 0.); - Expr e = parse(); - if (token.length() > 0) - setError("unexpected token: " + token); - return e; + if (token.length() == 0) + return new Expr(Expr.E_VAL, 0.); + Expr e = parse(); + if (token.length() > 0) + setError("unexpected token: " + token); + return e; } Expr parse() { - Expr e = parseOr(); - Expr e2, e3; - if (skip("?")) { - e2 = parseOr(); - skipOrError(":"); - e3 = parse(); - Expr ret = new Expr(e, e2, Expr.E_TERNARY); - ret.children.add(e3); - return ret; - } - return e; + Expr e = parseOr(); + Expr e2, e3; + if (skip("?")) { + e2 = parseOr(); + skipOrError(":"); + e3 = parse(); + Expr ret = new Expr(e, e2, Expr.E_TERNARY); + ret.children.add(e3); + return ret; + } + return e; } - + Expr parseOr() { - Expr e = parseAnd(); - while (skip("||")) { - e = new Expr(e, parseAnd(), Expr.E_OR); - } - return e; + Expr e = parseAnd(); + while (skip("||")) { + e = new Expr(e, parseAnd(), Expr.E_OR); + } + return e; } - + Expr parseAnd() { - Expr e = parseEquals(); - while (skip("&&")) { - e = new Expr(e, parseEquals(), Expr.E_AND); - } - return e; + Expr e = parseEquals(); + while (skip("&&")) { + e = new Expr(e, parseEquals(), Expr.E_AND); + } + return e; } - + Expr parseEquals() { - Expr e = parseCompare(); - if (skip("==")) - return new Expr(e, parseCompare(), Expr.E_EQUALS); - return e; + Expr e = parseCompare(); + if (skip("==")) + return new Expr(e, parseCompare(), Expr.E_EQUALS); + return e; } - + Expr parseCompare() { - Expr e = parseAdd(); - if (skip("<=")) - return new Expr(e, parseAdd(), Expr.E_LEQ); - if (skip(">=")) - return new Expr(e, parseAdd(), Expr.E_GEQ); - if (skip("!=")) - return new Expr(e, parseAdd(), Expr.E_NEQ); - if (skip("<")) - return new Expr(e, parseAdd(), Expr.E_LESS); - if (skip(">")) - return new Expr(e, parseAdd(), Expr.E_GREATER); - return e; + Expr e = parseAdd(); + if (skip("<=")) + return new Expr(e, parseAdd(), Expr.E_LEQ); + if (skip(">=")) + return new Expr(e, parseAdd(), Expr.E_GEQ); + if (skip("!=")) + return new Expr(e, parseAdd(), Expr.E_NEQ); + if (skip("<")) + return new Expr(e, parseAdd(), Expr.E_LESS); + if (skip(">")) + return new Expr(e, parseAdd(), Expr.E_GREATER); + return e; } Expr parseAdd() { - Expr e = parseMult(); - while (true) { - if (skip("+")) - e = new Expr(e, parseMult(), Expr.E_ADD); - else if (skip("-")) - e = new Expr(e, parseMult(), Expr.E_SUB); - else - break; - } - return e; + Expr e = parseMult(); + while (true) { + if (skip("+")) + e = new Expr(e, parseMult(), Expr.E_ADD); + else if (skip("-")) + e = new Expr(e, parseMult(), Expr.E_SUB); + else + break; + } + return e; } Expr parseMult() { - Expr e = parseUminus(); - while (true) { - if (skip("*")) - e = new Expr(e, parseUminus(), Expr.E_MUL); - else if (skip("/")) - e = new Expr(e, parseUminus(), Expr.E_DIV); - else - break; - } - return e; + Expr e = parseUminus(); + while (true) { + if (skip("*")) + e = new Expr(e, parseUminus(), Expr.E_MUL); + else if (skip("/")) + e = new Expr(e, parseUminus(), Expr.E_DIV); + else + break; + } + return e; } Expr parseUminus() { - skip("+"); - if (skip("!")) - return new Expr(parseUminus(), null, Expr.E_NOT); - if (skip("-")) - return new Expr(parseUminus(), null, Expr.E_UMINUS); - return parsePow(); + skip("+"); + if (skip("!")) + return new Expr(parseUminus(), null, Expr.E_NOT); + if (skip("-")) + return new Expr(parseUminus(), null, Expr.E_UMINUS); + return parsePow(); } Expr parsePow() { - Expr e = parseTerm(); - while (true) { - if (skip("^")) - e = new Expr(e, parseTerm(), Expr.E_POW); - else - break; - } - return e; + Expr e = parseTerm(); + while (true) { + if (skip("^")) + e = new Expr(e, parseTerm(), Expr.E_POW); + else + break; + } + return e; } Expr parseFunc(int t) { - skipOrError("("); - Expr e = parse(); - skipOrError(")"); - return new Expr(e, null, t); + skipOrError("("); + Expr e = parse(); + skipOrError(")"); + return new Expr(e, null, t); } Expr parseFuncMulti(int t, int minArgs, int maxArgs) { - int args = 1; - skipOrError("("); - Expr e1 = parse(); - Expr e = new Expr(e1, null, t); - while (skip(",")) { - Expr enext = parse(); - e.children.add(enext); - args++; - } - skipOrError(")"); - if (args < minArgs || args > maxArgs) - setError("bad number of function args: " + args); - return e; + int args = 1; + skipOrError("("); + Expr e1 = parse(); + Expr e = new Expr(e1, null, t); + while (skip(",")) { + Expr enext = parse(); + e.children.add(enext); + args++; + } + skipOrError(")"); + if (args < minArgs || args > maxArgs) + setError("bad number of function args: " + args); + return e; } Expr parseTerm() { - if (skip("(")) { - Expr e = parse(); - skipOrError(")"); - return e; - } - if (skip("t")) - return new Expr(Expr.E_T); - if (token.length() == 1) { - char c = token.charAt(0); - if (c >= 'a' && c <= 'i') { - getToken(); - return new Expr(Expr.E_A + (c-'a')); - } - } - if (token.startsWith("last") && token.length() == 5) { - char c = token.charAt(4); - if (c >= 'a' && c <= 'i') { - getToken(); - return new Expr(Expr.E_LASTA + (c-'a')); - } - } - if (token.endsWith("dt") && token.startsWith("d") && token.length() == 4) { - char c = token.charAt(1); - if (c >= 'a' && c <= 'i') { - getToken(); - return new Expr(Expr.E_DADT + (c-'a')); - } - } - if (skip("lastoutput")) - return new Expr(Expr.E_LASTOUTPUT); - if (skip("timestep")) - return new Expr(Expr.E_TIMESTEP); - if (skip("pi")) - return new Expr(Expr.E_VAL, 3.14159265358979323846); + if (skip("(")) { + Expr e = parse(); + skipOrError(")"); + return e; + } + if (skip("t")) + return new Expr(Expr.E_T); + if (token.length() == 1) { + char c = token.charAt(0); + if (c >= 'a' && c <= 'i') { + getToken(); + return new Expr(Expr.E_A + (c - 'a')); + } + } + if (token.startsWith("last") && token.length() == 5) { + char c = token.charAt(4); + if (c >= 'a' && c <= 'i') { + getToken(); + return new Expr(Expr.E_LASTA + (c - 'a')); + } + } + if (token.endsWith("dt") && token.startsWith("d") && token.length() == 4) { + char c = token.charAt(1); + if (c >= 'a' && c <= 'i') { + getToken(); + return new Expr(Expr.E_DADT + (c - 'a')); + } + } + if (skip("lastoutput")) + return new Expr(Expr.E_LASTOUTPUT); + if (skip("timestep")) + return new Expr(Expr.E_TIMESTEP); + if (skip("pi")) + return new Expr(Expr.E_VAL, 3.14159265358979323846); // if (skip("e")) // return new Expr(Expr.E_VAL, 2.7182818284590452354); - if (skip("sin")) - return parseFunc(Expr.E_SIN); - if (skip("cos")) - return parseFunc(Expr.E_COS); - if (skip("asin")) - return parseFunc(Expr.E_ASIN); - if (skip("acos")) - return parseFunc(Expr.E_ACOS); - if (skip("atan")) - return parseFunc(Expr.E_ATAN); - if (skip("sinh")) - return parseFunc(Expr.E_SINH); - if (skip("cosh")) - return parseFunc(Expr.E_COSH); - if (skip("tanh")) - return parseFunc(Expr.E_TANH); - if (skip("abs")) - return parseFunc(Expr.E_ABS); - if (skip("exp")) - return parseFunc(Expr.E_EXP); - if (skip("log")) - return parseFunc(Expr.E_LOG); - if (skip("sqrt")) - return parseFunc(Expr.E_SQRT); - if (skip("tan")) - return parseFunc(Expr.E_TAN); - if (skip("tri")) - return parseFunc(Expr.E_TRIANGLE); - if (skip("saw")) - return parseFunc(Expr.E_SAWTOOTH); - if (skip("floor")) - return parseFunc(Expr.E_FLOOR); - if (skip("ceil")) - return parseFunc(Expr.E_CEIL); - if (skip("min")) - return parseFuncMulti(Expr.E_MIN, 2, 1000); - if (skip("max")) - return parseFuncMulti(Expr.E_MAX, 2, 1000); - if (skip("pwl")) - return parseFuncMulti(Expr.E_PWL, 2, 1000); - if (skip("mod")) - return parseFuncMulti(Expr.E_MOD, 2, 2); - if (skip("step")) - return parseFuncMulti(Expr.E_STEP, 1, 2); - if (skip("select")) - return parseFuncMulti(Expr.E_SELECT, 3, 3); - if (skip("clamp")) - return parseFuncMulti(Expr.E_CLAMP, 3, 3); - if (skip("pwr")) - return parseFuncMulti(Expr.E_PWR, 2, 2); - if (skip("pwrs")) - return parseFuncMulti(Expr.E_PWRS, 2, 2); - try { - Expr e = new Expr(Expr.E_VAL, Double.valueOf(token).doubleValue()); - getToken(); - return e; - } catch (Exception e) { - if (token.length() == 0) - setError("unexpected end of input"); - else - setError("unrecognized token: " + token); - return new Expr(Expr.E_VAL, 0); - } + if (skip("sin")) + return parseFunc(Expr.E_SIN); + if (skip("cos")) + return parseFunc(Expr.E_COS); + if (skip("asin")) + return parseFunc(Expr.E_ASIN); + if (skip("acos")) + return parseFunc(Expr.E_ACOS); + if (skip("atan")) + return parseFunc(Expr.E_ATAN); + if (skip("sinh")) + return parseFunc(Expr.E_SINH); + if (skip("cosh")) + return parseFunc(Expr.E_COSH); + if (skip("tanh")) + return parseFunc(Expr.E_TANH); + if (skip("abs")) + return parseFunc(Expr.E_ABS); + if (skip("exp")) + return parseFunc(Expr.E_EXP); + if (skip("log")) + return parseFunc(Expr.E_LOG); + if (skip("sqrt")) + return parseFunc(Expr.E_SQRT); + if (skip("tan")) + return parseFunc(Expr.E_TAN); + if (skip("tri")) + return parseFunc(Expr.E_TRIANGLE); + if (skip("saw")) + return parseFunc(Expr.E_SAWTOOTH); + if (skip("floor")) + return parseFunc(Expr.E_FLOOR); + if (skip("ceil")) + return parseFunc(Expr.E_CEIL); + if (skip("min")) + return parseFuncMulti(Expr.E_MIN, 2, 1000); + if (skip("max")) + return parseFuncMulti(Expr.E_MAX, 2, 1000); + if (skip("pwl")) + return parseFuncMulti(Expr.E_PWL, 2, 1000); + if (skip("mod")) + return parseFuncMulti(Expr.E_MOD, 2, 2); + if (skip("step")) + return parseFuncMulti(Expr.E_STEP, 1, 2); + if (skip("select")) + return parseFuncMulti(Expr.E_SELECT, 3, 3); + if (skip("clamp")) + return parseFuncMulti(Expr.E_CLAMP, 3, 3); + if (skip("pwr")) + return parseFuncMulti(Expr.E_PWR, 2, 2); + if (skip("pwrs")) + return parseFuncMulti(Expr.E_PWRS, 2, 2); + try { + Expr e = new Expr(Expr.E_VAL, Double.valueOf(token).doubleValue()); + getToken(); + return e; + } catch (Exception e) { + if (token.length() == 0) + setError("unexpected end of input"); + else + setError("unrecognized token: " + token); + return new Expr(Expr.E_VAL, 0); + } } ExprParser(String s) { - text = s.toLowerCase(); - tlen = text.length(); - pos = 0; - err = null; - getToken(); + text = s.toLowerCase(); + tlen = text.length(); + pos = 0; + err = null; + getToken(); + } + + String gotError() { + return err; } - - String gotError() { return err; } }; diff --git a/src/main/java/com/lushprojects/circuitjs1/client/ExtVoltageElm.java b/src/main/java/com/lushprojects/circuitjs1/client/ExtVoltageElm.java index 9dd4afa..6f1dbdf 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/ExtVoltageElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/ExtVoltageElm.java @@ -21,48 +21,66 @@ import com.lushprojects.circuitjs1.client.util.Locale; -class ExtVoltageElm extends RailElm { - public ExtVoltageElm(int xx, int yy) { super(xx, yy, WF_AC); name = "ext"; } - public ExtVoltageElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f, st); - name = CustomLogicModel.unescape(st.nextToken()); - waveform = WF_AC; - } - - String name; - double voltage; - - String dump() { return super.dump() + " " + CustomLogicModel.escape(name); } - - void drawRail(Graphics g) { - drawRailText(g, name); - } - void setVoltage(double v) { if (!Double.isNaN(v)) voltage = v; } - String getName() { return name; } - - double getVoltage() { - return voltage; - } - - int getDumpType() { return 418; } - int getShortcut() { return 0; } - - public EditInfo getEditInfo(int n) { - if (n == 0) { - EditInfo ei = new EditInfo("Name", 0, -1, -1); - ei.text = name; - return ei; - } - return null; - } - public void setEditValue(int n, EditInfo ei) { - if (n == 0) - name = ei.textf.getText(); - } - - void getInfo(String arr[]) { - super.getInfo(arr); - arr[0] = Locale.LS("ext. voltage") + " (" + name + ")"; - } +public class ExtVoltageElm extends RailElm { + public ExtVoltageElm(int xx, int yy) { + super(xx, yy, WF_AC); + name = "ext"; } + + public ExtVoltageElm(int xa, int ya, int xb, int yb, int f, + StringTokenizer st) { + super(xa, ya, xb, yb, f, st); + name = CustomLogicModel.unescape(st.nextToken()); + waveform = WF_AC; + } + + String name; + double voltage; + + String dump() { + return super.dump() + " " + CustomLogicModel.escape(name); + } + + void drawRail(Graphics g) { + drawRailText(g, name); + } + + void setVoltage(double v) { + if (!Double.isNaN(v)) voltage = v; + } + + String getName() { + return name; + } + + double getVoltage() { + return voltage; + } + + int getDumpType() { + return 418; + } + + int getShortcut() { + return 0; + } + + public EditInfo getEditInfo(int n) { + if (n == 0) { + EditInfo ei = new EditInfo("Name", 0, -1, -1); + ei.text = name; + return ei; + } + return null; + } + + public void setEditValue(int n, EditInfo ei) { + if (n == 0) + name = ei.textf.getText(); + } + + void getInfo(String arr[]) { + super.getInfo(arr); + arr[0] = Locale.LS("ext. voltage") + " (" + name + ")"; + } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/FFT.java b/src/main/java/com/lushprojects/circuitjs1/client/FFT.java index e9243a3..071214d 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/FFT.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/FFT.java @@ -19,77 +19,79 @@ package com.lushprojects.circuitjs1.client; -class FFT { +public class FFT { private int size; private int bits; private double[] cosTable; private double[] sinTable; FFT(int n) { - size = n; - bits = (int) (Math.log(size) / Math.log(2)); - cosTable = new double[size / 2]; - sinTable = new double[size / 2]; - double dtheta = -2 * Math.PI / size; - for (int i = 0; i < cosTable.length; i++) { - cosTable[i] = Math.cos(dtheta * i); - sinTable[i] = Math.sin(dtheta * i); - } + size = n; + bits = (int) (Math.log(size) / Math.log(2)); + cosTable = new double[size / 2]; + sinTable = new double[size / 2]; + double dtheta = -2 * Math.PI / size; + for (int i = 0; i < cosTable.length; i++) { + cosTable[i] = Math.cos(dtheta * i); + sinTable[i] = Math.sin(dtheta * i); + } } /* * This uses the radix-2 decimation-in-time FFT algorithm. * Based on * http://www.ee.columbia.edu/~ronw/code/MEAPsoft/doc/html/FFT_8java-source.html - * Douglas L. Jones - * University of Illinois at Urbana-Champaign - * January 19, 1992 + * Douglas L. Jones + * University of Illinois at Urbana-Champaign + * January 19, 1992 * http://cnx.rice.edu/content/m12016/latest/ */ void fft(double[] real, double[] imag) { int j = 0; int n2 = real.length / 2; - for (int i=1; i < real.length - 1; i++) { - int n1 = n2; - while (j >= n1) { - j -= n1; - n1 /= 2; - } - j += n1; - if (i < j) { - double t1 = real[i]; - real[i] = real[j]; - real[j] = t1; - t1 = imag[i]; - imag[i] = imag[j]; - imag[j] = t1; - } + for (int i = 1; i < real.length - 1; i++) { + int n1 = n2; + while (j >= n1) { + j -= n1; + n1 /= 2; + } + j += n1; + if (i < j) { + double t1 = real[i]; + real[i] = real[j]; + real[j] = t1; + t1 = imag[i]; + imag[i] = imag[j]; + imag[j] = t1; + } } n2 = 1; for (int i = 0; i < bits; i++) { - int n1 = n2; - n2 <<= 1; - int a = 0; - for (j = 0; j < n1; j++) { - double c = cosTable[a]; - double s = sinTable[a]; - a += 1 << (bits - i - 1); - for (int k = j; k < real.length; k += n2) { - int t = k + n1; - double t1 = c * real[t] - s * imag[t]; - double t2 = s * real[t] + c * imag[t]; - real[k+n1] = real[k] - t1; - imag[k+n1] = imag[k] - t2; - real[k] += t1; - imag[k] += t2; + int n1 = n2; + n2 <<= 1; + int a = 0; + for (j = 0; j < n1; j++) { + double c = cosTable[a]; + double s = sinTable[a]; + a += 1 << (bits - i - 1); + for (int k = j; k < real.length; k += n2) { + int t = k + n1; + double t1 = c * real[t] - s * imag[t]; + double t2 = s * real[t] + c * imag[t]; + real[k + n1] = real[k] - t1; + imag[k + n1] = imag[k] - t2; + real[k] += t1; + imag[k] += t2; + } } - } } } - int getSize() { return size; } - + int getSize() { + return size; + } + double magnitude(double real, double imag) { - return Math.sqrt(real * real + imag * imag) / size; + return Math.sqrt(real * real + imag * imag) / size; } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/FMElm.java b/src/main/java/com/lushprojects/circuitjs1/client/FMElm.java index 3995cdf..58feada 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/FMElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/FMElm.java @@ -21,34 +21,40 @@ // contributed by Edward Calver -class FMElm extends CircuitElm { +public class FMElm extends CircuitElm { static final int FLAG_COS = 2; - double carrierfreq,signalfreq, maxVoltage, freqTimeZero,deviation; - double lasttime=0; - double funcx=0; + double carrierfreq, signalfreq, maxVoltage, freqTimeZero, deviation; + double lasttime = 0; + double funcx = 0; + public FMElm(int xx, int yy) { - super(xx, yy); - deviation=200; - maxVoltage = 5; - carrierfreq =800; - signalfreq=40; - reset(); + super(xx, yy); + deviation = 200; + maxVoltage = 5; + carrierfreq = 800; + signalfreq = 40; + reset(); } + public FMElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f); - carrierfreq = new Double(st.nextToken()).doubleValue(); - signalfreq= new Double(st.nextToken()).doubleValue(); - maxVoltage = new Double(st.nextToken()).doubleValue(); - deviation = new Double(st.nextToken()).doubleValue(); - if ((flags & FLAG_COS) != 0) { - flags &= ~FLAG_COS; - } - reset(); - } - int getDumpType() { return 201; } + StringTokenizer st) { + super(xa, ya, xb, yb, f); + carrierfreq = new Double(st.nextToken()).doubleValue(); + signalfreq = new Double(st.nextToken()).doubleValue(); + maxVoltage = new Double(st.nextToken()).doubleValue(); + deviation = new Double(st.nextToken()).doubleValue(); + if ((flags & FLAG_COS) != 0) { + flags &= ~FLAG_COS; + } + reset(); + } + + int getDumpType() { + return 201; + } + String dump() { - return super.dump() + " " +carrierfreq+" " + signalfreq + " " +maxVoltage + " " +deviation; + return super.dump() + " " + carrierfreq + " " + signalfreq + " " + maxVoltage + " " + deviation; } /*void setCurrent(double c) { current = c; @@ -56,101 +62,118 @@ String dump() { }*/ void reset() { - freqTimeZero = 0; - curcount = 0; + freqTimeZero = 0; + curcount = 0; + } + + int getPostCount() { + return 1; } - int getPostCount() { return 1; } - - void stamp() { - sim.stampVoltageSource(0, nodes[0], voltSource); + + void stamp() { + sim.stampVoltageSource(0, nodes[0], voltSource); } + void doStep() { - sim.updateVoltageSource(0, nodes[0], voltSource, getVoltage()); + sim.updateVoltageSource(0, nodes[0], voltSource, getVoltage()); } + double getVoltage() { - double deltaT=sim.t-lasttime; - lasttime=sim.t; - double signalamplitude=Math.sin((2*pi*(sim.t-freqTimeZero))*signalfreq); - funcx+=deltaT*(carrierfreq+(signalamplitude*deviation)); - double w = 2*pi*funcx; - return Math.sin(w)*maxVoltage; + double deltaT = sim.t - lasttime; + lasttime = sim.t; + double signalamplitude = Math.sin((2 * pi * (sim.t - freqTimeZero)) * signalfreq); + funcx += deltaT * (carrierfreq + (signalamplitude * deviation)); + double w = 2 * pi * funcx; + return Math.sin(w) * maxVoltage; } + final int circleSize = 17; void draw(Graphics g) { - setBbox(point1, point2, circleSize); - setVoltageColor(g, volts[0]); - drawThickLine(g, point1, lead1); - - Font f = new Font("SansSerif", 0, 12); - g.setFont(f); - g.setColor(needsHighlight() ? selectColor : whiteColor); - setPowerColor(g, false); - double v = getVoltage(); - String s = "FM"; - drawCenteredText(g, s, x2, y2, true); - drawWaveform(g, point2); - drawPosts(g); - curcount = updateDotCount(-current, curcount); - if (sim.dragElm != this) - drawDots(g, point1, lead1, curcount); - } - + setBbox(point1, point2, circleSize); + setVoltageColor(g, volts[0]); + drawThickLine(g, point1, lead1); + + Font f = new Font("SansSerif", 0, 12); + g.setFont(f); + g.setColor(needsHighlight() ? selectColor : whiteColor); + setPowerColor(g, false); + double v = getVoltage(); + String s = "FM"; + drawCenteredText(g, s, x2, y2, true); + drawWaveform(g, point2); + drawPosts(g); + curcount = updateDotCount(-current, curcount); + if (sim.dragElm != this) + drawDots(g, point1, lead1, curcount); + } + void drawWaveform(Graphics g, Point center) { - g.setColor(needsHighlight() ? selectColor : Color.gray); - setPowerColor(g, false); - int xc = center.x; int yc = center.y; - drawThickCircle(g, xc, yc, circleSize); - int wl = 8; - adjustBbox(xc-circleSize, yc-circleSize, - xc+circleSize, yc+circleSize); + g.setColor(needsHighlight() ? selectColor : Color.gray); + setPowerColor(g, false); + int xc = center.x; + int yc = center.y; + drawThickCircle(g, xc, yc, circleSize); + int wl = 8; + adjustBbox(xc - circleSize, yc - circleSize, + xc + circleSize, yc + circleSize); } - void setPoints() { - super.setPoints(); - lead1 = interpPoint(point1, point2, 1-circleSize/dn); + void setPoints() { + super.setPoints(); + lead1 = interpPoint(point1, point2, 1 - circleSize / dn); } - - double getVoltageDiff() { return volts[0]; } - - boolean hasGroundConnection(int n1) { return true; } - + + double getVoltageDiff() { + return volts[0]; + } + + boolean hasGroundConnection(int n1) { + return true; + } + int getVoltageSourceCount() { - return 1; + return 1; } - double getPower() { return -getVoltageDiff()*current; } + + double getPower() { + return -getVoltageDiff() * current; + } + void getInfo(String arr[]) { - - arr[0] = "FM Source"; - arr[1] = "I = " + getCurrentText(getCurrent()); - arr[2] = "V = " + - getVoltageText(getVoltageDiff()); - arr[3] = "cf = " + getUnitText(carrierfreq, "Hz"); - arr[4] = "sf = " + getUnitText(signalfreq, "Hz"); - arr[5]= "dev =" + getUnitText(deviation, "Hz"); - arr[6] = "Vmax = " + getVoltageText(maxVoltage); + + arr[0] = "FM Source"; + arr[1] = "I = " + getCurrentText(getCurrent()); + arr[2] = "V = " + + getVoltageText(getVoltageDiff()); + arr[3] = "cf = " + getUnitText(carrierfreq, "Hz"); + arr[4] = "sf = " + getUnitText(signalfreq, "Hz"); + arr[5] = "dev =" + getUnitText(deviation, "Hz"); + arr[6] = "Vmax = " + getVoltageText(maxVoltage); } + public EditInfo getEditInfo(int n) { - if (n == 0) - return new EditInfo("Max Voltage", maxVoltage, -20, 20); - if (n == 1) - return new EditInfo("Carrier Frequency (Hz)", carrierfreq, 4, 500); - if (n == 2) - return new EditInfo("Signal Frequency (Hz)", signalfreq, 4, 500); - if (n == 3) - return new EditInfo("Deviation (Hz)", deviation, 4, 500); - - return null; + if (n == 0) + return new EditInfo("Max Voltage", maxVoltage, -20, 20); + if (n == 1) + return new EditInfo("Carrier Frequency (Hz)", carrierfreq, 4, 500); + if (n == 2) + return new EditInfo("Signal Frequency (Hz)", signalfreq, 4, 500); + if (n == 3) + return new EditInfo("Deviation (Hz)", deviation, 4, 500); + + return null; } + public void setEditValue(int n, EditInfo ei) { - if (n == 0) - maxVoltage = ei.value; - if (n == 1) - carrierfreq = ei.value; - if (n == 2) - signalfreq=ei.value; - if (n == 3) - deviation=ei.value; + if (n == 0) + maxVoltage = ei.value; + if (n == 1) + carrierfreq = ei.value; + if (n == 2) + signalfreq = ei.value; + if (n == 3) + deviation = ei.value; } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/Font.java b/src/main/java/com/lushprojects/circuitjs1/client/Font.java index f7d332e..3fd8d01 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/Font.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/Font.java @@ -20,20 +20,19 @@ package com.lushprojects.circuitjs1.client; +public class Font { + static final int BOLD = 1; -class Font { - static final int BOLD=1; - - String fontname; - int size; - - public Font(String name, int style, int size){ - String styleStr="normal "; - if (name=="SansSerif") - name="sans-serif"; - if ((style & BOLD) !=0) - styleStr="bold "; - fontname=styleStr+size+"px "+name; - this.size=size; - } + String fontname; + int size; + + public Font(String name, int style, int size) { + String styleStr = "normal "; + if (name == "SansSerif") + name = "sans-serif"; + if ((style & BOLD) != 0) + styleStr = "bold "; + fontname = styleStr + size + "px " + name; + this.size = size; + } } \ No newline at end of file diff --git a/src/main/java/com/lushprojects/circuitjs1/client/FullAdderElm.java b/src/main/java/com/lushprojects/circuitjs1/client/FullAdderElm.java index c3e41a6..7b9beab 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/FullAdderElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/FullAdderElm.java @@ -19,77 +19,93 @@ package com.lushprojects.circuitjs1.client; - class FullAdderElm extends ChipElm { - public FullAdderElm(int xx, int yy) { - super(xx, yy); - flags |= FLAG_BITS; - bits = 4; - setupPins(); - } - public FullAdderElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f, st); - if (!needsBits()) - bits = 1; - setupPins(); - } - static final int FLAG_BITS = 2; - - String getChipName() { return "Adder"; } - int carryIn, carryOut; - - void setupPins() { - sizeX=2; - sizeY=bits*2+1; - pins=new Pin[getPostCount()]; - - int i; - for (i = 0; i != bits; i++) { - pins[i ] = new Pin(bits-1-i, SIDE_W, "A" + i); - pins[i+bits ] = new Pin(bits-1-i+bits, SIDE_W, "B" + i); - pins[i+bits*2] = new Pin(bits-1-i+2, SIDE_E, "S" + i); - pins[i+bits*2].output=true; - } - carryIn = bits*3; - carryOut = bits*3+1; - pins[carryOut] = new Pin(0, SIDE_E, "C"); - pins[carryOut].output=true; - pins[carryIn] = new Pin(bits*2, SIDE_W, "Cin"); - allocNodes(); - } - int getPostCount() { - return bits*3+2; - } - int getVoltageSourceCount() { return bits+1; } - - void execute() { - int i; - int c = pins[carryIn].value ? 1 : 0; - for (i = 0; i != bits; i++) { - int v = (pins[i].value ? 1 : 0) + (pins[i+bits].value ? 1 : 0) + c; - c = (v > 1) ? 1 : 0; - writeOutput(i+bits*2, ((v & 1) == 1)); - } - writeOutput(carryOut, (c == 1)); - } - int getDumpType() { return 196; } - boolean needsBits() { return (flags & FLAG_BITS) != 0; } - - public EditInfo getChipEditInfo(int n) { - if (n == 0) - return new EditInfo("# of Bits", bits, 1, 1).setDimensionless(); - return super.getChipEditInfo(n); +public class FullAdderElm extends ChipElm { + public FullAdderElm(int xx, int yy) { + super(xx, yy); + flags |= FLAG_BITS; + bits = 4; + setupPins(); + } + + public FullAdderElm(int xa, int ya, int xb, int yb, int f, + StringTokenizer st) { + super(xa, ya, xb, yb, f, st); + if (!needsBits()) + bits = 1; + setupPins(); + } + + static final int FLAG_BITS = 2; + + String getChipName() { + return "Adder"; + } + + int carryIn, carryOut; + + void setupPins() { + sizeX = 2; + sizeY = bits * 2 + 1; + pins = new Pin[getPostCount()]; + + int i; + for (i = 0; i != bits; i++) { + pins[i] = new Pin(bits - 1 - i, SIDE_W, "A" + i); + pins[i + bits] = new Pin(bits - 1 - i + bits, SIDE_W, "B" + i); + pins[i + bits * 2] = new Pin(bits - 1 - i + 2, SIDE_E, "S" + i); + pins[i + bits * 2].output = true; } - public void setChipEditValue(int n, EditInfo ei) { - if (n == 0 && ei.value > 0) { - bits = (int)ei.value; - flags |= FLAG_BITS; - setupPins(); - setPoints(); - allocNodes(); - return; - } - super.setChipEditValue(n, ei); + carryIn = bits * 3; + carryOut = bits * 3 + 1; + pins[carryOut] = new Pin(0, SIDE_E, "C"); + pins[carryOut].output = true; + pins[carryIn] = new Pin(bits * 2, SIDE_W, "Cin"); + allocNodes(); + } + + int getPostCount() { + return bits * 3 + 2; + } + + int getVoltageSourceCount() { + return bits + 1; + } + + void execute() { + int i; + int c = pins[carryIn].value ? 1 : 0; + for (i = 0; i != bits; i++) { + int v = (pins[i].value ? 1 : 0) + (pins[i + bits].value ? 1 : 0) + c; + c = (v > 1) ? 1 : 0; + writeOutput(i + bits * 2, ((v & 1) == 1)); } + writeOutput(carryOut, (c == 1)); + } + + int getDumpType() { + return 196; + } + boolean needsBits() { + return (flags & FLAG_BITS) != 0; } + + public EditInfo getChipEditInfo(int n) { + if (n == 0) + return new EditInfo("# of Bits", bits, 1, 1).setDimensionless(); + return super.getChipEditInfo(n); + } + + public void setChipEditValue(int n, EditInfo ei) { + if (n == 0 && ei.value > 0) { + bits = (int) ei.value; + flags |= FLAG_BITS; + setupPins(); + setPoints(); + allocNodes(); + return; + } + super.setChipEditValue(n, ei); + } + +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/FuseElm.java b/src/main/java/com/lushprojects/circuitjs1/client/FuseElm.java index f283986..5165306 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/FuseElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/FuseElm.java @@ -21,155 +21,161 @@ import com.lushprojects.circuitjs1.client.util.Locale; -class FuseElm extends CircuitElm { - double resistance; - double heat; - double i2t; - boolean blown; - final int FLAG_IEC_SYMBOL = 1; - final double blownResistance = 1e9; - public FuseElm(int xx, int yy) { - super(xx, yy); - // from https://m.littelfuse.com/~/media/electronics/datasheets/fuses/littelfuse_fuse_218_datasheet.pdf.pdf - i2t = 6.73; - resistance = .0613; - } - public FuseElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f); - resistance = new Double(st.nextToken()).doubleValue(); - i2t = new Double(st.nextToken()).doubleValue(); - heat = new Double(st.nextToken()).doubleValue(); - blown = new Boolean(st.nextToken()).booleanValue(); - } - String dump() { - return super.dump() + " " + resistance + " " + i2t + " " + heat + " " + blown; - } - int getDumpType() { return 404; } - - boolean isIECSymbol() { return (flags & FLAG_IEC_SYMBOL) != 0; } - - void reset() { - super.reset(); - heat = 0; - blown = false; - } - void setPoints() { - super.setPoints(); - int llen = isIECSymbol() ? 32 : 16; - calcLeads(llen); - } - - Color getTempColor(Graphics g) { - Color c = getVoltageColor(g, volts[0]); - double temp = heat/i2t; - if (temp < .3333) { - double val = temp*3; - int x = (int) (255*val); - if (x < 0) - x = 0; - return new Color(x+(255-x)*c.getRed()/255, (255-x)*c.getGreen()/255, (255-x)*c.getBlue()/255); - } - if (temp < .6667) { - int x = (int) ((temp-.3333)*3*255); - if (x < 0) - x = 0; - return new Color(255, x, 0); - } - if (temp < 1) { - int x = (int) ((temp-.6666)*3*255); - if (x < 0) - x = 0; - return new Color(255, 255, x); - } - return Color.white; - } - - void draw(Graphics g) { - int segments = 16; - int i; - int hs=6; - setBbox(point1, point2, hs); - draw2Leads(g); - - // double segf = 1./segments; - double len = distance(lead1, lead2); - g.context.save(); - g.context.setLineWidth(3.0); - g.context.transform(((double)(lead2.x-lead1.x))/len, ((double)(lead2.y-lead1.y))/len, -((double)(lead2.y-lead1.y))/len,((double)(lead2.x-lead1.x))/len,lead1.x,lead1.y); - g.context.setStrokeStyle(getTempColor(g).getHexValue()); - if (!isIECSymbol()) { - if (!blown) { - g.context.beginPath(); - g.context.moveTo(0,0); - for (i = 0; i <= segments; i++) - g.context.lineTo(i*len/segments, hs*Math.sin(i*Math.PI*2/segments)); - g.context.stroke(); - } - } else { - if (!blown) { - g.context.beginPath(); - g.context.moveTo(0, 0); - g.context.lineTo(len, 0); - g.context.stroke(); - g.context.strokeRect(0, -hs, len, 2.0*hs); - } - } - g.context.restore(); - doDots(g); - drawPosts(g); - } - - void calculateCurrent() { - current = (volts[0]-volts[1])/(blown ? blownResistance : resistance); - } - void stamp() { - sim.stampNonLinear(nodes[0]); - sim.stampNonLinear(nodes[1]); - } - boolean nonLinear() { return true; } - void startIteration() { - double i = getCurrent(); - - // accumulate heat - heat += i*i*sim.timeStep; - - // dissipate heat. we assume the fuse can dissipate its entire i2t in 3 seconds - heat -= sim.timeStep*i2t/3; - - if (heat < 0) - heat = 0; - if (heat > i2t) - blown = true; - } - void doStep() { - sim.stampResistor(nodes[0], nodes[1], blown ? blownResistance : resistance); - } - void getInfo(String arr[]) { - arr[0] = blown ? "fuse (blown)" : "fuse"; - getBasicInfo(arr); - arr[3] = "R = " + getUnitText(resistance, Locale.ohmString); - arr[4] = "I2t = " + i2t; - if (!blown) - arr[5] = ((int)(heat*100/i2t)) + "% " + Locale.LS("melted"); - } - public EditInfo getEditInfo(int n) { - if (n == 0) - return new EditInfo("I2t", i2t, 0, 0); - if (n == 1) - return new EditInfo("Resistance", resistance, 0, 0); - if (n == 2) - return EditInfo.createCheckbox("IEC Symbol", isIECSymbol()); - return null; - } - public void setEditValue(int n, EditInfo ei) { - if (n == 0 && ei.value > 0) - i2t = ei.value; - if (n == 1 && ei.value > 0) - resistance = ei.value; - if (n == 2) { - flags = ei.changeFlag(flags, FLAG_IEC_SYMBOL); - setPoints(); - } - } +public class FuseElm extends CircuitElm { + double resistance; + double heat; + double i2t; + boolean blown; + final int FLAG_IEC_SYMBOL = 1; + final double blownResistance = 1e9; + + public FuseElm(int xx, int yy) { + super(xx, yy); + // from https://m.littelfuse.com/~/media/electronics/datasheets/fuses/littelfuse_fuse_218_datasheet.pdf.pdf + i2t = 6.73; + resistance = .0613; + } + + public FuseElm(int xa, int ya, int xb, int yb, int f, StringTokenizer st) { + super(xa, ya, xb, yb, f); + resistance = new Double(st.nextToken()).doubleValue(); + i2t = new Double(st.nextToken()).doubleValue(); + heat = new Double(st.nextToken()).doubleValue(); + blown = new Boolean(st.nextToken()).booleanValue(); + } + + String dump() { + return super.dump() + " " + resistance + " " + i2t + " " + heat + " " + blown; + } + + int getDumpType() { + return 404; + } + + boolean isIECSymbol() { + return (flags & FLAG_IEC_SYMBOL) != 0; + } + + void reset() { + super.reset(); + heat = 0; + blown = false; + } + + void setPoints() { + super.setPoints(); + int llen = isIECSymbol() ? 32 : 16; + calcLeads(llen); + } + + Color getTempColor(Graphics g) { + Color c = getVoltageColor(g, volts[0]); + double temp = heat / i2t; + if (temp < .3333) { + double val = temp * 3; + int x = (int) (255 * val); + if (x < 0) x = 0; + return new Color(x + (255 - x) * c.getRed() / 255, (255 - x) * c.getGreen() / 255, (255 - x) * c.getBlue() / 255); + } + if (temp < .6667) { + int x = (int) ((temp - .3333) * 3 * 255); + if (x < 0) x = 0; + return new Color(255, x, 0); + } + if (temp < 1) { + int x = (int) ((temp - .6666) * 3 * 255); + if (x < 0) x = 0; + return new Color(255, 255, x); + } + return Color.white; + } + + void draw(Graphics g) { + int segments = 16; + int i; + int hs = 6; + setBbox(point1, point2, hs); + draw2Leads(g); + + // double segf = 1./segments; + double len = distance(lead1, lead2); + g.context.save(); + g.context.setLineWidth(3.0); + g.context.transform(((double) (lead2.x - lead1.x)) / len, ((double) (lead2.y - lead1.y)) / len, -((double) (lead2.y - lead1.y)) / len, ((double) (lead2.x - lead1.x)) / len, lead1.x, lead1.y); + g.context.setStrokeStyle(getTempColor(g).getHexValue()); + if (!isIECSymbol()) { + if (!blown) { + g.context.beginPath(); + g.context.moveTo(0, 0); + for (i = 0; i <= segments; i++) + g.context.lineTo(i * len / segments, hs * Math.sin(i * Math.PI * 2 / segments)); + g.context.stroke(); + } + } else { + if (!blown) { + g.context.beginPath(); + g.context.moveTo(0, 0); + g.context.lineTo(len, 0); + g.context.stroke(); + g.context.strokeRect(0, -hs, len, 2.0 * hs); + } + } + g.context.restore(); + doDots(g); + drawPosts(g); + } + + void calculateCurrent() { + current = (volts[0] - volts[1]) / (blown ? blownResistance : resistance); + } + + void stamp() { + sim.stampNonLinear(nodes[0]); + sim.stampNonLinear(nodes[1]); + } + + boolean nonLinear() { + return true; + } + + void startIteration() { + double i = getCurrent(); + + // accumulate heat + heat += i * i * sim.timeStep; + + // dissipate heat. we assume the fuse can dissipate its entire i2t in 3 seconds + heat -= sim.timeStep * i2t / 3; + + if (heat < 0) heat = 0; + if (heat > i2t) blown = true; + } + + void doStep() { + sim.stampResistor(nodes[0], nodes[1], blown ? blownResistance : resistance); + } + + void getInfo(String arr[]) { + arr[0] = blown ? "fuse (blown)" : "fuse"; + getBasicInfo(arr); + arr[3] = "R = " + getUnitText(resistance, Locale.ohmString); + arr[4] = "I2t = " + i2t; + if (!blown) arr[5] = ((int) (heat * 100 / i2t)) + "% " + Locale.LS("melted"); + } + + public EditInfo getEditInfo(int n) { + if (n == 0) return new EditInfo("I2t", i2t, 0, 0); + if (n == 1) return new EditInfo("Resistance", resistance, 0, 0); + if (n == 2) return EditInfo.createCheckbox("IEC Symbol", isIECSymbol()); + return null; + } + + public void setEditValue(int n, EditInfo ei) { + if (n == 0 && ei.value > 0) i2t = ei.value; + if (n == 1 && ei.value > 0) resistance = ei.value; + if (n == 2) { + flags = ei.changeFlag(flags, FLAG_IEC_SYMBOL); + setPoints(); + } } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/GateElm.java b/src/main/java/com/lushprojects/circuitjs1/client/GateElm.java index 36c7d01..2d88bf5 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/GateElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/GateElm.java @@ -19,242 +19,280 @@ package com.lushprojects.circuitjs1.client; - abstract class GateElm extends CircuitElm { - final int FLAG_SMALL = 1<<0; - final int FLAG_SCHMITT = 1<<1; - final int FLAG_INVERT_INPUTS = 1<<2; - int inputCount = 2; - boolean lastOutput; - double highVoltage; - public static double lastHighVoltage = 5; - static boolean lastSchmitt = false; - - public GateElm(int xx, int yy) { - super(xx, yy); - noDiagonal = true; - inputCount = 2; - - // copy defaults from last gate edited - highVoltage = lastHighVoltage; - if (lastSchmitt) - flags |= FLAG_SCHMITT; - - setSize(sim.smallGridCheckItem.getState() ? 1 : 2); - } - public GateElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f); - inputCount = new Integer(st.nextToken()).intValue(); - double lastOutputVoltage = new Double (st.nextToken()).doubleValue(); - noDiagonal = true; - highVoltage = 5; - try { - highVoltage = new Double(st.nextToken()).doubleValue(); - } catch (Exception e) { } - lastOutput = lastOutputVoltage > highVoltage*.5; - setSize((f & FLAG_SMALL) != 0 ? 1 : 2); - allocNodes(); - } - boolean isInverting() { return false; } - int gsize, gwidth, gwidth2, gheight, hs2; - void setSize(int s) { - gsize = s; - gwidth = 7*s; - gwidth2 = 14*s; - gheight = 8*s; - flags &= ~FLAG_SMALL; - flags |= (s == 1) ? FLAG_SMALL : 0; - } - String dump() { - return super.dump() + " " + inputCount + " " + volts[inputCount] + " " + highVoltage; - } - Point inPosts[], inGates[]; - boolean inputStates[]; - int ww; - void setPoints() { - super.setPoints(); - inputStates = new boolean[inputCount]; - if (dn > 150 && this == sim.dragElm) - setSize(2); - int hs = gheight; - int i; - ww = gwidth2; // was 24 - if (ww > dn/2) - ww = (int) (dn/2); - if (isInverting() && ww+8 > dn/2) - ww = (int) (dn/2-8); - calcLeads(ww*2); - inPosts = new Point[inputCount]; - inGates = new Point[inputCount]; - allocNodes(); - int i0 = -inputCount/2; - if (hasFlag(FLAG_INVERT_INPUTS)) - icircles = new Point[inputCount]; - else - icircles = null; - for (i = 0; i != inputCount; i++, i0++) { - if (i0 == 0 && (inputCount & 1) == 0) - i0++; - double adj = getLeadAdjustment(i); - inPosts[i] = interpPoint(point1, point2, 0, hs*i0); - inGates[i] = interpPoint(lead1, lead2, icircles != null ? -8/(ww*2.)+adj : adj, hs*i0); - if (icircles != null) - icircles[i] = interpPoint(lead1, lead2, -4/(ww*2.), hs*i0); - volts[i] = (lastOutput ^ isInverting()) ? 5 : 0; - } - hs2 = gwidth*(inputCount/2+1); - setBbox(point1, point2, hs2); - if (hasSchmittInputs()) - schmittPoly = getSchmittPolygon(gsize, .47f); - } - - double getLeadAdjustment(int ix) { return 0; } - - void createEuroGatePolygon() { - Point pts[] = newPointArray(4); - interpPoint2(lead1, lead2, pts[0], pts[1], 0, hs2); - interpPoint2(lead1, lead2, pts[3], pts[2], 1, hs2); - gatePoly = createPolygon(pts); - } - - String getGateText() { return null; } - static boolean useEuroGates() { return sim.euroGatesCheckItem.getState(); } - - void drawGatePolygon(Graphics g) { - drawThickPolygon(g, gatePoly); - } - - void draw(Graphics g) { - int i; - for (i = 0; i != inputCount; i++) { - setVoltageColor(g, volts[i]); - drawThickLine(g, inPosts[i], inGates[i]); - } - setVoltageColor(g, volts[inputCount]); - drawThickLine(g, lead2, point2); - g.setColor(needsHighlight() ? selectColor : lightGrayColor); - if (useEuroGates()) { - drawThickPolygon(g, gatePoly); - Point center = interpPoint(point1, point2, .5); - drawCenteredText(g, getGateText(), center.x, center.y-6*gsize, true); - } else - drawGatePolygon(g); - g.setLineWidth(2); - if (hasSchmittInputs()) - drawPolygon(g, schmittPoly); - g.setLineWidth(1); - if (linePoints != null) - for (i = 0; i != linePoints.length-1; i++) - drawThickLine(g, linePoints[i], linePoints[i+1]); - if (isInverting()) - drawThickCircle(g, pcircle.x, pcircle.y, 3); - if (icircles != null) - for (i = 0; i != inputCount; i++) - drawThickCircle(g, icircles[i].x, icircles[i].y, 3); - curcount = updateDotCount(current, curcount); - drawDots(g, lead2, point2, curcount); - drawPosts(g); - } - Polygon gatePoly, schmittPoly; - Point pcircle, linePoints[], icircles[]; - int getPostCount() { return inputCount+1; } - Point getPost(int n) { - if (n == inputCount) - return point2; - return inPosts[n]; - } - int getVoltageSourceCount() { return 1; } - abstract String getGateName(); - void getInfo(String arr[]) { - arr[0] = getGateName(); - arr[1] = "Vout = " + getVoltageText(volts[inputCount]); - arr[2] = "Iout = " + getCurrentText(getCurrent()); - } - void stamp() { - sim.stampVoltageSource(0, nodes[inputCount], voltSource); - } - boolean hasSchmittInputs() { return (flags & FLAG_SCHMITT) != 0; } - boolean getInput(int x) { - boolean high = !hasFlag(FLAG_INVERT_INPUTS); - if (!hasSchmittInputs()) - return (volts[x] > highVoltage*.5) ? high : !high; - boolean res = volts[x] > highVoltage*(inputStates[x] ? .35 : .55); - inputStates[x] = res ? high : !high; - return res; - } - abstract boolean calcFunction(); - - int oscillationCount; - double lastTime; - - void doStep() { - boolean f = calcFunction(); - if (isInverting()) - f = !f; - - if (lastTime != sim.t) { - // detect oscillation (using same strategy as Atanua) - if (lastOutput == !f) { - if (oscillationCount++ > 50) { - // output is oscillating too much, randomly leave output the same - oscillationCount = 0; - if (sim.getrand(10) > 5) - f = lastOutput; - } - } else - oscillationCount = 0; - - lastOutput = f; - lastTime = sim.t; - } - - double res = f ? highVoltage : 0; - sim.updateVoltageSource(0, nodes[inputCount], voltSource, res); - } - public EditInfo getEditInfo(int n) { - if (n == 0) - return new EditInfo("# of Inputs", inputCount, 1, 8). - setDimensionless(); - if (n == 1) - return new EditInfo("High Logic Voltage", highVoltage, 1, 10); - if (n == 2) - return EditInfo.createCheckbox("Schmitt Inputs", hasSchmittInputs()); - if (n == 3) - return EditInfo.createCheckbox("Invert Inputs", hasFlag(FLAG_INVERT_INPUTS)); - return null; - } - - public void setEditValue(int n, EditInfo ei) { - if (n == 0 && ei.value >= 1) { - inputCount = (int) ei.value; - setPoints(); - } - if (n == 1) - highVoltage = lastHighVoltage = ei.value; - if (n == 2) { - if (ei.checkbox.getState()) - flags |= FLAG_SCHMITT; - else - flags &= ~FLAG_SCHMITT; - lastSchmitt = hasSchmittInputs(); - setPoints(); - } - if (n == 3) { - flags = ei.changeFlag(flags, FLAG_INVERT_INPUTS); - setPoints(); - } - } - // there is no current path through the gate inputs, but there - // is an indirect path through the output to ground. - boolean getConnection(int n1, int n2) { return false; } - boolean hasGroundConnection(int n1) { - return (n1 == inputCount); - } - - double getCurrentIntoNode(int n) { - if (n == inputCount) - return current; - return 0; - } +public abstract class GateElm extends CircuitElm { + final int FLAG_SMALL = 1 << 0; + final int FLAG_SCHMITT = 1 << 1; + final int FLAG_INVERT_INPUTS = 1 << 2; + int inputCount = 2; + boolean lastOutput; + double highVoltage; + public static double lastHighVoltage = 5; + static boolean lastSchmitt = false; + + public GateElm(int xx, int yy) { + super(xx, yy); + noDiagonal = true; + inputCount = 2; + + // copy defaults from last gate edited + highVoltage = lastHighVoltage; + if (lastSchmitt) + flags |= FLAG_SCHMITT; + + setSize(sim.smallGridCheckItem.getState() ? 1 : 2); + } + + public GateElm(int xa, int ya, int xb, int yb, int f, + StringTokenizer st) { + super(xa, ya, xb, yb, f); + inputCount = new Integer(st.nextToken()).intValue(); + double lastOutputVoltage = new Double(st.nextToken()).doubleValue(); + noDiagonal = true; + highVoltage = 5; + try { + highVoltage = new Double(st.nextToken()).doubleValue(); + } catch (Exception e) { + } + lastOutput = lastOutputVoltage > highVoltage * .5; + setSize((f & FLAG_SMALL) != 0 ? 1 : 2); + allocNodes(); + } + + boolean isInverting() { + return false; + } + + int gsize, gwidth, gwidth2, gheight, hs2; + + void setSize(int s) { + gsize = s; + gwidth = 7 * s; + gwidth2 = 14 * s; + gheight = 8 * s; + flags &= ~FLAG_SMALL; + flags |= (s == 1) ? FLAG_SMALL : 0; + } + + String dump() { + return super.dump() + " " + inputCount + " " + volts[inputCount] + " " + highVoltage; + } + + Point inPosts[], inGates[]; + boolean inputStates[]; + int ww; + + void setPoints() { + super.setPoints(); + inputStates = new boolean[inputCount]; + if (dn > 150 && this == sim.dragElm) + setSize(2); + int hs = gheight; + int i; + ww = gwidth2; // was 24 + if (ww > dn / 2) + ww = (int) (dn / 2); + if (isInverting() && ww + 8 > dn / 2) + ww = (int) (dn / 2 - 8); + calcLeads(ww * 2); + inPosts = new Point[inputCount]; + inGates = new Point[inputCount]; + allocNodes(); + int i0 = -inputCount / 2; + if (hasFlag(FLAG_INVERT_INPUTS)) + icircles = new Point[inputCount]; + else + icircles = null; + for (i = 0; i != inputCount; i++, i0++) { + if (i0 == 0 && (inputCount & 1) == 0) + i0++; + double adj = getLeadAdjustment(i); + inPosts[i] = interpPoint(point1, point2, 0, hs * i0); + inGates[i] = interpPoint(lead1, lead2, icircles != null ? -8 / (ww * 2.) + adj : adj, hs * i0); + if (icircles != null) + icircles[i] = interpPoint(lead1, lead2, -4 / (ww * 2.), hs * i0); + volts[i] = (lastOutput ^ isInverting()) ? 5 : 0; + } + hs2 = gwidth * (inputCount / 2 + 1); + setBbox(point1, point2, hs2); + if (hasSchmittInputs()) + schmittPoly = getSchmittPolygon(gsize, .47f); + } + + double getLeadAdjustment(int ix) { + return 0; + } + + void createEuroGatePolygon() { + Point pts[] = newPointArray(4); + interpPoint2(lead1, lead2, pts[0], pts[1], 0, hs2); + interpPoint2(lead1, lead2, pts[3], pts[2], 1, hs2); + gatePoly = createPolygon(pts); + } + + String getGateText() { + return null; + } + + static boolean useEuroGates() { + return sim.euroGatesCheckItem.getState(); + } + + void drawGatePolygon(Graphics g) { + drawThickPolygon(g, gatePoly); + } + + void draw(Graphics g) { + int i; + for (i = 0; i != inputCount; i++) { + setVoltageColor(g, volts[i]); + drawThickLine(g, inPosts[i], inGates[i]); + } + setVoltageColor(g, volts[inputCount]); + drawThickLine(g, lead2, point2); + g.setColor(needsHighlight() ? selectColor : lightGrayColor); + if (useEuroGates()) { + drawThickPolygon(g, gatePoly); + Point center = interpPoint(point1, point2, .5); + drawCenteredText(g, getGateText(), center.x, center.y - 6 * gsize, true); + } else + drawGatePolygon(g); + g.setLineWidth(2); + if (hasSchmittInputs()) + drawPolygon(g, schmittPoly); + g.setLineWidth(1); + if (linePoints != null) + for (i = 0; i != linePoints.length - 1; i++) + drawThickLine(g, linePoints[i], linePoints[i + 1]); + if (isInverting()) + drawThickCircle(g, pcircle.x, pcircle.y, 3); + if (icircles != null) + for (i = 0; i != inputCount; i++) + drawThickCircle(g, icircles[i].x, icircles[i].y, 3); + curcount = updateDotCount(current, curcount); + drawDots(g, lead2, point2, curcount); + drawPosts(g); + } + + Polygon gatePoly, schmittPoly; + Point pcircle, linePoints[], icircles[]; + + int getPostCount() { + return inputCount + 1; + } + + Point getPost(int n) { + if (n == inputCount) + return point2; + return inPosts[n]; + } + + int getVoltageSourceCount() { + return 1; + } + + abstract String getGateName(); + + void getInfo(String arr[]) { + arr[0] = getGateName(); + arr[1] = "Vout = " + getVoltageText(volts[inputCount]); + arr[2] = "Iout = " + getCurrentText(getCurrent()); + } + + void stamp() { + sim.stampVoltageSource(0, nodes[inputCount], voltSource); + } + + boolean hasSchmittInputs() { + return (flags & FLAG_SCHMITT) != 0; + } + + boolean getInput(int x) { + boolean high = !hasFlag(FLAG_INVERT_INPUTS); + if (!hasSchmittInputs()) + return (volts[x] > highVoltage * .5) ? high : !high; + boolean res = volts[x] > highVoltage * (inputStates[x] ? .35 : .55); + inputStates[x] = res ? high : !high; + return res; + } + + abstract boolean calcFunction(); + + int oscillationCount; + double lastTime; + + void doStep() { + boolean f = calcFunction(); + if (isInverting()) + f = !f; + + if (lastTime != sim.t) { + // detect oscillation (using same strategy as Atanua) + if (lastOutput == !f) { + if (oscillationCount++ > 50) { + // output is oscillating too much, randomly leave output the same + oscillationCount = 0; + if (sim.getrand(10) > 5) + f = lastOutput; + } + } else + oscillationCount = 0; + + lastOutput = f; + lastTime = sim.t; + } + + double res = f ? highVoltage : 0; + sim.updateVoltageSource(0, nodes[inputCount], voltSource, res); + } + + public EditInfo getEditInfo(int n) { + if (n == 0) + return new EditInfo("# of Inputs", inputCount, 1, 8). + setDimensionless(); + if (n == 1) + return new EditInfo("High Logic Voltage", highVoltage, 1, 10); + if (n == 2) + return EditInfo.createCheckbox("Schmitt Inputs", hasSchmittInputs()); + if (n == 3) + return EditInfo.createCheckbox("Invert Inputs", hasFlag(FLAG_INVERT_INPUTS)); + return null; + } + + public void setEditValue(int n, EditInfo ei) { + if (n == 0 && ei.value >= 1) { + inputCount = (int) ei.value; + setPoints(); + } + if (n == 1) + highVoltage = lastHighVoltage = ei.value; + if (n == 2) { + if (ei.checkbox.getState()) + flags |= FLAG_SCHMITT; + else + flags &= ~FLAG_SCHMITT; + lastSchmitt = hasSchmittInputs(); + setPoints(); + } + if (n == 3) { + flags = ei.changeFlag(flags, FLAG_INVERT_INPUTS); + setPoints(); + } + } + + // there is no current path through the gate inputs, but there + // is an indirect path through the output to ground. + boolean getConnection(int n1, int n2) { + return false; + } + + boolean hasGroundConnection(int n1) { + return (n1 == inputCount); + } + + double getCurrentIntoNode(int n) { + if (n == inputCount) + return current; + return 0; } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/GraphicElm.java b/src/main/java/com/lushprojects/circuitjs1/client/GraphicElm.java index 591f564..0db1e84 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/GraphicElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/GraphicElm.java @@ -19,19 +19,18 @@ package com.lushprojects.circuitjs1.client; -class GraphicElm extends CircuitElm -{ +public class GraphicElm extends CircuitElm { - public GraphicElm(int xx, int yy) - { - super(xx,yy); + public GraphicElm(int xx, int yy) { + super(xx, yy); } - public GraphicElm(int xa, int ya, int xb, int yb, int flags) - { - super(xa, ya, xb, yb, flags); + public GraphicElm(int xa, int ya, int xb, int yb, int flags) { + super(xa, ya, xb, yb, flags); } - int getPostCount() { return 0; } + int getPostCount() { + return 0; + } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/Graphics.java b/src/main/java/com/lushprojects/circuitjs1/client/Graphics.java index 9a2b10c..07d7fb2 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/Graphics.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/Graphics.java @@ -22,167 +22,168 @@ import com.google.gwt.canvas.dom.client.Context2d; public class Graphics { - - Context2d context; - int currentFontSize; - Color lastColor; - int savedFontSize; - static boolean isFullScreen=false; - - public Graphics(Context2d context) { - this.context = context; - currentFontSize = 12; - } - - public void setColor(Color color) { - if (color != null) { - String colorString = color.getHexValue(); - context.setStrokeStyle(colorString); - context.setFillStyle(colorString); - } else { - System.out.println("Ignoring null-Color"); - } - lastColor=color; - } - - public void setColor(String color) { - context.setStrokeStyle(color); - context.setFillStyle(color); - lastColor=null; - } - - public void clipRect(int x, int y, int width, int height) { - context.beginPath(); - context.rect(x, y, width, height); - context.clip(); - } - - public void restore() { - context.restore(); - currentFontSize = savedFontSize; - } - public void save() { - context.save(); - savedFontSize = currentFontSize; - } - - - public void fillRect(int x, int y, int width, int height) { - // context.beginPath(); - context.fillRect(x, y, width, height); - // context.closePath(); - } - - public void drawRect(int x, int y, int width, int height) { - // context.beginPath(); - context.strokeRect(x, y, width, height); - // context.closePath(); - } - - public void fillOval(int x, int y, int width, int height) { - context.beginPath(); - context.arc(x+width/2, y+width/2, width/2, 0, 2.0*3.14159); - context.closePath(); - context.fill(); - } - - public void drawString(String s, int x, int y){ - context.fillText(s, x, y); - } - - public double measureWidth(String s) { - return context.measureText(s).getWidth(); - } - - public void setLineWidth(double width){ - context.setLineWidth(width); - } - - public void drawLine(int x1, int y1, int x2, int y2) { - context.beginPath(); - context.moveTo(x1, y1); - context.lineTo(x2, y2); - context.stroke(); - // context.closePath(); - } - - public void drawPolyline(int[] xpoints, int[] ypoints, int n) { - int i; - context.beginPath(); - for (i=0; i")); - - tp.add(vpRU = new VerticalPanel(), tab2Title); - vpRU.setWidth("500px"); - vpRU.add(new HTML("")); + //create tabs + tp.add(vpEN = new VerticalPanel(), tab1Title); + vpEN.setWidth("500px"); + vpEN.add(new HTML("")); + + tp.add(vpRU = new VerticalPanel(), tab2Title); + vpRU.setWidth("500px"); + vpRU.add(new HTML("")); /* tp.add(vpPL = new VerticalPanel(), tab3Title); vpPL.setWidth("500px"); @@ -62,29 +62,29 @@ public class HelpDialog extends DialogBox { vpDA.setWidth("500px"); vpDA.add(new HTML("")); */ - HorizontalPanel hp = new HorizontalPanel(); - hp.setWidth("100%"); - hp.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_LEFT); - hp.setStyleName("topSpace"); - - //select first tab - tp.selectTab(0); + HorizontalPanel hp = new HorizontalPanel(); + hp.setWidth("100%"); + hp.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_LEFT); + hp.setStyleName("topSpace"); + + //select first tab + tp.selectTab(0); + + //set width if tabpanel + tp.setWidth("500"); - //set width if tabpanel - tp.setWidth("500"); - - vp.add(hp); - hp.add(okButton = new Button("OK")); - okButton.addClickHandler(new ClickHandler() { - public void onClick(ClickEvent event) { - closeDialog(); - } - }); - center(); - show(); - } + vp.add(hp); + hp.add(okButton = new Button("OK")); + okButton.addClickHandler(new ClickHandler() { + public void onClick(ClickEvent event) { + closeDialog(); + } + }); + center(); + show(); + } - protected void closeDialog() { - hide(); - } + protected void closeDialog() { + hide(); + } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/ImportFromDropbox.java b/src/main/java/com/lushprojects/circuitjs1/client/ImportFromDropbox.java index 412721b..261484a 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/ImportFromDropbox.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/ImportFromDropbox.java @@ -2,17 +2,17 @@ public class ImportFromDropbox { - - static CirSim sim; - - ImportFromDropbox( CirSim asim ){ - sim=asim; + + static CirSim sim; + + ImportFromDropbox(CirSim asim) { + sim = asim; // CirSim.console("importing"); - doDropboxImport(); + doDropboxImport(); // CirSim.console("returned"); - } - - static public final native boolean isSupported() + } + + static public final native boolean isSupported() /*-{ try { // Bug in firefox prevents Dropbox dialog working properly in this application @@ -26,14 +26,14 @@ static public final native boolean isSupported() return false; } }-*/; - - static public void doLoadCallback(String s) { - sim.pushUndo(); - sim.readCircuit(s); - } - - - public final native void doDropboxImport() + + static public void doLoadCallback(String s) { + sim.pushUndo(); + sim.readCircuit(s); + } + + + public final native void doDropboxImport() /*-{ var options = { diff --git a/src/main/java/com/lushprojects/circuitjs1/client/ImportFromDropboxDialog.java b/src/main/java/com/lushprojects/circuitjs1/client/ImportFromDropboxDialog.java index 58d5f9e..9f7ee0d 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/ImportFromDropboxDialog.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/ImportFromDropboxDialog.java @@ -12,31 +12,31 @@ import com.google.gwt.user.client.Window; public class ImportFromDropboxDialog extends Dialog { - - - VerticalPanel vp; - Button cancelButton; - Button chooserButton; - Button importButton; - TextArea ta; - Label la; - HorizontalPanel hp; - ImportFromDropbox importFromDropbox; - static CirSim sim; - - - static public void setSim(CirSim csim) { - sim=csim; - } - - static public void doLoadCallback(String s) { - sim.pushUndo(); - sim.readCircuit(s); - sim.allowSave(false); - } - - - static public final native void doDropboxImport(String link) /*-{ + + + VerticalPanel vp; + Button cancelButton; + Button chooserButton; + Button importButton; + TextArea ta; + Label la; + HorizontalPanel hp; + ImportFromDropbox importFromDropbox; + static CirSim sim; + + + static public void setSim(CirSim csim) { + sim = csim; + } + + static public void doLoadCallback(String s) { + sim.pushUndo(); + sim.readCircuit(s); + sim.allowSave(false); + } + + + static public final native void doDropboxImport(String link) /*-{ try { var xhr= new XMLHttpRequest(); xhr.addEventListener("load", function reqListener() { @@ -53,70 +53,69 @@ static public final native void doDropboxImport(String link) /*-{ }-*/; - static public void doImportDropboxLink(String link, Boolean validateIsDropbox) { - if (validateIsDropbox && link.indexOf("https://www.dropbox.com/") != 0) - { - Window.alert("Dropbox links must start https://www.dropbox.com/"); - return; - } - // Work-around to allow CORS access to dropbox links - see - // https://www.dropboxforum.com/t5/API-support/CORS-issue-when-trying-to-download-shared-file/m-p/82466 - link=link.replace("www.dropbox.com", "dl.dropboxusercontent.com"); - doDropboxImport(link); - - } - - public ImportFromDropboxDialog(CirSim csim) { - super(); - setSim(csim); - - closeOnEnter = false; - vp=new VerticalPanel(); - setWidget(vp); - setText(Locale.LS("Import from Dropbox")); - if (ImportFromDropbox.isSupported()) { - vp.add(new Label(Locale.LS("To open a file in your dropbox account using the chooser click below."))); - chooserButton = new Button(Locale.LS("Open Dropbox Chooser")); - vp.add(chooserButton); - chooserButton.addClickHandler(new ClickHandler() { - public void onClick(ClickEvent event) { - closeDialog(); - importFromDropbox= new ImportFromDropbox(sim); - } - }); - la = new Label(Locale.LS("To open a shared Dropbox file from a Dropbox link paste the link below...")); - } else { - vp.add(new Label("This site, or your browser doesn't support the Dropbox chooser so you can't pick a file from your dropbox account.")); - la = new Label("You can open a shared Dropbox file if you have a link. Paste the Dropbox link below..."); - la.setStyleName("topSpace"); - } - - vp.add(la); - ta=new TextArea(); - ta.setWidth("300px"); - ta.setHeight("200px"); - vp.add(ta); - hp = new HorizontalPanel(); - hp.setWidth("100%"); - vp.add(hp); - hp.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_LEFT); - importButton= new Button(Locale.LS("Import From Dropbox Link")); - importButton.addClickHandler(new ClickHandler() { - public void onClick(ClickEvent event) { - closeDialog(); - doImportDropboxLink(ta.getText(), true); - } - }); - hp.add(importButton); - hp.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_RIGHT); - cancelButton=new Button(Locale.LS("Cancel")); - hp.add(cancelButton); - cancelButton.addClickHandler(new ClickHandler() { - public void onClick(ClickEvent event) { - closeDialog(); - } - }); - this.center(); - } + static public void doImportDropboxLink(String link, Boolean validateIsDropbox) { + if (validateIsDropbox && link.indexOf("https://www.dropbox.com/") != 0) { + Window.alert("Dropbox links must start https://www.dropbox.com/"); + return; + } + // Work-around to allow CORS access to dropbox links - see + // https://www.dropboxforum.com/t5/API-support/CORS-issue-when-trying-to-download-shared-file/m-p/82466 + link = link.replace("www.dropbox.com", "dl.dropboxusercontent.com"); + doDropboxImport(link); + + } + + public ImportFromDropboxDialog(CirSim csim) { + super(); + setSim(csim); + + closeOnEnter = false; + vp = new VerticalPanel(); + setWidget(vp); + setText(Locale.LS("Import from Dropbox")); + if (ImportFromDropbox.isSupported()) { + vp.add(new Label(Locale.LS("To open a file in your dropbox account using the chooser click below."))); + chooserButton = new Button(Locale.LS("Open Dropbox Chooser")); + vp.add(chooserButton); + chooserButton.addClickHandler(new ClickHandler() { + public void onClick(ClickEvent event) { + closeDialog(); + importFromDropbox = new ImportFromDropbox(sim); + } + }); + la = new Label(Locale.LS("To open a shared Dropbox file from a Dropbox link paste the link below...")); + } else { + vp.add(new Label("This site, or your browser doesn't support the Dropbox chooser so you can't pick a file from your dropbox account.")); + la = new Label("You can open a shared Dropbox file if you have a link. Paste the Dropbox link below..."); + la.setStyleName("topSpace"); + } + + vp.add(la); + ta = new TextArea(); + ta.setWidth("300px"); + ta.setHeight("200px"); + vp.add(ta); + hp = new HorizontalPanel(); + hp.setWidth("100%"); + vp.add(hp); + hp.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_LEFT); + importButton = new Button(Locale.LS("Import From Dropbox Link")); + importButton.addClickHandler(new ClickHandler() { + public void onClick(ClickEvent event) { + closeDialog(); + doImportDropboxLink(ta.getText(), true); + } + }); + hp.add(importButton); + hp.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_RIGHT); + cancelButton = new Button(Locale.LS("Cancel")); + hp.add(cancelButton); + cancelButton.addClickHandler(new ClickHandler() { + public void onClick(ClickEvent event) { + closeDialog(); + } + }); + this.center(); + } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/ImportFromTextDialog.java b/src/main/java/com/lushprojects/circuitjs1/client/ImportFromTextDialog.java index 0afa253..b8cfd43 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/ImportFromTextDialog.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/ImportFromTextDialog.java @@ -29,49 +29,49 @@ import com.google.gwt.user.client.ui.Label; public class ImportFromTextDialog extends Dialog { - -VerticalPanel vp; -HorizontalPanel hp; -CirSim sim; -// RichTextArea textBox; -TextArea textArea; - - public ImportFromTextDialog( CirSim asim) { - super(); - sim=asim; - closeOnEnter = false; - Button okButton, cancelButton; - final Checkbox subCheck; - vp=new VerticalPanel(); - setWidget(vp); - setText(Locale.LS("Import from Text")); - vp.add(new Label(Locale.LS("Paste the text file for your circuit here..."))); + + VerticalPanel vp; + HorizontalPanel hp; + CirSim sim; + // RichTextArea textBox; + TextArea textArea; + + public ImportFromTextDialog(CirSim asim) { + super(); + sim = asim; + closeOnEnter = false; + Button okButton, cancelButton; + final Checkbox subCheck; + vp = new VerticalPanel(); + setWidget(vp); + setText(Locale.LS("Import from Text")); + vp.add(new Label(Locale.LS("Paste the text file for your circuit here..."))); // vp.add(textBox = new RichTextArea()); - vp.add(textArea = new TextArea()); - textArea.setWidth("300px"); - textArea.setHeight("200px"); - vp.add(subCheck = new Checkbox(Locale.LS("Load Subcircuits Only"))); - hp = new HorizontalPanel(); - vp.add(hp); - hp.add(okButton = new Button(Locale.LS("OK"))); - okButton.addClickHandler(new ClickHandler() { - public void onClick(ClickEvent event) { - String s; - sim.pushUndo(); - closeDialog(); + vp.add(textArea = new TextArea()); + textArea.setWidth("300px"); + textArea.setHeight("200px"); + vp.add(subCheck = new Checkbox(Locale.LS("Load Subcircuits Only"))); + hp = new HorizontalPanel(); + vp.add(hp); + hp.add(okButton = new Button(Locale.LS("OK"))); + okButton.addClickHandler(new ClickHandler() { + public void onClick(ClickEvent event) { + String s; + sim.pushUndo(); + closeDialog(); // s=textBox.getHTML(); // s=s.replace("
", "\r"); - s=textArea.getText(); - sim.importCircuitFromText(s, subCheck.getState()); - } - }); - hp.add(cancelButton = new Button(Locale.LS("Cancel"))); - cancelButton.addClickHandler(new ClickHandler() { - public void onClick(ClickEvent event) { - closeDialog(); - } - }); - this.center(); - show(); - } + s = textArea.getText(); + sim.importCircuitFromText(s, subCheck.getState()); + } + }); + hp.add(cancelButton = new Button(Locale.LS("Cancel"))); + cancelButton.addClickHandler(new ClickHandler() { + public void onClick(ClickEvent event) { + closeDialog(); + } + }); + this.center(); + show(); + } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/Inductor.java b/src/main/java/com/lushprojects/circuitjs1/client/Inductor.java index 6379aa1..d44b61f 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/Inductor.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/Inductor.java @@ -19,66 +19,80 @@ package com.lushprojects.circuitjs1.client; -class Inductor { +public class Inductor { public static final int FLAG_BACK_EULER = 2; int nodes[]; int flags; CirSim sim; - + double inductance; double compResistance, current; double curSourceValue; + Inductor(CirSim s) { - sim = s; - nodes = new int[2]; + sim = s; + nodes = new int[2]; } + void setup(double ic, double cr, int f) { - inductance = ic; - current = cr; - flags = f; + inductance = ic; + current = cr; + flags = f; + } + + boolean isTrapezoidal() { + return (flags & FLAG_BACK_EULER) == 0; + } + + void reset() { + resetTo(0); } - boolean isTrapezoidal() { return (flags & FLAG_BACK_EULER) == 0; } - void reset() { resetTo(0); } + void resetTo(double c) { - // need to set curSourceValue here in case one of inductor nodes is node 0. In that case - // calculateCurrent() may get called (from setNodeVoltage()) when analyzing circuit, before - // startIteration() gets called - curSourceValue = current = c; + // need to set curSourceValue here in case one of inductor nodes is node 0. In that case + // calculateCurrent() may get called (from setNodeVoltage()) when analyzing circuit, before + // startIteration() gets called + curSourceValue = current = c; } + void stamp(int n0, int n1) { - // inductor companion model using trapezoidal or backward euler - // approximations (Norton equivalent) consists of a current - // source in parallel with a resistor. Trapezoidal is more - // accurate than backward euler but can cause oscillatory behavior. - // The oscillation is a real problem in circuits with switches. - nodes[0] = n0; - nodes[1] = n1; - if (isTrapezoidal()) - compResistance = 2*inductance/sim.timeStep; - else // backward euler - compResistance = inductance/sim.timeStep; - sim.stampResistor(nodes[0], nodes[1], compResistance); - sim.stampRightSide(nodes[0]); - sim.stampRightSide(nodes[1]); + // inductor companion model using trapezoidal or backward euler + // approximations (Norton equivalent) consists of a current + // source in parallel with a resistor. Trapezoidal is more + // accurate than backward euler but can cause oscillatory behavior. + // The oscillation is a real problem in circuits with switches. + nodes[0] = n0; + nodes[1] = n1; + if (isTrapezoidal()) + compResistance = 2 * inductance / sim.timeStep; + else // backward euler + compResistance = inductance / sim.timeStep; + sim.stampResistor(nodes[0], nodes[1], compResistance); + sim.stampRightSide(nodes[0]); + sim.stampRightSide(nodes[1]); + } + + boolean nonLinear() { + return false; } - boolean nonLinear() { return false; } void startIteration(double voltdiff) { - if (isTrapezoidal()) - curSourceValue = voltdiff/compResistance+current; - else // backward euler - curSourceValue = current; + if (isTrapezoidal()) + curSourceValue = voltdiff / compResistance + current; + else // backward euler + curSourceValue = current; } - + double calculateCurrent(double voltdiff) { - // we check compResistance because this might get called - // before stamp(), which sets compResistance, causing - // infinite current - if (compResistance > 0) - current = voltdiff/compResistance + curSourceValue; - return current; + // we check compResistance because this might get called + // before stamp(), which sets compResistance, causing + // infinite current + if (compResistance > 0) + current = voltdiff / compResistance + curSourceValue; + return current; } + void doStep(double voltdiff) { - sim.stampCurrentSource(nodes[0], nodes[1], curSourceValue); + sim.stampCurrentSource(nodes[0], nodes[1], curSourceValue); } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/InductorElm.java b/src/main/java/com/lushprojects/circuitjs1/client/InductorElm.java index 2808422..c7d149d 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/InductorElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/InductorElm.java @@ -19,107 +19,134 @@ package com.lushprojects.circuitjs1.client; - class InductorElm extends CircuitElm { - Inductor ind; - double inductance; - double initialCurrent; - public InductorElm(int xx, int yy) { - super(xx, yy); - ind = new Inductor(sim); - inductance = 1; - ind.setup(inductance, current, flags); - } - public InductorElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f); - ind = new Inductor(sim); - inductance = new Double(st.nextToken()).doubleValue(); - current = new Double(st.nextToken()).doubleValue(); - try { - initialCurrent = new Double(st.nextToken()).doubleValue(); - } catch (Exception e) {} - ind.setup(inductance, current, flags); - } - int getDumpType() { return 'l'; } - String dump() { - return super.dump() + " " + inductance + " " + current + " " + initialCurrent; - } - void setPoints() { - super.setPoints(); - calcLeads(32); - } - void draw(Graphics g) { - double v1 = volts[0]; - double v2 = volts[1]; - int i; - int hs = 8; - setBbox(point1, point2, hs); - draw2Leads(g); - setPowerColor(g, false); - drawCoil(g, 8, lead1, lead2, v1, v2); - if (sim.showValuesCheckItem.getState()) { - String s = getShortUnitText(inductance, "H"); - drawValues(g, s, hs); - } - doDots(g); - drawPosts(g); - } - void reset() { - volts[0] = volts[1] = curcount = 0; - current = initialCurrent; - ind.resetTo(initialCurrent); - } - void stamp() { ind.stamp(nodes[0], nodes[1]); } - void startIteration() { - ind.startIteration(volts[0]-volts[1]); - } - boolean nonLinear() { return ind.nonLinear(); } - void calculateCurrent() { - double voltdiff = volts[0]-volts[1]; - current = ind.calculateCurrent(voltdiff); - } - void doStep() { - double voltdiff = volts[0]-volts[1]; - ind.doStep(voltdiff); - } - void getInfo(String arr[]) { - arr[0] = "inductor"; - getBasicInfo(arr); - arr[3] = "L = " + getUnitText(inductance, "H"); - arr[4] = "P = " + getUnitText(getPower(), "W"); - } - public EditInfo getEditInfo(int n) { - if (n == 0) - return new EditInfo("Inductance (H)", inductance, 1e-2, 10); - if (n == 1) { - EditInfo ei = new EditInfo("", 0, -1, -1); - ei.checkbox = new Checkbox("Trapezoidal Approximation", - ind.isTrapezoidal()); - return ei; - } - if (n == 2) - return new EditInfo("Initial Current (on Reset) (A)", initialCurrent); - return null; - } - - public void setEditValue(int n, EditInfo ei) { - if (n == 0 && ei.value > 0) - inductance = ei.value; - if (n == 1) { - if (ei.checkbox.getState()) - flags &= ~Inductor.FLAG_BACK_EULER; - else - flags |= Inductor.FLAG_BACK_EULER; - } - if (n == 2) - initialCurrent = ei.value; - ind.setup(inductance, current, flags); - } - - int getShortcut() { return 'L'; } - public double getInductance() { return inductance; } - void setInductance(double l) { - inductance = l; - ind.setup(inductance, current, flags); - } +public class InductorElm extends CircuitElm { + Inductor ind; + double inductance; + double initialCurrent; + + public InductorElm(int xx, int yy) { + super(xx, yy); + ind = new Inductor(sim); + inductance = 1; + ind.setup(inductance, current, flags); + } + + public InductorElm(int xa, int ya, int xb, int yb, int f, + StringTokenizer st) { + super(xa, ya, xb, yb, f); + ind = new Inductor(sim); + inductance = new Double(st.nextToken()).doubleValue(); + current = new Double(st.nextToken()).doubleValue(); + try { + initialCurrent = new Double(st.nextToken()).doubleValue(); + } catch (Exception e) { + } + ind.setup(inductance, current, flags); + } + + int getDumpType() { + return 'l'; + } + + String dump() { + return super.dump() + " " + inductance + " " + current + " " + initialCurrent; + } + + void setPoints() { + super.setPoints(); + calcLeads(32); + } + + void draw(Graphics g) { + double v1 = volts[0]; + double v2 = volts[1]; + int i; + int hs = 8; + setBbox(point1, point2, hs); + draw2Leads(g); + setPowerColor(g, false); + drawCoil(g, 8, lead1, lead2, v1, v2); + if (sim.showValuesCheckItem.getState()) { + String s = getShortUnitText(inductance, "H"); + drawValues(g, s, hs); + } + doDots(g); + drawPosts(g); + } + + void reset() { + volts[0] = volts[1] = curcount = 0; + current = initialCurrent; + ind.resetTo(initialCurrent); + } + + void stamp() { + ind.stamp(nodes[0], nodes[1]); + } + + void startIteration() { + ind.startIteration(volts[0] - volts[1]); + } + + boolean nonLinear() { + return ind.nonLinear(); + } + + void calculateCurrent() { + double voltdiff = volts[0] - volts[1]; + current = ind.calculateCurrent(voltdiff); + } + + void doStep() { + double voltdiff = volts[0] - volts[1]; + ind.doStep(voltdiff); + } + + void getInfo(String arr[]) { + arr[0] = "inductor"; + getBasicInfo(arr); + arr[3] = "L = " + getUnitText(inductance, "H"); + arr[4] = "P = " + getUnitText(getPower(), "W"); + } + + public EditInfo getEditInfo(int n) { + if (n == 0) + return new EditInfo("Inductance (H)", inductance, 1e-2, 10); + if (n == 1) { + EditInfo ei = new EditInfo("", 0, -1, -1); + ei.checkbox = new Checkbox("Trapezoidal Approximation", + ind.isTrapezoidal()); + return ei; + } + if (n == 2) + return new EditInfo("Initial Current (on Reset) (A)", initialCurrent); + return null; + } + + public void setEditValue(int n, EditInfo ei) { + if (n == 0 && ei.value > 0) + inductance = ei.value; + if (n == 1) { + if (ei.checkbox.getState()) + flags &= ~Inductor.FLAG_BACK_EULER; + else + flags |= Inductor.FLAG_BACK_EULER; + } + if (n == 2) + initialCurrent = ei.value; + ind.setup(inductance, current, flags); + } + + int getShortcut() { + return 'L'; + } + + public double getInductance() { + return inductance; + } + + void setInductance(double l) { + inductance = l; + ind.setup(inductance, current, flags); } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/InverterElm.java b/src/main/java/com/lushprojects/circuitjs1/client/InverterElm.java index 91ded4e..b20829a 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/InverterElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/InverterElm.java @@ -19,122 +19,148 @@ package com.lushprojects.circuitjs1.client; - class InverterElm extends CircuitElm { - double slewRate; // V/ns - double highVoltage; - public InverterElm(int xx, int yy) { - super(xx, yy); - noDiagonal = true; - slewRate = .5; - - // copy defaults from last gate edited - highVoltage = GateElm.lastHighVoltage; - } - public InverterElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f); - noDiagonal = true; - slewRate = .5; - highVoltage = 5; - try { - slewRate = new Double (st.nextToken()).doubleValue(); - highVoltage = new Double (st.nextToken()).doubleValue(); - } catch (Exception e) { - } - } - String dump() { - return super.dump() + " " + slewRate + " " + highVoltage; - } - - int getDumpType() { return 'I'; } - - Point center; - - void draw(Graphics g) { - drawPosts(g); - draw2Leads(g); - g.setColor(needsHighlight() ? selectColor : lightGrayColor); - drawThickPolygon(g, gatePoly); - if (GateElm.useEuroGates()) - drawCenteredText(g, "1", center.x, center.y-6, true); - drawThickCircle(g, pcircle.x, pcircle.y, 3); - curcount = updateDotCount(current, curcount); - drawDots(g, lead2, point2, curcount); - } - Polygon gatePoly; - Point pcircle; - void setPoints() { - super.setPoints(); - int hs = 16; - int ww = 16; - if (ww > dn/2) - ww = (int) (dn/2); - lead1 = interpPoint(point1, point2, .5-ww/dn); - lead2 = interpPoint(point1, point2, .5+(ww+2)/dn); - pcircle = interpPoint(point1, point2, .5+(ww-2)/dn); - - if (GateElm.useEuroGates()) { - Point pts[] = newPointArray(4); - Point l2 = interpPoint(point1, point2, .5+(ww-5)/dn); // make room for circle - interpPoint2(lead1, l2, pts[0], pts[1], 0, hs); - interpPoint2(lead1, l2, pts[3], pts[2], 1, hs); - gatePoly = createPolygon(pts); - center = interpPoint(lead1, l2, .5); - } else { - Point triPoints[] = newPointArray(3); - interpPoint2(lead1, lead2, triPoints[0], triPoints[1], 0, hs); - triPoints[2] = interpPoint(point1, point2, .5+(ww-5)/dn); - gatePoly = createPolygon(triPoints); - } - setBbox(point1, point2, hs); - } - int getVoltageSourceCount() { return 1; } - void stamp() { - sim.stampVoltageSource(0, nodes[1], voltSource); - } - - double lastOutputVoltage; - - void startIteration() { - lastOutputVoltage = volts[1]; - } - void doStep() { - double out = volts[0] > highVoltage*.5 ? 0 : highVoltage; - double maxStep = slewRate * sim.timeStep * 1e9; - out = Math.max(Math.min(lastOutputVoltage+maxStep, out), lastOutputVoltage-maxStep); - sim.updateVoltageSource(0, nodes[1], voltSource, out); - } - double getVoltageDiff() { return volts[0]; } - void getInfo(String arr[]) { - arr[0] = "inverter"; - arr[1] = "Vi = " + getVoltageText(volts[0]); - arr[2] = "Vo = " + getVoltageText(volts[1]); - } - public EditInfo getEditInfo(int n) { - if (n == 0) - return new EditInfo("Slew Rate (V/ns)", slewRate, 0, 0); - if (n == 1) - return new EditInfo("High Logic Voltage", highVoltage, 1, 10); - return null; - } - public void setEditValue(int n, EditInfo ei) { - if (n == 0) - slewRate = ei.value; - if (n == 1) - highVoltage = GateElm.lastHighVoltage = ei.value; - } - // there is no current path through the inverter input, but there - // is an indirect path through the output to ground. - boolean getConnection(int n1, int n2) { return false; } - boolean hasGroundConnection(int n1) { - return (n1 == 1); - } - int getShortcut() { return '1'; } - - @Override double getCurrentIntoNode(int n) { - if (n == 1) - return current; - return 0; - } +public class InverterElm extends CircuitElm { + double slewRate; // V/ns + double highVoltage; + public InverterElm(int xx, int yy) { + super(xx, yy); + noDiagonal = true; + slewRate = .5; + + // copy defaults from last gate edited + highVoltage = GateElm.lastHighVoltage; + } + + public InverterElm(int xa, int ya, int xb, int yb, int f, + StringTokenizer st) { + super(xa, ya, xb, yb, f); + noDiagonal = true; + slewRate = .5; + highVoltage = 5; + try { + slewRate = new Double(st.nextToken()).doubleValue(); + highVoltage = new Double(st.nextToken()).doubleValue(); + } catch (Exception e) { + } + } + + String dump() { + return super.dump() + " " + slewRate + " " + highVoltage; + } + + int getDumpType() { + return 'I'; + } + + Point center; + + void draw(Graphics g) { + drawPosts(g); + draw2Leads(g); + g.setColor(needsHighlight() ? selectColor : lightGrayColor); + drawThickPolygon(g, gatePoly); + if (GateElm.useEuroGates()) + drawCenteredText(g, "1", center.x, center.y - 6, true); + drawThickCircle(g, pcircle.x, pcircle.y, 3); + curcount = updateDotCount(current, curcount); + drawDots(g, lead2, point2, curcount); + } + + Polygon gatePoly; + Point pcircle; + + void setPoints() { + super.setPoints(); + int hs = 16; + int ww = 16; + if (ww > dn / 2) + ww = (int) (dn / 2); + lead1 = interpPoint(point1, point2, .5 - ww / dn); + lead2 = interpPoint(point1, point2, .5 + (ww + 2) / dn); + pcircle = interpPoint(point1, point2, .5 + (ww - 2) / dn); + + if (GateElm.useEuroGates()) { + Point pts[] = newPointArray(4); + Point l2 = interpPoint(point1, point2, .5 + (ww - 5) / dn); // make room for circle + interpPoint2(lead1, l2, pts[0], pts[1], 0, hs); + interpPoint2(lead1, l2, pts[3], pts[2], 1, hs); + gatePoly = createPolygon(pts); + center = interpPoint(lead1, l2, .5); + } else { + Point triPoints[] = newPointArray(3); + interpPoint2(lead1, lead2, triPoints[0], triPoints[1], 0, hs); + triPoints[2] = interpPoint(point1, point2, .5 + (ww - 5) / dn); + gatePoly = createPolygon(triPoints); + } + setBbox(point1, point2, hs); + } + + int getVoltageSourceCount() { + return 1; + } + + void stamp() { + sim.stampVoltageSource(0, nodes[1], voltSource); + } + + double lastOutputVoltage; + + void startIteration() { + lastOutputVoltage = volts[1]; + } + + void doStep() { + double out = volts[0] > highVoltage * .5 ? 0 : highVoltage; + double maxStep = slewRate * sim.timeStep * 1e9; + out = Math.max(Math.min(lastOutputVoltage + maxStep, out), lastOutputVoltage - maxStep); + sim.updateVoltageSource(0, nodes[1], voltSource, out); + } + + double getVoltageDiff() { + return volts[0]; + } + + void getInfo(String arr[]) { + arr[0] = "inverter"; + arr[1] = "Vi = " + getVoltageText(volts[0]); + arr[2] = "Vo = " + getVoltageText(volts[1]); + } + + public EditInfo getEditInfo(int n) { + if (n == 0) + return new EditInfo("Slew Rate (V/ns)", slewRate, 0, 0); + if (n == 1) + return new EditInfo("High Logic Voltage", highVoltage, 1, 10); + return null; + } + + public void setEditValue(int n, EditInfo ei) { + if (n == 0) + slewRate = ei.value; + if (n == 1) + highVoltage = GateElm.lastHighVoltage = ei.value; + } + + // there is no current path through the inverter input, but there + // is an indirect path through the output to ground. + boolean getConnection(int n1, int n2) { + return false; + } + + boolean hasGroundConnection(int n1) { + return (n1 == 1); } + + int getShortcut() { + return '1'; + } + + @Override + double getCurrentIntoNode(int n) { + if (n == 1) + return current; + return 0; + } + +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/InvertingSchmittElm.java b/src/main/java/com/lushprojects/circuitjs1/client/InvertingSchmittElm.java index 5b78dee..5f6e00d 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/InvertingSchmittElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/InvertingSchmittElm.java @@ -21,183 +21,192 @@ // contributed by Edward Calver - class InvertingSchmittElm extends CircuitElm { - double slewRate; // V/ns - double lowerTrigger; - double upperTrigger; - boolean state; - double logicOnLevel; - double logicOffLevel; - - public InvertingSchmittElm(int xx, int yy) { - super(xx, yy); - noDiagonal = true; - slewRate = .5; - state=false; - lowerTrigger=1.66; - upperTrigger=3.33; - logicOnLevel = 5; - logicOffLevel = 0; - } - - public InvertingSchmittElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f); - noDiagonal = true; - slewRate = .5; - lowerTrigger=1.66; - upperTrigger=3.33; - logicOnLevel = 5; - logicOffLevel = 0; - try { - slewRate = new Double (st.nextToken()).doubleValue(); - lowerTrigger = new Double (st.nextToken()).doubleValue(); - upperTrigger = new Double (st.nextToken()).doubleValue(); - logicOnLevel = new Double (st.nextToken()).doubleValue(); - logicOffLevel = new Double (st.nextToken()).doubleValue(); - } catch (Exception e) { - } - } - - String dump() { - return super.dump() + " " + slewRate+" "+lowerTrigger+" "+upperTrigger+" "+logicOnLevel+" "+logicOffLevel; - } - - int getDumpType() { return 183; }//Trying to find unused type - - void draw(Graphics g) { - drawPosts(g); - draw2Leads(g); - g.setColor(needsHighlight() ? selectColor : lightGrayColor); - drawThickPolygon(g, gatePoly); - g.setLineWidth(2); - drawPolygon(g, symbolPoly); - g.setLineWidth(1);; - drawThickCircle(g, pcircle.x, pcircle.y, 3); - curcount = updateDotCount(current, curcount); - drawDots(g, lead2, point2, curcount); - } - Polygon gatePoly; - Polygon symbolPoly; - Point pcircle; - void setPoints() { - super.setPoints(); - int hs = 16; - int ww = 16; - if (ww > dn/2) - ww = (int) (dn/2); - lead1 = interpPoint(point1, point2, .5-ww/dn); - lead2 = interpPoint(point1, point2, .5+(ww+2)/dn); - pcircle = interpPoint(point1, point2, .5+(ww-2)/dn); - Point triPoints[] = newPointArray(3); - interpPoint2(lead1, lead2, triPoints[0], triPoints[1], 0, hs); - triPoints[2] = interpPoint(point1, point2, .5+(ww-5)/dn); - - gatePoly = createPolygon(triPoints); - symbolPoly = getSchmittPolygon(1, .3f); - setBbox(point1, point2, hs); - } - int getVoltageSourceCount() { return 1; } - void stamp() { - sim.stampVoltageSource(0, nodes[1], voltSource); - } - void doStep() { - double v0 = volts[1]; - double out; - if(state) - {//Output is high - if(volts[0]>upperTrigger)//Input voltage high enough to set output low - { - state=false; - out=logicOffLevel; - } - else - { - out=logicOnLevel; - } - } - else - {//Output is low - if(volts[0]dut) - { - upperTrigger=dlt; - lowerTrigger=dut; - } - else - { - upperTrigger=dut; - lowerTrigger=dlt; - } - - } - // there is no current path through the InvertingSchmitt input, but there - // is an indirect path through the output to ground. - boolean getConnection(int n1, int n2) { return false; } - boolean hasGroundConnection(int n1) { - return (n1 == 1); - } - - @Override double getCurrentIntoNode(int n) { - if (n == 1) - return current; - return 0; - } +public class InvertingSchmittElm extends CircuitElm { + double slewRate; // V/ns + double lowerTrigger; + double upperTrigger; + boolean state; + double logicOnLevel; + double logicOffLevel; + public InvertingSchmittElm(int xx, int yy) { + super(xx, yy); + noDiagonal = true; + slewRate = .5; + state = false; + lowerTrigger = 1.66; + upperTrigger = 3.33; + logicOnLevel = 5; + logicOffLevel = 0; } + + public InvertingSchmittElm(int xa, int ya, int xb, int yb, int f, + StringTokenizer st) { + super(xa, ya, xb, yb, f); + noDiagonal = true; + slewRate = .5; + lowerTrigger = 1.66; + upperTrigger = 3.33; + logicOnLevel = 5; + logicOffLevel = 0; + try { + slewRate = new Double(st.nextToken()).doubleValue(); + lowerTrigger = new Double(st.nextToken()).doubleValue(); + upperTrigger = new Double(st.nextToken()).doubleValue(); + logicOnLevel = new Double(st.nextToken()).doubleValue(); + logicOffLevel = new Double(st.nextToken()).doubleValue(); + } catch (Exception e) { + } + } + + String dump() { + return super.dump() + " " + slewRate + " " + lowerTrigger + " " + upperTrigger + " " + logicOnLevel + " " + logicOffLevel; + } + + int getDumpType() { + return 183; + }//Trying to find unused type + + void draw(Graphics g) { + drawPosts(g); + draw2Leads(g); + g.setColor(needsHighlight() ? selectColor : lightGrayColor); + drawThickPolygon(g, gatePoly); + g.setLineWidth(2); + drawPolygon(g, symbolPoly); + g.setLineWidth(1); + ; + drawThickCircle(g, pcircle.x, pcircle.y, 3); + curcount = updateDotCount(current, curcount); + drawDots(g, lead2, point2, curcount); + } + + Polygon gatePoly; + Polygon symbolPoly; + Point pcircle; + + void setPoints() { + super.setPoints(); + int hs = 16; + int ww = 16; + if (ww > dn / 2) + ww = (int) (dn / 2); + lead1 = interpPoint(point1, point2, .5 - ww / dn); + lead2 = interpPoint(point1, point2, .5 + (ww + 2) / dn); + pcircle = interpPoint(point1, point2, .5 + (ww - 2) / dn); + Point triPoints[] = newPointArray(3); + interpPoint2(lead1, lead2, triPoints[0], triPoints[1], 0, hs); + triPoints[2] = interpPoint(point1, point2, .5 + (ww - 5) / dn); + + gatePoly = createPolygon(triPoints); + symbolPoly = getSchmittPolygon(1, .3f); + setBbox(point1, point2, hs); + } + + int getVoltageSourceCount() { + return 1; + } + + void stamp() { + sim.stampVoltageSource(0, nodes[1], voltSource); + } + + void doStep() { + double v0 = volts[1]; + double out; + if (state) {//Output is high + if (volts[0] > upperTrigger)//Input voltage high enough to set output low + { + state = false; + out = logicOffLevel; + } else { + out = logicOnLevel; + } + } else {//Output is low + if (volts[0] < lowerTrigger)//Input voltage low enough to set output high + { + state = true; + out = logicOnLevel; + } else { + out = logicOffLevel; + } + } + + double maxStep = slewRate * sim.timeStep * 1e9; + out = Math.max(Math.min(v0 + maxStep, out), v0 - maxStep); + sim.updateVoltageSource(0, nodes[1], voltSource, out); + } + + double getVoltageDiff() { + return volts[0]; + } + + void getInfo(String arr[]) { + arr[0] = "inverting Schmitt trigger"; + arr[1] = "Vi = " + getVoltageText(volts[0]); + arr[2] = "Vo = " + getVoltageText(volts[1]); + } + + public EditInfo getEditInfo(int n) { + if (n == 0) { + dlt = lowerTrigger; + return new EditInfo("Lower threshold (V)", lowerTrigger, 0.01, 5); + } + if (n == 1) { + dut = upperTrigger; + return new EditInfo("Upper threshold (V)", upperTrigger, 0.01, 5); + } + if (n == 2) + return new EditInfo("Slew Rate (V/ns)", slewRate, 0, 0); + if (n == 3) + return new EditInfo("High Logic Voltage", logicOnLevel, 0, 0); + if (n == 4) + return new EditInfo("Low Voltage (V)", logicOffLevel, 0, 0); + + return null; + } + + double dlt; + double dut; + + public void setEditValue(int n, EditInfo ei) { + if (n == 0) + dlt = ei.value; + if (n == 1) + dut = ei.value; + if (n == 2) + slewRate = ei.value; + if (n == 3) + logicOnLevel = ei.value; + if (n == 4) + logicOffLevel = ei.value; + + + if (dlt > dut) { + upperTrigger = dlt; + lowerTrigger = dut; + } else { + upperTrigger = dut; + lowerTrigger = dlt; + } + + } + + // there is no current path through the InvertingSchmitt input, but there + // is an indirect path through the output to ground. + boolean getConnection(int n1, int n2) { + return false; + } + + boolean hasGroundConnection(int n1) { + return (n1 == 1); + } + + @Override + double getCurrentIntoNode(int n) { + if (n == 1) + return current; + return 0; + } + +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/JKFlipFlopElm.java b/src/main/java/com/lushprojects/circuitjs1/client/JKFlipFlopElm.java index 4ba22ad..7ec2e56 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/JKFlipFlopElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/JKFlipFlopElm.java @@ -19,125 +19,151 @@ package com.lushprojects.circuitjs1.client; - class JKFlipFlopElm extends ChipElm { - final int FLAG_RESET = 2; - final int FLAG_POSITIVE_EDGE = 4; - final int FLAG_INVERT_RESET = 8; - boolean hasReset(){return (flags & FLAG_RESET)!= 0;} - boolean positiveEdgeTriggered() { return (flags & FLAG_POSITIVE_EDGE) != 0; } - boolean invertReset() { return (flags & FLAG_INVERT_RESET) != 0; } - public JKFlipFlopElm(int xx, int yy) { super(xx, yy); } - public JKFlipFlopElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f, st); - pins[4].value = !pins[3].value; - justLoaded = true; - } - String getChipName() { return "JK flip-flop"; } - void setupPins() { - sizeX = 2; - sizeY = 3; - pins = new Pin[getPostCount()]; - pins[0] = new Pin(0, SIDE_W, "J"); - pins[1] = new Pin(1, SIDE_W, ""); - pins[1].clock = true; - pins[1].bubble = !positiveEdgeTriggered(); - pins[2] = new Pin(2, SIDE_W, "K"); - pins[3] = new Pin(0, SIDE_E, "Q"); - pins[3].output = pins[3].state = true; - pins[4] = new Pin(2, SIDE_E, "Q"); - pins[4].output = true; - pins[4].lineOver = true; - - if(hasReset()){ - pins[5] = new Pin(1, SIDE_E, "R"); - pins[5].bubble = invertReset(); - } - } - int getPostCount() { return 5 + (hasReset() ? 1:0); } - int getVoltageSourceCount() { return 2; } - - boolean justLoaded; - - void execute() { - // if we just loaded then the volts[] array is likely to be all zeroes, which might force us to do a reset, so defer execution until the next iteration - if (justLoaded) { - justLoaded = false; - return; +public class JKFlipFlopElm extends ChipElm { + final int FLAG_RESET = 2; + final int FLAG_POSITIVE_EDGE = 4; + final int FLAG_INVERT_RESET = 8; + + boolean hasReset() { + return (flags & FLAG_RESET) != 0; + } + + boolean positiveEdgeTriggered() { + return (flags & FLAG_POSITIVE_EDGE) != 0; + } + + boolean invertReset() { + return (flags & FLAG_INVERT_RESET) != 0; + } + + public JKFlipFlopElm(int xx, int yy) { + super(xx, yy); + } + + public JKFlipFlopElm(int xa, int ya, int xb, int yb, int f, + StringTokenizer st) { + super(xa, ya, xb, yb, f, st); + pins[4].value = !pins[3].value; + justLoaded = true; + } + + String getChipName() { + return "JK flip-flop"; + } + + void setupPins() { + sizeX = 2; + sizeY = 3; + pins = new Pin[getPostCount()]; + pins[0] = new Pin(0, SIDE_W, "J"); + pins[1] = new Pin(1, SIDE_W, ""); + pins[1].clock = true; + pins[1].bubble = !positiveEdgeTriggered(); + pins[2] = new Pin(2, SIDE_W, "K"); + pins[3] = new Pin(0, SIDE_E, "Q"); + pins[3].output = pins[3].state = true; + pins[4] = new Pin(2, SIDE_E, "Q"); + pins[4].output = true; + pins[4].lineOver = true; + + if (hasReset()) { + pins[5] = new Pin(1, SIDE_E, "R"); + pins[5].bubble = invertReset(); + } + } + + int getPostCount() { + return 5 + (hasReset() ? 1 : 0); + } + + int getVoltageSourceCount() { + return 2; + } + + boolean justLoaded; + + void execute() { + // if we just loaded then the volts[] array is likely to be all zeroes, which might force us to do a reset, so defer execution until the next iteration + if (justLoaded) { + justLoaded = false; + return; + } + + boolean transition; + if (positiveEdgeTriggered()) + transition = pins[1].value && !lastClock; + else + transition = !pins[1].value && lastClock; + if (transition) { + boolean q = pins[3].value; + if (pins[0].value) { + if (pins[2].value) + q = !q; + else + q = true; + } else if (pins[2].value) + q = false; + writeOutput(3, q); + } + lastClock = pins[1].value; + + if (hasReset()) { + if (pins[5].value != invertReset()) + writeOutput(3, false); + } + + writeOutput(4, !pins[3].value); + } + + int getDumpType() { + return 156; + } + + public EditInfo getChipEditInfo(int n) { + if (n == 0) { + EditInfo ei = new EditInfo("", 0, -1, -1); + ei.checkbox = new Checkbox("Reset Pin", hasReset()); + return ei; + } + + if (n == 1) { + EditInfo ei = new EditInfo("", 0, -1, -1); + ei.checkbox = new Checkbox("Positive Edge Triggered", positiveEdgeTriggered()); + return ei; + } + + if (n == 2) { + EditInfo ei = new EditInfo("", 0, -1, -1); + ei.checkbox = new Checkbox("Invert Reset", invertReset()); + return ei; + } + + + return super.getChipEditInfo(n); + } + + public void setChipEditValue(int n, EditInfo ei) { + if (n == 0) { + if (ei.checkbox.getState()) { + flags |= FLAG_RESET; + } else { + flags &= ~FLAG_RESET; } - - boolean transition; - if (positiveEdgeTriggered()) - transition = pins[1].value && !lastClock; - else - transition = !pins[1].value && lastClock; - if (transition) { - boolean q = pins[3].value; - if (pins[0].value) { - if (pins[2].value) - q = !q; - else - q = true; - } else if (pins[2].value) - q = false; - writeOutput(3, q); - } - lastClock = pins[1].value; - - if(hasReset()){ - if(pins[5].value != invertReset()) - writeOutput(3, false); - } - - writeOutput(4, !pins[3].value); - } - int getDumpType() { return 156; } - - public EditInfo getChipEditInfo(int n){ - if (n == 0){ - EditInfo ei = new EditInfo("", 0, -1, -1); - ei.checkbox = new Checkbox("Reset Pin", hasReset()); - return ei; - } - - if (n == 1){ - EditInfo ei = new EditInfo("", 0, -1, -1); - ei.checkbox = new Checkbox("Positive Edge Triggered", positiveEdgeTriggered()); - return ei; - } - - if (n == 2){ - EditInfo ei = new EditInfo("", 0, -1, -1); - ei.checkbox = new Checkbox("Invert Reset", invertReset()); - return ei; - } - - - return super.getChipEditInfo(n); - } - - public void setChipEditValue(int n, EditInfo ei){ - if (n == 0){ - if(ei.checkbox.getState()){ - flags |= FLAG_RESET; - } else { - flags &= ~FLAG_RESET; - } - - setupPins(); - allocNodes(); - setPoints(); - } - if (n == 1) { - flags = ei.changeFlag(flags, FLAG_POSITIVE_EDGE); - pins[1].bubble = !positiveEdgeTriggered(); - } - if (n == 2) { - flags = ei.changeFlag(flags, FLAG_INVERT_RESET); - setupPins(); - setPoints(); - } - - super.setChipEditValue(n, ei); - } + + setupPins(); + allocNodes(); + setPoints(); + } + if (n == 1) { + flags = ei.changeFlag(flags, FLAG_POSITIVE_EDGE); + pins[1].bubble = !positiveEdgeTriggered(); + } + if (n == 2) { + flags = ei.changeFlag(flags, FLAG_INVERT_RESET); + setupPins(); + setPoints(); + } + + super.setChipEditValue(n, ei); } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/JfetElm.java b/src/main/java/com/lushprojects/circuitjs1/client/JfetElm.java index ac480fd..d2fc4c1 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/JfetElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/JfetElm.java @@ -21,131 +21,148 @@ import com.lushprojects.circuitjs1.client.util.Locale; -class JfetElm extends MosfetElm { - Diode diode; - double gateCurrent; - - JfetElm(int xx, int yy, boolean pnpflag) { - super(xx, yy, pnpflag); - noDiagonal = true; - diode = new Diode(sim); - diode.setupForDefaultModel(); - } - public JfetElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f, st); - noDiagonal = true; - diode = new Diode(sim); - diode.setupForDefaultModel(); - } - - void reset() { - super.reset(); - diode.reset(); - } - - Polygon gatePoly; - Polygon arrowPoly; - Point gatePt; - double curcountg, curcounts, curcountd; - - void draw(Graphics g) { - setBbox(point1, point2, hs); - setVoltageColor(g, volts[1]); - drawThickLine(g, src[0], src[1]); - drawThickLine(g, src[1], src[2]); - setVoltageColor(g, volts[2]); - drawThickLine(g, drn[0], drn[1]); - drawThickLine(g, drn[1], drn[2]); - setVoltageColor(g, volts[0]); - drawThickLine(g, point1, gatePt); - g.fillPolygon(arrowPoly); - setPowerColor(g, true); - g.fillPolygon(gatePoly); - curcountd = updateDotCount(-ids, curcountd); - curcountg = updateDotCount(gateCurrent, curcountg); - curcounts = updateDotCount(-gateCurrent-ids, curcounts); - if (curcountd != 0 || curcounts != 0) { - drawDots(g, src[0], src[1], curcounts); - drawDots(g, src[1], src[2], addCurCount(curcounts, 8)); - drawDots(g, drn[0], drn[1], -curcountd); - drawDots(g, drn[1], drn[2], -addCurCount(curcountd, 8)); - drawDots(g, point1, gatePt, curcountg); - } - drawPosts(g); - } - - double getCurrentIntoNode(int n) { - if (n == 0) - return -gateCurrent; - if (n == 1) - return gateCurrent+ids; - return -ids; - } - - void setPoints() { - super.setPoints(); - - // find the coordinates of the various points we need to draw - // the JFET. - int hs2 = hs*dsign; - src = newPointArray(3); - drn = newPointArray(3); - interpPoint2(point1, point2, src[0], drn[0], 1, -hs2); - interpPoint2(point1, point2, src[1], drn[1], 1, -hs2/2); - interpPoint2(point1, point2, src[2], drn[2], 1-10/dn, -hs2/2); - - gatePt = interpPoint(point1, point2, 1-14/dn); - - Point ra[] = newPointArray(4); - interpPoint2(point1, point2, ra[0], ra[1], 1-13/dn, hs); - interpPoint2(point1, point2, ra[2], ra[3], 1-10/dn, hs); - gatePoly = createPolygon(ra[0], ra[1], ra[3], ra[2]); - if (pnp == -1) { - Point x = interpPoint(gatePt, point1, 18/dn); - arrowPoly = calcArrow(gatePt, x, 8, 3); - } else - arrowPoly = calcArrow(point1, gatePt, 8, 3); - } - - void stamp() { - super.stamp(); - if (pnp < 0) - diode.stamp(nodes[1], nodes[0]); - else - diode.stamp(nodes[0], nodes[1]); - } - - void doStep() { - super.doStep(); - diode.doStep(pnp*(volts[0]-volts[1])); - } - - void calculateCurrent() { - gateCurrent = pnp*diode.calculateCurrent(pnp*(volts[0]-volts[1])); - } - - boolean showBulk() { return false; } - - int getDumpType() { return 'j'; } - // these values are taken from Hayes+Horowitz p155 - double getDefaultThreshold() { return -4; } - double getDefaultBeta() { return .00125; } - double getBackwardCompatibilityBeta() { return getDefaultBeta(); } - void getInfo(String arr[]) { - getFetInfo(arr, "JFET"); - } - public EditInfo getEditInfo(int n) { - if (n < 2) - return super.getEditInfo(n); - return null; +public class JfetElm extends MosfetElm { + Diode diode; + double gateCurrent; + + JfetElm(int xx, int yy, boolean pnpflag) { + super(xx, yy, pnpflag); + noDiagonal = true; + diode = new Diode(sim); + diode.setupForDefaultModel(); + } + + public JfetElm(int xa, int ya, int xb, int yb, int f, + StringTokenizer st) { + super(xa, ya, xb, yb, f, st); + noDiagonal = true; + diode = new Diode(sim); + diode.setupForDefaultModel(); + } + + void reset() { + super.reset(); + diode.reset(); + } + + Polygon gatePoly; + Polygon arrowPoly; + Point gatePt; + double curcountg, curcounts, curcountd; + + void draw(Graphics g) { + setBbox(point1, point2, hs); + setVoltageColor(g, volts[1]); + drawThickLine(g, src[0], src[1]); + drawThickLine(g, src[1], src[2]); + setVoltageColor(g, volts[2]); + drawThickLine(g, drn[0], drn[1]); + drawThickLine(g, drn[1], drn[2]); + setVoltageColor(g, volts[0]); + drawThickLine(g, point1, gatePt); + g.fillPolygon(arrowPoly); + setPowerColor(g, true); + g.fillPolygon(gatePoly); + curcountd = updateDotCount(-ids, curcountd); + curcountg = updateDotCount(gateCurrent, curcountg); + curcounts = updateDotCount(-gateCurrent - ids, curcounts); + if (curcountd != 0 || curcounts != 0) { + drawDots(g, src[0], src[1], curcounts); + drawDots(g, src[1], src[2], addCurCount(curcounts, 8)); + drawDots(g, drn[0], drn[1], -curcountd); + drawDots(g, drn[1], drn[2], -addCurCount(curcountd, 8)); + drawDots(g, point1, gatePt, curcountg); } - - boolean getConnection(int n1, int n2) { - return true; - } + drawPosts(g); + } + + double getCurrentIntoNode(int n) { + if (n == 0) + return -gateCurrent; + if (n == 1) + return gateCurrent + ids; + return -ids; + } + + void setPoints() { + super.setPoints(); + + // find the coordinates of the various points we need to draw + // the JFET. + int hs2 = hs * dsign; + src = newPointArray(3); + drn = newPointArray(3); + interpPoint2(point1, point2, src[0], drn[0], 1, -hs2); + interpPoint2(point1, point2, src[1], drn[1], 1, -hs2 / 2); + interpPoint2(point1, point2, src[2], drn[2], 1 - 10 / dn, -hs2 / 2); + + gatePt = interpPoint(point1, point2, 1 - 14 / dn); + + Point ra[] = newPointArray(4); + interpPoint2(point1, point2, ra[0], ra[1], 1 - 13 / dn, hs); + interpPoint2(point1, point2, ra[2], ra[3], 1 - 10 / dn, hs); + gatePoly = createPolygon(ra[0], ra[1], ra[3], ra[2]); + if (pnp == -1) { + Point x = interpPoint(gatePt, point1, 18 / dn); + arrowPoly = calcArrow(gatePt, x, 8, 3); + } else + arrowPoly = calcArrow(point1, gatePt, 8, 3); + } + + void stamp() { + super.stamp(); + if (pnp < 0) + diode.stamp(nodes[1], nodes[0]); + else + diode.stamp(nodes[0], nodes[1]); + } + + void doStep() { + super.doStep(); + diode.doStep(pnp * (volts[0] - volts[1])); + } + + void calculateCurrent() { + gateCurrent = pnp * diode.calculateCurrent(pnp * (volts[0] - volts[1])); + } + + boolean showBulk() { + return false; + } + + int getDumpType() { + return 'j'; + } + + // these values are taken from Hayes+Horowitz p155 + double getDefaultThreshold() { + return -4; + } + + double getDefaultBeta() { + return .00125; + } + + double getBackwardCompatibilityBeta() { + return getDefaultBeta(); + } + + void getInfo(String arr[]) { + getFetInfo(arr, "JFET"); + } + + public EditInfo getEditInfo(int n) { + if (n < 2) + return super.getEditInfo(n); + return null; + } + + boolean getConnection(int n1, int n2) { + return true; + } - @Override String getScopeText(int v) { - return Locale.LS(((pnp == -1) ? "p-" : "n-") + "JFET"); - } + @Override + String getScopeText(int v) { + return Locale.LS(((pnp == -1) ? "p-" : "n-") + "JFET"); } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/LDRElm.java b/src/main/java/com/lushprojects/circuitjs1/client/LDRElm.java index 9565943..a475a12 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/LDRElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/LDRElm.java @@ -9,203 +9,216 @@ /*Bill Collis - June 2015 */ -class LDRElm extends CircuitElm implements Command, MouseWheelHandler { +public class LDRElm extends CircuitElm implements Command, MouseWheelHandler { double position; //of the slider 0.005 to 0.995 double resistance; //based upon slider position double minLux, maxLux; double lux; - Scrollbar slider; + Scrollbar slider; Label label; String sliderText; //constructor - when initially created public LDRElm(int xx, int yy) { - super(xx, yy); - //setup(); - minLux = 0.1; //dark - maxLux = 10000; // sunlight - position = .34; + super(xx, yy); + //setup(); + minLux = 0.1; //dark + maxLux = 10000; // sunlight + position = .34; - lux = LuxFromSliderPos(); - resistance = calcResistance(lux); - sliderText = "Light Brightness"; - createSlider(); + lux = LuxFromSliderPos(); + resistance = calcResistance(lux); + sliderText = "Light Brightness"; + createSlider(); } //constructor - when read in from file public LDRElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f); - minLux = 0.1; //dark - maxLux = 10000; // sunlight - position = new Double(st.nextToken()).doubleValue(); - lux = LuxFromSliderPos(); - resistance = calcResistance(lux); - sliderText = CustomLogicModel.unescape(st.nextToken()); - createSlider(); //uses position to set the slider + StringTokenizer st) { + super(xa, ya, xb, yb, f); + minLux = 0.1; //dark + maxLux = 10000; // sunlight + position = new Double(st.nextToken()).doubleValue(); + lux = LuxFromSliderPos(); + resistance = calcResistance(lux); + sliderText = CustomLogicModel.unescape(st.nextToken()); + createSlider(); //uses position to set the slider } //void setup() { //} - int getPostCount() { return 2; } - int getDumpType() { return 374; } //LDR + int getPostCount() { + return 2; + } + + int getDumpType() { + return 374; + } //LDR //data for file saving - make sure it matches order of items in file input constructor - String dump() { - return super.dump() + " " + position + " " + CustomLogicModel.escape(sliderText); + String dump() { + return super.dump() + " " + position + " " + CustomLogicModel.escape(sliderText); } void createSlider() { - sim.addWidgetToVerticalPanel(label = new Label(sliderText)); - label.addStyleName("topSpace"); - int value = (int) (position*100); - sim.addWidgetToVerticalPanel(slider = new Scrollbar(Scrollbar.HORIZONTAL, value, 1, 0, 101, this, this)); + sim.addWidgetToVerticalPanel(label = new Label(sliderText)); + label.addStyleName("topSpace"); + int value = (int) (position * 100); + sim.addWidgetToVerticalPanel(slider = new Scrollbar(Scrollbar.HORIZONTAL, value, 1, 0, 101, this, this)); } public void execute() { - sim.analyzeFlag = true; - setPoints(); + sim.analyzeFlag = true; + setPoints(); } void delete() { - sim.removeWidgetFromVerticalPanel(label); - sim.removeWidgetFromVerticalPanel(slider); + sim.removeWidgetFromVerticalPanel(label); + sim.removeWidgetFromVerticalPanel(slider); } - Point ps3, ps4; + + Point ps3, ps4; //called straight after constructor when txt file is loaded void setPoints() { - super.setPoints(); - calcLeads(32); - position = slider.getValue()*.0099+.0001; - lux = LuxFromSliderPos(); - resistance = calcResistance(lux); - ps3 = new Point(); - ps4 = new Point(); + super.setPoints(); + calcLeads(32); + position = slider.getValue() * .0099 + .0001; + lux = LuxFromSliderPos(); + resistance = calcResistance(lux); + ps3 = new Point(); + ps4 = new Point(); } + Polygon arrowPoly; + void draw(Graphics g) { //used Resistor draw - //int segments = 16; - int i; - //int ox = 0; - int hs=6; //width - double v1 = volts[0]; - double v2 = volts[1]; - setBbox(point1, point2, hs); //the two points that are there when the device is being created - draw2Leads(g); //from point1 to lead1 and lead1 to point2 (lead1&2 are on the body) - setPowerColor(g, true); - double len = distance(lead1, lead2); - g.context.save(); - g.context.setLineWidth(3.0); - g.context.transform(((double)(lead2.x-lead1.x))/len, ((double)(lead2.y-lead1.y))/len, -((double)(lead2.y-lead1.y))/len,((double)(lead2.x-lead1.x))/len,lead1.x,lead1.y); - CanvasGradient grad = g.context.createLinearGradient(0,0,len,0); - grad.addColorStop(0, getVoltageColor(g,v1).getHexValue()); - grad.addColorStop(1.0, getVoltageColor(g,v2).getHexValue()); - g.context.setStrokeStyle(grad); - if (!sim.euroResistorCheckItem.getState()) { - g.context.beginPath(); - g.context.moveTo(0,0); - for (i=0;i<4;i++){ - g.context.lineTo((1+4*i)*len/16, hs); - g.context.lineTo((3+4*i)*len/16, -hs); - } - g.context.lineTo(len, 0); - g.context.stroke(); - - } else { - g.context.strokeRect(0, -hs, len, 2.0*hs); //draw the box for the euro resistor - } - - g.context.beginPath(); //thermistor symbol lines 0 is in the middle of the left handside of the resistor box - //upper arrow - g.context.moveTo(-8,26); //arrow1 start (y,x coordinates from center?) - g.context.lineTo(8,12); //arrow end point - g.context.moveTo(2,12); //arrow 1 head - g.context.lineTo(8,12); //arrow end point - g.context.lineTo(8,18); - g.context.moveTo(12,26); //arrow2 start (y,x coordinates from center?) - g.context.lineTo(26,12); //arrow end point - g.context.moveTo(20,12); //arrow 1 head - g.context.lineTo(26,12); //arrow end point - g.context.lineTo(26,18); - - g.context.stroke(); - - - g.context.restore(); - if (sim.showValuesCheckItem.getState()) { - lux = LuxFromSliderPos(); - resistance = calcResistance(lux); - String s = getShortUnitText(resistance, ""); - drawValues(g, s+"\u03A9", hs); - } - doDots(g); - drawPosts(g); + //int segments = 16; + int i; + //int ox = 0; + int hs = 6; //width + double v1 = volts[0]; + double v2 = volts[1]; + setBbox(point1, point2, hs); //the two points that are there when the device is being created + draw2Leads(g); //from point1 to lead1 and lead1 to point2 (lead1&2 are on the body) + setPowerColor(g, true); + double len = distance(lead1, lead2); + g.context.save(); + g.context.setLineWidth(3.0); + g.context.transform(((double) (lead2.x - lead1.x)) / len, ((double) (lead2.y - lead1.y)) / len, -((double) (lead2.y - lead1.y)) / len, ((double) (lead2.x - lead1.x)) / len, lead1.x, lead1.y); + CanvasGradient grad = g.context.createLinearGradient(0, 0, len, 0); + grad.addColorStop(0, getVoltageColor(g, v1).getHexValue()); + grad.addColorStop(1.0, getVoltageColor(g, v2).getHexValue()); + g.context.setStrokeStyle(grad); + if (!sim.euroResistorCheckItem.getState()) { + g.context.beginPath(); + g.context.moveTo(0, 0); + for (i = 0; i < 4; i++) { + g.context.lineTo((1 + 4 * i) * len / 16, hs); + g.context.lineTo((3 + 4 * i) * len / 16, -hs); + } + g.context.lineTo(len, 0); + g.context.stroke(); + + } else { + g.context.strokeRect(0, -hs, len, 2.0 * hs); //draw the box for the euro resistor + } + + g.context.beginPath(); //thermistor symbol lines 0 is in the middle of the left handside of the resistor box + //upper arrow + g.context.moveTo(-8, 26); //arrow1 start (y,x coordinates from center?) + g.context.lineTo(8, 12); //arrow end point + g.context.moveTo(2, 12); //arrow 1 head + g.context.lineTo(8, 12); //arrow end point + g.context.lineTo(8, 18); + g.context.moveTo(12, 26); //arrow2 start (y,x coordinates from center?) + g.context.lineTo(26, 12); //arrow end point + g.context.moveTo(20, 12); //arrow 1 head + g.context.lineTo(26, 12); //arrow end point + g.context.lineTo(26, 18); + + g.context.stroke(); + + + g.context.restore(); + if (sim.showValuesCheckItem.getState()) { + lux = LuxFromSliderPos(); + resistance = calcResistance(lux); + String s = getShortUnitText(resistance, ""); + drawValues(g, s + "\u03A9", hs); + } + doDots(g); + drawPosts(g); } void calculateCurrent() { - current = (volts[0]-volts[1])/resistance; + current = (volts[0] - volts[1]) / resistance; } + void stamp() { - lux = LuxFromSliderPos(); - resistance = calcResistance(lux); - sim.stampResistor(nodes[0], nodes[1], resistance); + lux = LuxFromSliderPos(); + resistance = calcResistance(lux); + sim.stampResistor(nodes[0], nodes[1], resistance); } void getInfo(String arr[]) { - arr[0] = "photoresistor"; - arr[1] = "I = "+ getCurrentDText(current); //getBasicInfo(arr); - arr[2] = "Vd = "+ getVoltageDText(getVoltageDiff()); - arr[3] = "R = " + getUnitText(resistance, Locale.ohmString); - arr[4] = "P = " + getUnitText(getPower(), "W"); + arr[0] = "photoresistor"; + arr[1] = "I = " + getCurrentDText(current); //getBasicInfo(arr); + arr[2] = "Vd = " + getVoltageDText(getVoltageDiff()); + arr[3] = "R = " + getUnitText(resistance, Locale.ohmString); + arr[4] = "P = " + getUnitText(getPower(), "W"); } + public EditInfo getEditInfo(int n) { - if (n == 0) { - EditInfo ei = new EditInfo("Slider Text", 0, -1, -1); - ei.text = sliderText; - return ei; - } - return null; + if (n == 0) { + EditInfo ei = new EditInfo("Slider Text", 0, -1, -1); + ei.text = sliderText; + return ei; + } + return null; } + //component edited public void setEditValue(int n, EditInfo ei) { - if (n == 0) { - sliderText = ei.textf.getText(); - label.setText(sliderText); - sim.setSlidersPanelHeight(); - } - lux = LuxFromSliderPos(); - resistance = calcResistance(lux); + if (n == 0) { + sliderText = ei.textf.getText(); + label.setText(sliderText); + sim.setSlidersPanelHeight(); + } + lux = LuxFromSliderPos(); + resistance = calcResistance(lux); } + void setMouseElm(boolean v) { - super.setMouseElm(v); - if (slider!=null) - slider.draw(); + super.setMouseElm(v); + if (slider != null) + slider.draw(); } public void onMouseWheel(MouseWheelEvent e) { - if (slider!=null) - slider.onMouseWheel(e); + if (slider != null) + slider.onMouseWheel(e); } double calcResistance(double lux) //knowing the lux { - //double loglux = Math.log10(lux); - //double slope = -1.4; - //double intercept = 7.1; - //double logR = (loglux-intercept)/slope; + //double loglux = Math.log10(lux); + //double slope = -1.4; + //double intercept = 7.1; + //double logR = (loglux-intercept)/slope; - //return Math.round(Math.pow(10, logR)); - double r = (maxLux-lux+1)*10; + //return Math.round(Math.pow(10, logR)); + double r = (maxLux - lux + 1) * 10; - r = Math.round(r); - return r; + r = Math.round(r); + return r; } + double LuxFromSliderPos() //knowing slider position etc { - return maxLux * position + minLux ; + return maxLux * position + minLux; } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/LEDArrayElm.java b/src/main/java/com/lushprojects/circuitjs1/client/LEDArrayElm.java index bb3adc2..c1b1df6 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/LEDArrayElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/LEDArrayElm.java @@ -19,168 +19,191 @@ package com.lushprojects.circuitjs1.client; - class LEDArrayElm extends ChipElm { - public LEDArrayElm(int xx, int yy) { - super(xx, yy); - } - public LEDArrayElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f, st); - try { - sizeX = Integer.parseInt(st.nextToken()); - sizeY = Integer.parseInt(st.nextToken()); - } catch (Exception e) {} - allocNodes(); - setupPins(); - setPoints(); - } - - String dump() { return super.dump() + " " + sizeX + " " + sizeY ; } - - String getChipName() { return "LED array"; } - - void setupPins() { - if (sizeX == 0 || sizeY == 0) { - sizeX = sizeY = 8; - allocNodes(); - } - pins = new Pin[sizeX+sizeY]; - int i; - for (i = 0; i != sizeX; i++) - pins[i] = new Pin(i, SIDE_S, ""); - for (i = 0; i != sizeY; i++) - pins[i+sizeX] = new Pin(i, SIDE_W, ""); - brightness = new double[sizeX*sizeY]; - } - - Diode diodes[]; - double currents[]; - double brightness[]; - - void stamp() { - super.stamp(); - - // create grid of diodes - diodes = new Diode[sizeX*sizeY]; - int i; - DiodeModel model = DiodeModel.getModelWithName("default-led"); - for (i = 0; i != diodes.length; i++) { - diodes[i] = new Diode(sim); - diodes[i].setup(model); - diodes[i].stamp(nodes[sizeX+(i / sizeX)], nodes[i % sizeX]); - } - currents = new double[diodes.length]; - } - void doStep() { - super.doStep(); - - int ix, iy, i = 0; - for (iy = 0; iy != sizeY; iy++) - for (ix = 0; ix != sizeX; ix++, i++) - diodes[i].doStep(volts[sizeX+iy]-volts[ix]); - } - boolean nonLinear() { return true; } - @Override boolean isDigitalChip() { return false; } - - void draw(Graphics g) { - drawChip(g); - int ix, iy; - for (ix = 0; ix != sizeX; ix++) - for (iy = 0; iy != sizeY; iy++) { - int i = ix+iy*sizeX; - setColor(g, i); - if (isFlippedXY()) - g.fillOval(pins[iy+sizeX].post.x-cspc/2, pins[ix].post.y-cspc/2, cspc, cspc); - else - g.fillOval(pins[ix].post.x-cspc/2, pins[iy+sizeX].post.y-cspc/2, cspc, cspc); - } - } - - void calculateCurrent() { - // calculate diode currents - int ix, iy, i = 0; - for (ix = 0; ix != sizeX; ix++) - pins[ix].current = 0; - - // avoid exception if this is called before stamp() - if (diodes == null) - return; - - for (iy = 0; iy != sizeY; iy++) { - double cur = 0; - for (ix = 0; ix != sizeX; ix++, i++) { - currents[i] = diodes[i].calculateCurrent(volts[sizeX+iy]-volts[ix]); - cur += currents[i]; - pins[ix].current += currents[i]; - } - pins[iy+sizeX].current = -cur; - } - } - - void stepFinished() { - // stop for huge currents that make simulator act weird - int i; - for (i = 0; i != currents.length; i++) - if (Math.abs(currents[i]) > 1e12) - sim.stop("max current exceeded", this); - } - - void setColor(Graphics g, int p) { - // 10mA current = max brightness - if (currents == null) { - g.setColor(new Color(20, 0, 0)); - return; - } - double w = currents[p] / .01; - if (w > 0) - w = 255*(1+.2*Math.log(w)); - if (w > 255) - w = 255; - if (w < 20) - w = 20; - - // when diode turns off, made it fade gradually to simulate persistence of vision - w = Math.max(w, brightness[p]); - brightness[p] = w*.99; - - Color cc = new Color((int) w, 0, 0); - g.setColor(cc); - } - int getPostCount() { return sizeX+sizeY; } - int getVoltageSourceCount() { return 0; } - int getDumpType() { return 405; } - - // this is true but it causes strange behavior with unconnected pins so we don't do it -// boolean getConnection(int n1, int n2) { return true; } - - public EditInfo getChipEditInfo(int n) { - if (n == 0) - return new EditInfo("Grid Width", sizeX).setDimensionless(); - if (n == 1) - return new EditInfo("Grid Height", sizeY).setDimensionless(); - return null; - } - - public void setChipEditValue(int n, EditInfo ei) { - if (n == 0 && ei.value >= 2 && ei.value <= 16) { - sizeX = (int)ei.value; - allocNodes(); - setupPins(); - setPoints(); - return; +public class LEDArrayElm extends ChipElm { + public LEDArrayElm(int xx, int yy) { + super(xx, yy); + } + + public LEDArrayElm(int xa, int ya, int xb, int yb, int f, + StringTokenizer st) { + super(xa, ya, xb, yb, f, st); + try { + sizeX = Integer.parseInt(st.nextToken()); + sizeY = Integer.parseInt(st.nextToken()); + } catch (Exception e) { + } + allocNodes(); + setupPins(); + setPoints(); + } + + String dump() { + return super.dump() + " " + sizeX + " " + sizeY; + } + + String getChipName() { + return "LED array"; + } + + void setupPins() { + if (sizeX == 0 || sizeY == 0) { + sizeX = sizeY = 8; + allocNodes(); + } + pins = new Pin[sizeX + sizeY]; + int i; + for (i = 0; i != sizeX; i++) + pins[i] = new Pin(i, SIDE_S, ""); + for (i = 0; i != sizeY; i++) + pins[i + sizeX] = new Pin(i, SIDE_W, ""); + brightness = new double[sizeX * sizeY]; + } + + Diode diodes[]; + double currents[]; + double brightness[]; + + void stamp() { + super.stamp(); + + // create grid of diodes + diodes = new Diode[sizeX * sizeY]; + int i; + DiodeModel model = DiodeModel.getModelWithName("default-led"); + for (i = 0; i != diodes.length; i++) { + diodes[i] = new Diode(sim); + diodes[i].setup(model); + diodes[i].stamp(nodes[sizeX + (i / sizeX)], nodes[i % sizeX]); + } + currents = new double[diodes.length]; + } + + void doStep() { + super.doStep(); + + int ix, iy, i = 0; + for (iy = 0; iy != sizeY; iy++) + for (ix = 0; ix != sizeX; ix++, i++) + diodes[i].doStep(volts[sizeX + iy] - volts[ix]); + } + + boolean nonLinear() { + return true; + } + + @Override + boolean isDigitalChip() { + return false; + } + + void draw(Graphics g) { + drawChip(g); + int ix, iy; + for (ix = 0; ix != sizeX; ix++) + for (iy = 0; iy != sizeY; iy++) { + int i = ix + iy * sizeX; + setColor(g, i); + if (isFlippedXY()) + g.fillOval(pins[iy + sizeX].post.x - cspc / 2, pins[ix].post.y - cspc / 2, cspc, cspc); + else + g.fillOval(pins[ix].post.x - cspc / 2, pins[iy + sizeX].post.y - cspc / 2, cspc, cspc); } - if (n == 1 && ei.value >= 2 && ei.value <= 16) { - sizeY = (int)ei.value; - allocNodes(); - setupPins(); - setPoints(); - return; + } + + void calculateCurrent() { + // calculate diode currents + int ix, iy, i = 0; + for (ix = 0; ix != sizeX; ix++) + pins[ix].current = 0; + + // avoid exception if this is called before stamp() + if (diodes == null) + return; + + for (iy = 0; iy != sizeY; iy++) { + double cur = 0; + for (ix = 0; ix != sizeX; ix++, i++) { + currents[i] = diodes[i].calculateCurrent(volts[sizeX + iy] - volts[ix]); + cur += currents[i]; + pins[ix].current += currents[i]; } - } - - // default getInfo doesn't work because the pins are unlabeled - void getInfo(String arr[]) { - arr[0] = getChipName(); - return; - } + pins[iy + sizeX].current = -cur; + } + } + + void stepFinished() { + // stop for huge currents that make simulator act weird + int i; + for (i = 0; i != currents.length; i++) + if (Math.abs(currents[i]) > 1e12) + sim.stop("max current exceeded", this); + } + + void setColor(Graphics g, int p) { + // 10mA current = max brightness + if (currents == null) { + g.setColor(new Color(20, 0, 0)); + return; + } + double w = currents[p] / .01; + if (w > 0) + w = 255 * (1 + .2 * Math.log(w)); + if (w > 255) + w = 255; + if (w < 20) + w = 20; + + // when diode turns off, made it fade gradually to simulate persistence of vision + w = Math.max(w, brightness[p]); + brightness[p] = w * .99; + + Color cc = new Color((int) w, 0, 0); + g.setColor(cc); + } + + int getPostCount() { + return sizeX + sizeY; + } + + int getVoltageSourceCount() { + return 0; + } + + int getDumpType() { + return 405; + } + + // this is true but it causes strange behavior with unconnected pins so we don't do it +// boolean getConnection(int n1, int n2) { return true; } + + public EditInfo getChipEditInfo(int n) { + if (n == 0) + return new EditInfo("Grid Width", sizeX).setDimensionless(); + if (n == 1) + return new EditInfo("Grid Height", sizeY).setDimensionless(); + return null; + } + + public void setChipEditValue(int n, EditInfo ei) { + if (n == 0 && ei.value >= 2 && ei.value <= 16) { + sizeX = (int) ei.value; + allocNodes(); + setupPins(); + setPoints(); + return; + } + if (n == 1 && ei.value >= 2 && ei.value <= 16) { + sizeY = (int) ei.value; + allocNodes(); + setupPins(); + setPoints(); + return; + } + } + + // default getInfo doesn't work because the pins are unlabeled + void getInfo(String arr[]) { + arr[0] = getChipName(); + return; } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/LEDElm.java b/src/main/java/com/lushprojects/circuitjs1/client/LEDElm.java index 51b672f..0d93764 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/LEDElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/LEDElm.java @@ -21,118 +21,130 @@ import com.lushprojects.circuitjs1.client.util.Locale; -class LEDElm extends DiodeElm { - double colorR, colorG, colorB, maxBrightnessCurrent; - static String lastLEDModelName = "default-led"; - - public LEDElm(int xx, int yy) { - super(xx, yy); - modelName = lastLEDModelName; - setup(); - maxBrightnessCurrent = .01; - colorR = 1; colorG = colorB = 0; - } - public LEDElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f, st); - if ((f & (FLAG_MODEL|FLAG_FWDROP)) == 0) { - final double fwdrop = 2.1024259; - model = DiodeModel.getModelWithParameters(fwdrop, 0); - modelName = model.name; - CirSim.console("model name wparams = " + modelName); - setup(); - } - colorR = new Double(st.nextToken()).doubleValue(); - colorG = new Double(st.nextToken()).doubleValue(); - colorB = new Double(st.nextToken()).doubleValue(); - maxBrightnessCurrent = .01; - try { - maxBrightnessCurrent = new Double(st.nextToken()).doubleValue(); - } catch (Exception e) { } - } - int getDumpType() { return 162; } - String dump() { - return super.dump() + " " + colorR + " " + colorG + " " + colorB + " " + - maxBrightnessCurrent; - } - - Point ledLead1, ledLead2, ledCenter; - void setPoints() { - super.setPoints(); - int cr = 12; - ledLead1 = interpPoint(point1, point2, .5-cr/dn); - ledLead2 = interpPoint(point1, point2, .5+cr/dn); - ledCenter = interpPoint(point1, point2, .5); - } - - void draw(Graphics g) { - if (needsHighlight() || this == sim.dragElm) { - super.draw(g); - return; - } - setVoltageColor(g, volts[0]); - drawThickLine(g, point1, ledLead1); - setVoltageColor(g, volts[1]); - drawThickLine(g, ledLead2, point2); - - g.setColor(Color.gray); - int cr = 12; - drawThickCircle(g, ledCenter.x, ledCenter.y, cr); - cr -= 4; - double w = current/maxBrightnessCurrent; - if (w > 0) - w = 255*(1+.2*Math.log(w)); - if (w > 255) - w = 255; - if (w < 0) - w = 0; - Color cc = new Color((int) (colorR*w), (int) (colorG*w), - (int) (colorB*w)); - g.setColor(cc); - g.fillOval(ledCenter.x-cr, ledCenter.y-cr, cr*2, cr*2); - setBbox(point1, point2, cr); - updateDotCount(); - drawDots(g, point1, ledLead1, curcount); - drawDots(g, point2, ledLead2, -curcount); - drawPosts(g); - } - - void getInfo(String arr[]) { - super.getInfo(arr); - if (model.oldStyle) - arr[0] = "LED"; - else - arr[0] = Locale.LS("LED") + " (" + modelName + ")"; - } - - public EditInfo getEditInfo(int n) { - if (n == 0) - return new EditInfo("Red Value (0-1)", colorR, 0, 1). - setDimensionless(); - if (n == 1) - return new EditInfo("Green Value (0-1)", colorG, 0, 1). - setDimensionless(); - if (n == 2) - return new EditInfo("Blue Value (0-1)", colorB, 0, 1). - setDimensionless(); - if (n == 3) - return new EditInfo("Max Brightness Current (A)", maxBrightnessCurrent, 0, .1); - return super.getEditInfo(n-4); - } - public void setEditValue(int n, EditInfo ei) { - if (n == 0) - colorR = ei.value; - if (n == 1) - colorG = ei.value; - if (n == 2) - colorB = ei.value; - if (n == 3) - maxBrightnessCurrent = ei.value; - super.setEditValue(n-4, ei); - } - int getShortcut() { return 'l'; } - - void setLastModelName(String n) { - lastLEDModelName = n; - } +public class LEDElm extends DiodeElm { + double colorR, colorG, colorB, maxBrightnessCurrent; + static String lastLEDModelName = "default-led"; + + public LEDElm(int xx, int yy) { + super(xx, yy); + modelName = lastLEDModelName; + setup(); + maxBrightnessCurrent = .01; + colorR = 1; + colorG = colorB = 0; + } + + public LEDElm(int xa, int ya, int xb, int yb, int f, + StringTokenizer st) { + super(xa, ya, xb, yb, f, st); + if ((f & (FLAG_MODEL | FLAG_FWDROP)) == 0) { + final double fwdrop = 2.1024259; + model = DiodeModel.getModelWithParameters(fwdrop, 0); + modelName = model.name; + CirSim.console("model name wparams = " + modelName); + setup(); + } + colorR = new Double(st.nextToken()).doubleValue(); + colorG = new Double(st.nextToken()).doubleValue(); + colorB = new Double(st.nextToken()).doubleValue(); + maxBrightnessCurrent = .01; + try { + maxBrightnessCurrent = new Double(st.nextToken()).doubleValue(); + } catch (Exception e) { + } + } + + int getDumpType() { + return 162; + } + + String dump() { + return super.dump() + " " + colorR + " " + colorG + " " + colorB + " " + + maxBrightnessCurrent; + } + + Point ledLead1, ledLead2, ledCenter; + + void setPoints() { + super.setPoints(); + int cr = 12; + ledLead1 = interpPoint(point1, point2, .5 - cr / dn); + ledLead2 = interpPoint(point1, point2, .5 + cr / dn); + ledCenter = interpPoint(point1, point2, .5); + } + + void draw(Graphics g) { + if (needsHighlight() || this == sim.dragElm) { + super.draw(g); + return; + } + setVoltageColor(g, volts[0]); + drawThickLine(g, point1, ledLead1); + setVoltageColor(g, volts[1]); + drawThickLine(g, ledLead2, point2); + + g.setColor(Color.gray); + int cr = 12; + drawThickCircle(g, ledCenter.x, ledCenter.y, cr); + cr -= 4; + double w = current / maxBrightnessCurrent; + if (w > 0) + w = 255 * (1 + .2 * Math.log(w)); + if (w > 255) + w = 255; + if (w < 0) + w = 0; + Color cc = new Color((int) (colorR * w), (int) (colorG * w), + (int) (colorB * w)); + g.setColor(cc); + g.fillOval(ledCenter.x - cr, ledCenter.y - cr, cr * 2, cr * 2); + setBbox(point1, point2, cr); + updateDotCount(); + drawDots(g, point1, ledLead1, curcount); + drawDots(g, point2, ledLead2, -curcount); + drawPosts(g); + } + + void getInfo(String arr[]) { + super.getInfo(arr); + if (model.oldStyle) + arr[0] = "LED"; + else + arr[0] = Locale.LS("LED") + " (" + modelName + ")"; + } + + public EditInfo getEditInfo(int n) { + if (n == 0) + return new EditInfo("Red Value (0-1)", colorR, 0, 1). + setDimensionless(); + if (n == 1) + return new EditInfo("Green Value (0-1)", colorG, 0, 1). + setDimensionless(); + if (n == 2) + return new EditInfo("Blue Value (0-1)", colorB, 0, 1). + setDimensionless(); + if (n == 3) + return new EditInfo("Max Brightness Current (A)", maxBrightnessCurrent, 0, .1); + return super.getEditInfo(n - 4); + } + + public void setEditValue(int n, EditInfo ei) { + if (n == 0) + colorR = ei.value; + if (n == 1) + colorG = ei.value; + if (n == 2) + colorB = ei.value; + if (n == 3) + maxBrightnessCurrent = ei.value; + super.setEditValue(n - 4, ei); + } + + int getShortcut() { + return 'l'; + } + + void setLastModelName(String n) { + lastLEDModelName = n; } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/LabeledNodeElm.java b/src/main/java/com/lushprojects/circuitjs1/client/LabeledNodeElm.java index 2f6d561..45a575d 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/LabeledNodeElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/LabeledNodeElm.java @@ -20,43 +20,49 @@ package com.lushprojects.circuitjs1.client; import java.util.HashMap; + import com.lushprojects.circuitjs1.client.util.Locale; -class LabeledNodeElm extends CircuitElm { +public class LabeledNodeElm extends CircuitElm { final int FLAG_ESCAPE = 4; final int FLAG_INTERNAL = 1; - + public LabeledNodeElm(int xx, int yy) { - super(xx, yy); - text = "label"; + super(xx, yy); + text = "label"; } + public LabeledNodeElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f); - text = st.nextToken(); - if ((flags & FLAG_ESCAPE) == 0) { - // old-style dump before escape/unescape - while (st.hasMoreTokens()) - text += ' ' + st.nextToken(); - } else { - // new-style dump - text = CustomLogicModel.unescape(text); - } + StringTokenizer st) { + super(xa, ya, xb, yb, f); + text = st.nextToken(); + if ((flags & FLAG_ESCAPE) == 0) { + // old-style dump before escape/unescape + while (st.hasMoreTokens()) + text += ' ' + st.nextToken(); + } else { + // new-style dump + text = CustomLogicModel.unescape(text); + } } + String dump() { - flags |= FLAG_ESCAPE; - return super.dump() + " " + CustomLogicModel.escape(text); + flags |= FLAG_ESCAPE; + return super.dump() + " " + CustomLogicModel.escape(text); } String text; - + class LabelEntry { - Point point; - int node; + Point point; + int node; + } + + static HashMap labelList; + + boolean isInternal() { + return (flags & FLAG_INTERNAL) != 0; } - - static HashMap labelList; - boolean isInternal() { return (flags & FLAG_INTERNAL) != 0; } public static native void console(String text) /*-{ @@ -64,98 +70,125 @@ public static native void console(String text) }-*/; static void resetNodeList() { - labelList = new HashMap(); + labelList = new HashMap(); } + final int circleSize = 17; + void setPoints() { - super.setPoints(); - lead1 = interpPoint(point1, point2, 1-circleSize/dn); + super.setPoints(); + lead1 = interpPoint(point1, point2, 1 - circleSize / dn); } - + // get post we're connected to Point getConnectedPost() { - LabelEntry le = labelList.get(text); - if (le != null) - return le.point; - - // this is the first time calcWireClosure() encountered this label. so save point1 and - // return null for now, but return point1 the next time we see this label so that all nodes - // with the same label are connected - le = new LabelEntry(); - le.point = point1; - labelList.put(text, le); - return null; - } - + LabelEntry le = labelList.get(text); + if (le != null) + return le.point; + + // this is the first time calcWireClosure() encountered this label. so save point1 and + // return null for now, but return point1 the next time we see this label so that all nodes + // with the same label are connected + le = new LabelEntry(); + le.point = point1; + labelList.put(text, le); + return null; + } + void setNode(int p, int n) { - super.setNode(p, n); - - // save node number so we can return it in getByName() - LabelEntry le = labelList.get(text); - if (le != null) // should never happen - le.node = n; - } - - int getDumpType() { return 207; } - int getPostCount() { return 1; } - + super.setNode(p, n); + + // save node number so we can return it in getByName() + LabelEntry le = labelList.get(text); + if (le != null) // should never happen + le.node = n; + } + + int getDumpType() { + return 207; + } + + int getPostCount() { + return 1; + } + // this is basically a wire, since it just connects two or more nodes together - boolean isWireEquivalent() { return true; } - boolean isRemovableWire() { return true; } - + boolean isWireEquivalent() { + return true; + } + + boolean isRemovableWire() { + return true; + } + static Integer getByName(String n) { - if (labelList == null) - return null; - LabelEntry le = labelList.get(n); - if (le == null) - return null; - return le.node; - } - + if (labelList == null) + return null; + LabelEntry le = labelList.get(n); + if (le == null) + return null; + return le.node; + } + void draw(Graphics g) { - setVoltageColor(g, volts[0]); - drawThickLine(g, point1, lead1); - g.setColor(needsHighlight() ? selectColor : whiteColor); - setPowerColor(g, false); - interpPoint(point1, point2, ps2, 1+11./dn); - setBbox(point1, ps2, circleSize); - drawLabeledNode(g, text, point1, lead1); - - curcount = updateDotCount(current, curcount); - drawDots(g, point1, lead1, curcount); - drawPosts(g); - } - double getCurrentIntoNode(int n) { return -current; } - void setCurrent(int x, double c) { current = c; } - double getVoltageDiff() { return volts[0]; } + setVoltageColor(g, volts[0]); + drawThickLine(g, point1, lead1); + g.setColor(needsHighlight() ? selectColor : whiteColor); + setPowerColor(g, false); + interpPoint(point1, point2, ps2, 1 + 11. / dn); + setBbox(point1, ps2, circleSize); + drawLabeledNode(g, text, point1, lead1); + + curcount = updateDotCount(current, curcount); + drawDots(g, point1, lead1, curcount); + drawPosts(g); + } + + double getCurrentIntoNode(int n) { + return -current; + } + + void setCurrent(int x, double c) { + current = c; + } + + double getVoltageDiff() { + return volts[0]; + } + void getInfo(String arr[]) { - arr[0] = Locale.LS(text) + " (" + Locale.LS("Labeled Node") + ")"; - arr[1] = "I = " + getCurrentText(getCurrent()); - arr[2] = "V = " + getVoltageText(volts[0]); + arr[0] = Locale.LS(text) + " (" + Locale.LS("Labeled Node") + ")"; + arr[1] = "I = " + getCurrentText(getCurrent()); + arr[2] = "V = " + getVoltageText(volts[0]); } public EditInfo getEditInfo(int n) { - if (n == 0) { - EditInfo ei = new EditInfo("Text", 0, -1, -1); - ei.text = text; - return ei; - } + if (n == 0) { + EditInfo ei = new EditInfo("Text", 0, -1, -1); + ei.text = text; + return ei; + } if (n == 1) { EditInfo ei = new EditInfo("", 0, -1, -1); ei.checkbox = new Checkbox("Internal Node", isInternal()); return ei; } - return null; + return null; } + public void setEditValue(int n, EditInfo ei) { - if (n == 0) - text = ei.textf.getText(); - if (n == 1) - flags = ei.changeFlag(flags, FLAG_INTERNAL); + if (n == 0) + text = ei.textf.getText(); + if (n == 1) + flags = ei.changeFlag(flags, FLAG_INTERNAL); } - @Override String getScopeText(int v) { - return text; + + @Override + String getScopeText(int v) { + return text; + } + + String getName() { + return text; } - - String getName() { return text; } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/LampElm.java b/src/main/java/com/lushprojects/circuitjs1/client/LampElm.java index c101a6b..02fec76 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/LampElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/LampElm.java @@ -21,187 +21,206 @@ import com.lushprojects.circuitjs1.client.util.Locale; -class LampElm extends CircuitElm { - double resistance; - final double roomTemp = 300; - double temp, nom_pow, nom_v, warmTime, coolTime; - public LampElm(int xx, int yy) { - super(xx, yy); - temp = roomTemp; - nom_pow = 100; - nom_v = 120; - warmTime = .4; - coolTime = .4; - } - public LampElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f); - temp = new Double(st.nextToken()).doubleValue(); - if (Double.isNaN(temp)) - temp = roomTemp; - nom_pow = new Double(st.nextToken()).doubleValue(); - nom_v = new Double(st.nextToken()).doubleValue(); - warmTime = new Double(st.nextToken()).doubleValue(); - coolTime = new Double(st.nextToken()).doubleValue(); - } - String dump() { - return super.dump() + " " + temp + " " + nom_pow + " " + nom_v + - " " + warmTime + " " + coolTime; - } - int getDumpType() { return 181; } - - Point bulbLead[], filament[], bulb; - int bulbR; - - void reset() { - super.reset(); - temp = roomTemp; - - // make sure resistance is not 0 or NaN or current will be NaN before we have a chance - // to call startIteration() - resistance = 100; - } - final int filament_len = 24; - void setPoints() { - super.setPoints(); - int llen = 16; - calcLeads(llen); - bulbLead = newPointArray(2); - filament = newPointArray(2); - bulbR = 20; - filament[0] = interpPoint(lead1, lead2, 0, filament_len); - filament[1] = interpPoint(lead1, lead2, 1, filament_len); - double br = filament_len-Math.sqrt(bulbR*bulbR-llen*llen); - bulbLead[0] = interpPoint(lead1, lead2, 0, br); - bulbLead[1] = interpPoint(lead1, lead2, 1, br); - bulb = interpPoint(filament[0], filament[1], .5); - } - - Color getTempColor() { - if (temp < 1200) { - int x = (int) (255*(temp-800)/400); - if (x < 0) - x = 0; - return new Color(x, 0, 0); - } - if (temp < 1700) { - int x = (int) (255*(temp-1200)/500); - if (x < 0) - x = 0; - return new Color(255, x, 0); - } - if (temp < 2400) { - int x = (int) (255*(temp-1700)/700); - if (x < 0) - x = 0; - return new Color(255, 255, x); - } - return Color.white; - } - - void draw(Graphics g) { - double v1 = volts[0]; - double v2 = volts[1]; - setBbox(point1, point2, 4); - adjustBbox(bulb.x-bulbR, bulb.y-bulbR, - bulb.x+bulbR, bulb.y+bulbR); - // adjustbbox - draw2Leads(g); - setPowerColor(g, true); - g.setColor(getTempColor()); - g.fillOval(bulb.x-bulbR, bulb.y-bulbR, bulbR*2, bulbR*2); - g.setColor(whiteColor); - drawThickCircle(g, bulb.x, bulb.y, bulbR); - setVoltageColor(g, v1); - drawThickLine(g, lead1, filament[0]); - setVoltageColor(g, v2); - drawThickLine(g, lead2, filament[1]); - setVoltageColor(g, (v1+v2)*.5); - drawThickLine(g, filament[0], filament[1]); - updateDotCount(); - if (sim.dragElm != this) { - drawDots(g, point1, lead1, curcount); - double cc = addCurCount(curcount, (dn-16)/2); - drawDots(g, lead1, filament[0], cc); - cc = addCurCount(cc, filament_len); - drawDots(g, filament[0], filament[1], cc); - cc = addCurCount(cc, 16); - drawDots(g, filament[1], lead2, cc); - cc = addCurCount(cc, filament_len); - drawDots(g, lead2, point2, curcount); - } - drawPosts(g); - } - - void calculateCurrent() { - current = (volts[0]-volts[1])/resistance; - if (resistance == 0) - current = 0; +public class LampElm extends CircuitElm { + double resistance; + final double roomTemp = 300; + double temp, nom_pow, nom_v, warmTime, coolTime; + + public LampElm(int xx, int yy) { + super(xx, yy); + temp = roomTemp; + nom_pow = 100; + nom_v = 120; + warmTime = .4; + coolTime = .4; + } + + public LampElm(int xa, int ya, int xb, int yb, int f, + StringTokenizer st) { + super(xa, ya, xb, yb, f); + temp = new Double(st.nextToken()).doubleValue(); + if (Double.isNaN(temp)) + temp = roomTemp; + nom_pow = new Double(st.nextToken()).doubleValue(); + nom_v = new Double(st.nextToken()).doubleValue(); + warmTime = new Double(st.nextToken()).doubleValue(); + coolTime = new Double(st.nextToken()).doubleValue(); + } + + String dump() { + return super.dump() + " " + temp + " " + nom_pow + " " + nom_v + + " " + warmTime + " " + coolTime; + } + + int getDumpType() { + return 181; + } + + Point bulbLead[], filament[], bulb; + int bulbR; + + void reset() { + super.reset(); + temp = roomTemp; + + // make sure resistance is not 0 or NaN or current will be NaN before we have a chance + // to call startIteration() + resistance = 100; + } + + final int filament_len = 24; + + void setPoints() { + super.setPoints(); + int llen = 16; + calcLeads(llen); + bulbLead = newPointArray(2); + filament = newPointArray(2); + bulbR = 20; + filament[0] = interpPoint(lead1, lead2, 0, filament_len); + filament[1] = interpPoint(lead1, lead2, 1, filament_len); + double br = filament_len - Math.sqrt(bulbR * bulbR - llen * llen); + bulbLead[0] = interpPoint(lead1, lead2, 0, br); + bulbLead[1] = interpPoint(lead1, lead2, 1, br); + bulb = interpPoint(filament[0], filament[1], .5); + } + + Color getTempColor() { + if (temp < 1200) { + int x = (int) (255 * (temp - 800) / 400); + if (x < 0) + x = 0; + return new Color(x, 0, 0); + } + if (temp < 1700) { + int x = (int) (255 * (temp - 1200) / 500); + if (x < 0) + x = 0; + return new Color(255, x, 0); + } + if (temp < 2400) { + int x = (int) (255 * (temp - 1700) / 700); + if (x < 0) + x = 0; + return new Color(255, 255, x); + } + return Color.white; + } + + void draw(Graphics g) { + double v1 = volts[0]; + double v2 = volts[1]; + setBbox(point1, point2, 4); + adjustBbox(bulb.x - bulbR, bulb.y - bulbR, + bulb.x + bulbR, bulb.y + bulbR); + // adjustbbox + draw2Leads(g); + setPowerColor(g, true); + g.setColor(getTempColor()); + g.fillOval(bulb.x - bulbR, bulb.y - bulbR, bulbR * 2, bulbR * 2); + g.setColor(whiteColor); + drawThickCircle(g, bulb.x, bulb.y, bulbR); + setVoltageColor(g, v1); + drawThickLine(g, lead1, filament[0]); + setVoltageColor(g, v2); + drawThickLine(g, lead2, filament[1]); + setVoltageColor(g, (v1 + v2) * .5); + drawThickLine(g, filament[0], filament[1]); + updateDotCount(); + if (sim.dragElm != this) { + drawDots(g, point1, lead1, curcount); + double cc = addCurCount(curcount, (dn - 16) / 2); + drawDots(g, lead1, filament[0], cc); + cc = addCurCount(cc, filament_len); + drawDots(g, filament[0], filament[1], cc); + cc = addCurCount(cc, 16); + drawDots(g, filament[1], lead2, cc); + cc = addCurCount(cc, filament_len); + drawDots(g, lead2, point2, curcount); + } + drawPosts(g); + } + + void calculateCurrent() { + current = (volts[0] - volts[1]) / resistance; + if (resistance == 0) + current = 0; // sim.console("lampcc " + current + " " + resistance); - } - void stamp() { - sim.stampNonLinear(nodes[0]); - sim.stampNonLinear(nodes[1]); - } - boolean nonLinear() { return true; } - void startIteration() { - // based on http://www.intusoft.com/nlpdf/nl11.pdf - double nom_r = nom_v*nom_v/nom_pow; - // this formula doesn't work for values over 5390 - double tp = (temp > 5390) ? 5390 : temp; - resistance = nom_r*(1.26104 - - 4.90662*Math.sqrt(17.1839/tp - 0.00318794) - - 7.8569/(tp - 187.56)); - double cap = 1.57e-4*nom_pow; - double capw = cap * warmTime/.4; - double capc = cap * coolTime/.4; - //System.out.println(nom_r + " " + (resistance/nom_r)); - temp += getPower()*sim.timeStep/capw; - double cr = 2600/nom_pow; - temp -= sim.timeStep*(temp-roomTemp)/(capc*cr); + } + + void stamp() { + sim.stampNonLinear(nodes[0]); + sim.stampNonLinear(nodes[1]); + } + + boolean nonLinear() { + return true; + } + + void startIteration() { + // based on http://www.intusoft.com/nlpdf/nl11.pdf + double nom_r = nom_v * nom_v / nom_pow; + // this formula doesn't work for values over 5390 + double tp = (temp > 5390) ? 5390 : temp; + resistance = nom_r * (1.26104 - + 4.90662 * Math.sqrt(17.1839 / tp - 0.00318794) - + 7.8569 / (tp - 187.56)); + double cap = 1.57e-4 * nom_pow; + double capw = cap * warmTime / .4; + double capc = cap * coolTime / .4; + //System.out.println(nom_r + " " + (resistance/nom_r)); + temp += getPower() * sim.timeStep / capw; + double cr = 2600 / nom_pow; + temp -= sim.timeStep * (temp - roomTemp) / (capc * cr); // sim.console("lampsi " + temp + " " + capc + " " + nom_pow); - } - void doStep() { - sim.stampResistor(nodes[0], nodes[1], resistance); - } - void getInfo(String arr[]) { - arr[0] = "lamp"; - getBasicInfo(arr); - arr[3] = "R = " + getUnitText(resistance, Locale.ohmString); - arr[4] = "P = " + getUnitText(getPower(), "W"); - arr[5] = "T = " + ((int) temp) + " K"; - } - public EditInfo getEditInfo(int n) { - // ohmString doesn't work here on linux - if (n == 0) - return new EditInfo("Nominal Power", nom_pow, 0, 0); - if (n == 1) - return new EditInfo("Nominal Voltage", nom_v, 0, 0); - if (n == 2) - return new EditInfo("Warmup Time (s)", warmTime, 0, 0); - if (n == 3) - return new EditInfo("Cooldown Time (s)", coolTime, 0, 0); - return null; - } - public void setEditValue(int n, EditInfo ei) { - if (n == 0 && ei.value > 0) - nom_pow = ei.value; - if (n == 1 && ei.value > 0) - nom_v = ei.value; - if (n == 2 && ei.value > 0) - warmTime = ei.value; - if (n == 3 && ei.value > 0) - coolTime = ei.value; - } - - double getScopeValue(int x) { - return (x == Scope.VAL_R) ? resistance : super.getScopeValue(x); - } - int getScopeUnits(int x) { - return (x == Scope.VAL_R) ? Scope.UNITS_OHMS : super.getScopeUnits(x); - } - boolean canShowValueInScope(int x) { - return x == Scope.VAL_R; - } + } + + void doStep() { + sim.stampResistor(nodes[0], nodes[1], resistance); + } + + void getInfo(String arr[]) { + arr[0] = "lamp"; + getBasicInfo(arr); + arr[3] = "R = " + getUnitText(resistance, Locale.ohmString); + arr[4] = "P = " + getUnitText(getPower(), "W"); + arr[5] = "T = " + ((int) temp) + " K"; + } + + public EditInfo getEditInfo(int n) { + // ohmString doesn't work here on linux + if (n == 0) + return new EditInfo("Nominal Power", nom_pow, 0, 0); + if (n == 1) + return new EditInfo("Nominal Voltage", nom_v, 0, 0); + if (n == 2) + return new EditInfo("Warmup Time (s)", warmTime, 0, 0); + if (n == 3) + return new EditInfo("Cooldown Time (s)", coolTime, 0, 0); + return null; + } + + public void setEditValue(int n, EditInfo ei) { + if (n == 0 && ei.value > 0) + nom_pow = ei.value; + if (n == 1 && ei.value > 0) + nom_v = ei.value; + if (n == 2 && ei.value > 0) + warmTime = ei.value; + if (n == 3 && ei.value > 0) + coolTime = ei.value; + } + double getScopeValue(int x) { + return (x == Scope.VAL_R) ? resistance : super.getScopeValue(x); } + + int getScopeUnits(int x) { + return (x == Scope.VAL_R) ? Scope.UNITS_OHMS : super.getScopeUnits(x); + } + + boolean canShowValueInScope(int x) { + return x == Scope.VAL_R; + } + +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/LatchElm.java b/src/main/java/com/lushprojects/circuitjs1/client/LatchElm.java index 4640bb9..69cc7f9 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/LatchElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/LatchElm.java @@ -19,64 +19,87 @@ package com.lushprojects.circuitjs1.client; -class LatchElm extends ChipElm { +public class LatchElm extends ChipElm { final int FLAG_STATE = 2; + public LatchElm(int xx, int yy) { - super(xx, yy); - flags |= FLAG_STATE; - setupPins(); + super(xx, yy); + flags |= FLAG_STATE; + setupPins(); } + public LatchElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f, st); - - // add FLAG_STATE flag to old latches so their state gets saved - if ((flags & FLAG_STATE) == 0) { - flags |= FLAG_STATE; - setupPins(); - } + StringTokenizer st) { + super(xa, ya, xb, yb, f, st); + + // add FLAG_STATE flag to old latches so their state gets saved + if ((flags & FLAG_STATE) == 0) { + flags |= FLAG_STATE; + setupPins(); + } } - String getChipName() { return "Latch"; } - boolean needsBits() { return true; } + + String getChipName() { + return "Latch"; + } + + boolean needsBits() { + return true; + } + int loadPin; + void setupPins() { - sizeX = 2; - sizeY = bits+1; - pins = new Pin[getPostCount()]; - int i; - for (i = 0; i != bits; i++) - pins[i] = new Pin(bits-1-i, SIDE_W, "I" + i); - for (i = 0; i != bits; i++) { - pins[i+bits] = new Pin(bits-1-i, SIDE_E, "O"); - pins[i+bits].output = true; - pins[i+bits].state = (flags & FLAG_STATE) != 0; - } - pins[loadPin = bits*2] = new Pin(bits, SIDE_W, "Ld"); - allocNodes(); + sizeX = 2; + sizeY = bits + 1; + pins = new Pin[getPostCount()]; + int i; + for (i = 0; i != bits; i++) + pins[i] = new Pin(bits - 1 - i, SIDE_W, "I" + i); + for (i = 0; i != bits; i++) { + pins[i + bits] = new Pin(bits - 1 - i, SIDE_E, "O"); + pins[i + bits].output = true; + pins[i + bits].state = (flags & FLAG_STATE) != 0; + } + pins[loadPin = bits * 2] = new Pin(bits, SIDE_W, "Ld"); + allocNodes(); } + boolean lastLoad = false; + void execute() { - int i; - if (pins[loadPin].value && !lastLoad) - for (i = 0; i != bits; i++) - pins[i+bits].value = pins[i].value; - lastLoad = pins[loadPin].value; + int i; + if (pins[loadPin].value && !lastLoad) + for (i = 0; i != bits; i++) + pins[i + bits].value = pins[i].value; + lastLoad = pins[loadPin].value; } - int getVoltageSourceCount() { return bits; } - int getPostCount() { return bits*2+1; } - int getDumpType() { return 168; } + + int getVoltageSourceCount() { + return bits; + } + + int getPostCount() { + return bits * 2 + 1; + } + + int getDumpType() { + return 168; + } + public EditInfo getChipEditInfo(int n) { - if (n == 0) - return new EditInfo("# of Bits", bits, 1, 1).setDimensionless(); - return null; + if (n == 0) + return new EditInfo("# of Bits", bits, 1, 1).setDimensionless(); + return null; } + public void setChipEditValue(int n, EditInfo ei) { - if (n == 0 && ei.value >= 2) { - bits = (int)ei.value; - setupPins(); - setPoints(); - } + if (n == 0 && ei.value >= 2) { + bits = (int) ei.value; + setupPins(); + setPoints(); + } } - + } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/LicenseDialog.java b/src/main/java/com/lushprojects/circuitjs1/client/LicenseDialog.java index 1b6bcb2..5f491d6 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/LicenseDialog.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/LicenseDialog.java @@ -13,29 +13,29 @@ import com.lushprojects.circuitjs1.client.util.Locale; public class LicenseDialog extends DialogBox { - - VerticalPanel vp; - Button okButton; - CirSim sim; - - LicenseDialog() { - super(); - vp = new VerticalPanel(); - setWidget(vp); - setText(Locale.LS("License")); - vp.setWidth("500px"); - vp.add(new HTML("")); - vp.add(okButton = new Button("OK")); - okButton.addClickHandler(new ClickHandler() { - public void onClick(ClickEvent event) { - closeDialog(); - } - }); - center(); - show(); - } - protected void closeDialog() { - hide(); - } + VerticalPanel vp; + Button okButton; + CirSim sim; + + LicenseDialog() { + super(); + vp = new VerticalPanel(); + setWidget(vp); + setText(Locale.LS("License")); + vp.setWidth("500px"); + vp.add(new HTML("")); + vp.add(okButton = new Button("OK")); + okButton.addClickHandler(new ClickHandler() { + public void onClick(ClickEvent event) { + closeDialog(); + } + }); + center(); + show(); + } + + protected void closeDialog() { + hide(); + } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/LineElm.java b/src/main/java/com/lushprojects/circuitjs1/client/LineElm.java index 9fec0f9..b06a339 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/LineElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/LineElm.java @@ -19,47 +19,49 @@ package com.lushprojects.circuitjs1.client; -class LineElm extends GraphicElm { +public class LineElm extends GraphicElm { public LineElm(int xx, int yy) { - super(xx, yy); - x2 = xx; - y2 = yy; - setBbox(x, y, x2, y2); + super(xx, yy); + x2 = xx; + y2 = yy; + setBbox(x, y, x2, y2); } public LineElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f); - x2 = xb; - y2 = yb; - setBbox(x, y, x2, y2); + StringTokenizer st) { + super(xa, ya, xb, yb, f); + x2 = xb; + y2 = yb; + setBbox(x, y, x2, y2); } String dump() { - return super.dump(); + return super.dump(); } - int getDumpType() { return 423; } + int getDumpType() { + return 423; + } void drag(int xx, int yy) { - x2 = xx; - y2 = yy; + x2 = xx; + y2 = yy; } boolean creationFailed() { - return Math.hypot(x-x2, y-y2) < 16; + return Math.hypot(x - x2, y - y2) < 16; } - + void draw(Graphics g) { - //g.setColor(needsHighlight() ? selectColor : lightGrayColor); - g.setColor(needsHighlight() ? selectColor : Color.GRAY); - setBbox(x, y, x2, y2); - g.drawLine(x, y, x2, y2); + //g.setColor(needsHighlight() ? selectColor : lightGrayColor); + g.setColor(needsHighlight() ? selectColor : Color.GRAY); + setBbox(x, y, x2, y2); + g.drawLine(x, y, x2, y2); } public EditInfo getEditInfo(int n) { - return null; + return null; } public void setEditValue(int n, EditInfo ei) { @@ -69,6 +71,8 @@ void getInfo(String arr[]) { } @Override - int getShortcut() { return 0; } + int getShortcut() { + return 0; + } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/LoadFile.java b/src/main/java/com/lushprojects/circuitjs1/client/LoadFile.java index 729643c..768ddd7 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/LoadFile.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/LoadFile.java @@ -23,46 +23,45 @@ import com.google.gwt.event.dom.client.ChangeEvent; import com.google.gwt.event.dom.client.ChangeHandler; -public class LoadFile extends FileUpload implements ChangeHandler { - - static CirSim sim; - - static public final native boolean isSupported() +public class LoadFile extends FileUpload implements ChangeHandler { + + static CirSim sim; + + static public final native boolean isSupported() /*-{ return !!($wnd.File && $wnd.FileReader); }-*/; - - static public void doLoadCallback(String s, String t) { - sim.pushUndo(); - sim.readCircuit(s); - sim.createNewLoadFile(); - sim.setCircuitTitle(t); - ExportAsLocalFileDialog.setLastFileName(t); - sim.unsavedChanges = false; - } - - LoadFile(CirSim s) { - super(); - sim=s; - this.setName("Import"); - this.getElement().setId("LoadFileElement"); - this.addChangeHandler(this); - this.addStyleName("offScreen"); - } - - - - public void onChange(ChangeEvent e) { - doLoad(); - } - - - public final native void click() + + static public void doLoadCallback(String s, String t) { + sim.pushUndo(); + sim.readCircuit(s); + sim.createNewLoadFile(); + sim.setCircuitTitle(t); + ExportAsLocalFileDialog.setLastFileName(t); + sim.unsavedChanges = false; + } + + LoadFile(CirSim s) { + super(); + sim = s; + this.setName("Import"); + this.getElement().setId("LoadFileElement"); + this.addChangeHandler(this); + this.addStyleName("offScreen"); + } + + + public void onChange(ChangeEvent e) { + doLoad(); + } + + + public final native void click() /*-{ $doc.getElementById("LoadFileElement").click(); }-*/; - - static public final native void doLoad() + + static public final native void doLoad() /*-{ var oFiles = $doc.getElementById("LoadFileElement").files, nFiles = oFiles.length; @@ -80,5 +79,5 @@ static public final native void doLoad() } } }-*/; - + } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/LogicInputElm.java b/src/main/java/com/lushprojects/circuitjs1/client/LogicInputElm.java index d4fb0cc..97954d5 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/LogicInputElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/LogicInputElm.java @@ -19,134 +19,176 @@ package com.lushprojects.circuitjs1.client; - class LogicInputElm extends SwitchElm { - final int FLAG_TERNARY = 1; - final int FLAG_NUMERIC = 2; - double hiV, loV; - public LogicInputElm(int xx, int yy) { - super(xx, yy, false); - hiV = 5; - loV = 0; - - } - public LogicInputElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f, st); - try { - hiV = new Double(st.nextToken()).doubleValue(); - loV = new Double(st.nextToken()).doubleValue(); - } catch (Exception e) { - hiV = 5; - loV = 0; - } - if (isTernary()) - posCount = 3; - } - boolean isTernary() { return (flags & FLAG_TERNARY) != 0; } - boolean isNumeric() { return (flags & (FLAG_TERNARY|FLAG_NUMERIC)) != 0; } - int getDumpType() { return 'L'; } - String dump() { - return super.dump() + " " + hiV + " " + loV; - } - int getPostCount() { return 1; } - void setPoints() { - super.setPoints(); - lead1 = interpPoint(point1, point2, 1-12/dn); - } - void draw(Graphics g) { - g.save(); - Font f = new Font("SansSerif", Font.BOLD, 20); - g.setFont(f); - g.setColor(needsHighlight() ? selectColor : whiteColor); - String s = position == 0 ? "L" : "H"; - if (isNumeric()) - s = "" + position; - setBbox(point1, lead1, 0); - drawCenteredText(g, s, x2, y2, true); - setVoltageColor(g, volts[0]); - drawThickLine(g, point1, lead1); - updateDotCount(); - drawDots(g, point1, lead1, -curcount); - drawPosts(g); - g.restore(); - } - - Rectangle getSwitchRect() { - return new Rectangle(x2-10, y2-10, 20, 20); - } - - void setCurrent(int vs, double c) { current = c; } - void calculateCurrent() {} - void stamp() { - sim.stampVoltageSource(0, nodes[0], voltSource); - } - - boolean isWireEquivalent() { return false; } - boolean isRemovableWire() { return false; } - - void doStep() { - double v = (position == 0) ? loV : hiV; - if (isTernary()) - v = loV + position * (hiV-loV) * .5; - sim.updateVoltageSource(0, nodes[0], voltSource, v); - } - int getVoltageSourceCount() { return 1; } - double getVoltageDiff() { return volts[0]; } - void getInfo(String arr[]) { - arr[0] = "logic input"; - arr[1] = (position == 0) ? "low" : "high"; - if (isNumeric()) - arr[1] = "" + position; - arr[1] += " (" + getVoltageText(volts[0]) + ")"; - arr[2] = "I = " + getCurrentText(getCurrent()); - } - boolean hasGroundConnection(int n1) { return true; } - public EditInfo getEditInfo(int n) { - if (n == 0) { - EditInfo ei = new EditInfo("", 0, 0, 0); - ei.checkbox = new Checkbox("Momentary Switch", momentary); - return ei; - } - if (n == 1) - return new EditInfo("High Logic Voltage", hiV, 10, -10); - if (n == 2) - return new EditInfo("Low Voltage", loV, 10, -10); - if (n == 3) { - EditInfo ei = new EditInfo("", 0, 0, 0); - ei.checkbox = new Checkbox("Numeric", isNumeric()); - return ei; - } - if (n == 4) { - EditInfo ei = new EditInfo("", 0, 0, 0); - ei.checkbox = new Checkbox("Ternary", isTernary()); - return ei; - } - return null; - } - public void setEditValue(int n, EditInfo ei) { - if (n == 0) - momentary = ei.checkbox.getState(); - if (n == 1) - hiV = ei.value; - if (n == 2) - loV = ei.value; - if (n == 3) { - if (ei.checkbox.getState()) - flags |= FLAG_NUMERIC; - else - flags &= ~FLAG_NUMERIC; - } - if (n == 4) { - if (ei.checkbox.getState()) - flags |= FLAG_TERNARY; - else - flags &= ~FLAG_TERNARY; - posCount = (isTernary()) ? 3 : 2; - } - } - int getShortcut() { return 'i'; } - - double getCurrentIntoNode(int n) { - return current; - } +public class LogicInputElm extends SwitchElm { + final int FLAG_TERNARY = 1; + final int FLAG_NUMERIC = 2; + double hiV, loV; + + public LogicInputElm(int xx, int yy) { + super(xx, yy, false); + hiV = 5; + loV = 0; + + } + + public LogicInputElm(int xa, int ya, int xb, int yb, int f, + StringTokenizer st) { + super(xa, ya, xb, yb, f, st); + try { + hiV = new Double(st.nextToken()).doubleValue(); + loV = new Double(st.nextToken()).doubleValue(); + } catch (Exception e) { + hiV = 5; + loV = 0; + } + if (isTernary()) + posCount = 3; + } + + boolean isTernary() { + return (flags & FLAG_TERNARY) != 0; + } + + boolean isNumeric() { + return (flags & (FLAG_TERNARY | FLAG_NUMERIC)) != 0; + } + + int getDumpType() { + return 'L'; + } + + String dump() { + return super.dump() + " " + hiV + " " + loV; + } + + int getPostCount() { + return 1; + } + + void setPoints() { + super.setPoints(); + lead1 = interpPoint(point1, point2, 1 - 12 / dn); + } + + void draw(Graphics g) { + g.save(); + Font f = new Font("SansSerif", Font.BOLD, 20); + g.setFont(f); + g.setColor(needsHighlight() ? selectColor : whiteColor); + String s = position == 0 ? "L" : "H"; + if (isNumeric()) + s = "" + position; + setBbox(point1, lead1, 0); + drawCenteredText(g, s, x2, y2, true); + setVoltageColor(g, volts[0]); + drawThickLine(g, point1, lead1); + updateDotCount(); + drawDots(g, point1, lead1, -curcount); + drawPosts(g); + g.restore(); + } + + Rectangle getSwitchRect() { + return new Rectangle(x2 - 10, y2 - 10, 20, 20); + } + + void setCurrent(int vs, double c) { + current = c; + } + + void calculateCurrent() { + } + + void stamp() { + sim.stampVoltageSource(0, nodes[0], voltSource); + } + + boolean isWireEquivalent() { + return false; + } + + boolean isRemovableWire() { + return false; + } + + void doStep() { + double v = (position == 0) ? loV : hiV; + if (isTernary()) + v = loV + position * (hiV - loV) * .5; + sim.updateVoltageSource(0, nodes[0], voltSource, v); + } + + int getVoltageSourceCount() { + return 1; + } + + double getVoltageDiff() { + return volts[0]; + } + + void getInfo(String arr[]) { + arr[0] = "logic input"; + arr[1] = (position == 0) ? "low" : "high"; + if (isNumeric()) + arr[1] = "" + position; + arr[1] += " (" + getVoltageText(volts[0]) + ")"; + arr[2] = "I = " + getCurrentText(getCurrent()); + } + + boolean hasGroundConnection(int n1) { + return true; + } + + public EditInfo getEditInfo(int n) { + if (n == 0) { + EditInfo ei = new EditInfo("", 0, 0, 0); + ei.checkbox = new Checkbox("Momentary Switch", momentary); + return ei; + } + if (n == 1) + return new EditInfo("High Logic Voltage", hiV, 10, -10); + if (n == 2) + return new EditInfo("Low Voltage", loV, 10, -10); + if (n == 3) { + EditInfo ei = new EditInfo("", 0, 0, 0); + ei.checkbox = new Checkbox("Numeric", isNumeric()); + return ei; + } + if (n == 4) { + EditInfo ei = new EditInfo("", 0, 0, 0); + ei.checkbox = new Checkbox("Ternary", isTernary()); + return ei; + } + return null; + } + + public void setEditValue(int n, EditInfo ei) { + if (n == 0) + momentary = ei.checkbox.getState(); + if (n == 1) + hiV = ei.value; + if (n == 2) + loV = ei.value; + if (n == 3) { + if (ei.checkbox.getState()) + flags |= FLAG_NUMERIC; + else + flags &= ~FLAG_NUMERIC; + } + if (n == 4) { + if (ei.checkbox.getState()) + flags |= FLAG_TERNARY; + else + flags &= ~FLAG_TERNARY; + posCount = (isTernary()) ? 3 : 2; + } + } + + int getShortcut() { + return 'i'; + } + + double getCurrentIntoNode(int n) { + return current; } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/LogicOutputElm.java b/src/main/java/com/lushprojects/circuitjs1/client/LogicOutputElm.java index 573080e..ea6746a 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/LogicOutputElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/LogicOutputElm.java @@ -19,121 +19,151 @@ package com.lushprojects.circuitjs1.client; - class LogicOutputElm extends CircuitElm { - final int FLAG_TERNARY = 1; - final int FLAG_NUMERIC = 2; - final int FLAG_PULLDOWN = 4; - double threshold; - String value; - public LogicOutputElm(int xx, int yy) { - super(xx, yy); - threshold = 2.5; - } - public LogicOutputElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f); - try { - threshold = new Double(st.nextToken()).doubleValue(); - } catch (Exception e) { - threshold = 2.5; - } - } - String dump() { - return super.dump() + " " + threshold; - } - int getDumpType() { return 'M'; } - int getPostCount() { return 1; } - boolean isTernary() { return (flags & FLAG_TERNARY) != 0; } - boolean isNumeric() { return (flags & (FLAG_TERNARY|FLAG_NUMERIC)) != 0; } - boolean needsPullDown() { return (flags & FLAG_PULLDOWN) != 0; } - void setPoints() { - super.setPoints(); - lead1 = interpPoint(point1, point2, 1-12/dn); - } - void draw(Graphics g) { - g.save(); - Font f = new Font("SansSerif", Font.BOLD, 20); - g.setFont(f); - //g.setColor(needsHighlight() ? selectColor : lightGrayColor); - g.setColor(lightGrayColor); - String s = (volts[0] < threshold) ? "L" : "H"; - if (isTernary()) { - // we don't have 2 separate thresholds for ternary inputs so we do this instead - if (volts[0] > threshold * 1.5) // 3.75 V - s = "2"; - else if (volts[0] > threshold * .5) // 1.25 V - s = "1"; - else - s = "0"; - } else if (isNumeric()) - s = (volts[0] < threshold) ? "0" : "1"; - value = s; - setBbox(point1, lead1, 0); - drawCenteredText(g, s, x2, y2, true); - setVoltageColor(g, volts[0]); - drawThickLine(g, point1, lead1); - drawPosts(g); - g.restore(); - } - void stamp() { - if (needsPullDown()) - sim.stampResistor(nodes[0], 0, 1e6); - } - double getVoltageDiff() { return volts[0]; } - void getInfo(String arr[]) { - arr[0] = "logic output"; - arr[1] = (volts[0] < threshold) ? "low" : "high"; - if (isNumeric()) - arr[1] = value; - arr[2] = "V = " + getVoltageText(volts[0]); - } - public EditInfo getEditInfo(int n) { - if (n == 0) - return new EditInfo("Threshold", threshold, 10, -10); - if (n == 1) { - EditInfo ei = new EditInfo("", 0, -1, -1); - ei.checkbox = new Checkbox("Current Required", needsPullDown()); - return ei; - } - if (n == 2) { - EditInfo ei = new EditInfo("", 0, 0, 0); - ei.checkbox = new Checkbox("Numeric", isNumeric()); - return ei; - } - if (n == 3) { - EditInfo ei = new EditInfo("", 0, 0, 0); - ei.checkbox = new Checkbox("Ternary", isTernary()); - return ei; - } - return null; - } - public void setEditValue(int n, EditInfo ei) { - if (n == 0) - threshold = ei.value; - if (n == 1) { - if (ei.checkbox.getState()) - flags |= FLAG_PULLDOWN; - else - flags &= ~FLAG_PULLDOWN; - } - if (n == 2) { - if (ei.checkbox.getState()) - flags |= FLAG_NUMERIC; - else - flags &= ~FLAG_NUMERIC; - } - if (n == 3) { - if (ei.checkbox.getState()) - flags |= FLAG_TERNARY; - else - flags &= ~FLAG_TERNARY; - } - } - int getShortcut() { return 'o'; } - +public class LogicOutputElm extends CircuitElm { + final int FLAG_TERNARY = 1; + final int FLAG_NUMERIC = 2; + final int FLAG_PULLDOWN = 4; + double threshold; + String value; + + public LogicOutputElm(int xx, int yy) { + super(xx, yy); + threshold = 2.5; + } + + public LogicOutputElm(int xa, int ya, int xb, int yb, int f, + StringTokenizer st) { + super(xa, ya, xb, yb, f); + try { + threshold = new Double(st.nextToken()).doubleValue(); + } catch (Exception e) { + threshold = 2.5; + } + } + + String dump() { + return super.dump() + " " + threshold; + } + + int getDumpType() { + return 'M'; + } + + int getPostCount() { + return 1; + } + + boolean isTernary() { + return (flags & FLAG_TERNARY) != 0; + } + + boolean isNumeric() { + return (flags & (FLAG_TERNARY | FLAG_NUMERIC)) != 0; + } + + boolean needsPullDown() { + return (flags & FLAG_PULLDOWN) != 0; + } + + void setPoints() { + super.setPoints(); + lead1 = interpPoint(point1, point2, 1 - 12 / dn); + } + + void draw(Graphics g) { + g.save(); + Font f = new Font("SansSerif", Font.BOLD, 20); + g.setFont(f); + //g.setColor(needsHighlight() ? selectColor : lightGrayColor); + g.setColor(lightGrayColor); + String s = (volts[0] < threshold) ? "L" : "H"; + if (isTernary()) { + // we don't have 2 separate thresholds for ternary inputs so we do this instead + if (volts[0] > threshold * 1.5) // 3.75 V + s = "2"; + else if (volts[0] > threshold * .5) // 1.25 V + s = "1"; + else + s = "0"; + } else if (isNumeric()) + s = (volts[0] < threshold) ? "0" : "1"; + value = s; + setBbox(point1, lead1, 0); + drawCenteredText(g, s, x2, y2, true); + setVoltageColor(g, volts[0]); + drawThickLine(g, point1, lead1); + drawPosts(g); + g.restore(); + } + + void stamp() { + if (needsPullDown()) + sim.stampResistor(nodes[0], 0, 1e6); + } + + double getVoltageDiff() { + return volts[0]; + } + + void getInfo(String arr[]) { + arr[0] = "logic output"; + arr[1] = (volts[0] < threshold) ? "low" : "high"; + if (isNumeric()) + arr[1] = value; + arr[2] = "V = " + getVoltageText(volts[0]); + } + + public EditInfo getEditInfo(int n) { + if (n == 0) + return new EditInfo("Threshold", threshold, 10, -10); + if (n == 1) { + EditInfo ei = new EditInfo("", 0, -1, -1); + ei.checkbox = new Checkbox("Current Required", needsPullDown()); + return ei; + } + if (n == 2) { + EditInfo ei = new EditInfo("", 0, 0, 0); + ei.checkbox = new Checkbox("Numeric", isNumeric()); + return ei; + } + if (n == 3) { + EditInfo ei = new EditInfo("", 0, 0, 0); + ei.checkbox = new Checkbox("Ternary", isTernary()); + return ei; + } + return null; + } + + public void setEditValue(int n, EditInfo ei) { + if (n == 0) + threshold = ei.value; + if (n == 1) { + if (ei.checkbox.getState()) + flags |= FLAG_PULLDOWN; + else + flags &= ~FLAG_PULLDOWN; + } + if (n == 2) { + if (ei.checkbox.getState()) + flags |= FLAG_NUMERIC; + else + flags &= ~FLAG_NUMERIC; + } + if (n == 3) { + if (ei.checkbox.getState()) + flags |= FLAG_TERNARY; + else + flags &= ~FLAG_TERNARY; + } + } + + int getShortcut() { + return 'o'; + } + // void drawHandles(Graphics g, Color c) { // g.setColor(c); // g.fillRect(x-3, y-3, 7, 7); // } - - } + +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/MBBSwitchElm.java b/src/main/java/com/lushprojects/circuitjs1/client/MBBSwitchElm.java index e529a15..5c3b898 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/MBBSwitchElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/MBBSwitchElm.java @@ -19,175 +19,196 @@ package com.lushprojects.circuitjs1.client; - class MBBSwitchElm extends SwitchElm { - int link; - int voltSources[]; - double currents[]; - double curcounts[]; - boolean both; - - public MBBSwitchElm(int xx, int yy) { - super(xx, yy, false); - setup(); - } - - void setup() { - noDiagonal = true; - voltSources = new int[2]; - currents = new double[2]; - curcounts = new double[3]; - } - - public MBBSwitchElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f, st); - link = new Integer(st.nextToken()).intValue(); - setup(); - } - - int getDumpType() { return 416; } - String dump() { - return super.dump() + " " + link; - } - - final int openhs = 16; - Point swposts[], swpoles[]; - void setPoints() { - super.setPoints(); - calcLeads(32); - swposts = newPointArray(2); - swpoles = newPointArray(2+2); - int i; - for (i = 0; i != 2; i++) { - int hs = -openhs*(i-(2-1)/2); - if (i == 0) - hs = openhs; - interpPoint(lead1, lead2, swpoles[i], 1, hs); - interpPoint(point1, point2, swposts[i], 1, hs); - } - - // 4 positions (pole 1, both, pole 2, both) - posCount = 4; - } - - void draw(Graphics g) { - - setBbox(point1, point2, openhs); - adjustBbox(swposts[0], swposts[1]); - - // draw first lead - setVoltageColor(g, volts[0]); - drawThickLine(g, point1, lead1); - - // draw other leads - int i; - for (i = 0; i != 2; i++) { - setVoltageColor(g, volts[i+1]); - drawThickLine(g, swpoles[i], swposts[i]); - } - - // draw switch - if (!needsHighlight()) - g.setColor(whiteColor); - if (both || position == 0) - drawThickLine(g, lead1, swpoles[0]); - if (both || position == 2) - drawThickLine(g, lead1, swpoles[1]); - - // draw current - for (i = 0; i != 2; i++) { - curcounts[i] = updateDotCount(currents[i], curcounts[i]); - drawDots(g, swpoles[i], swposts[i], curcounts[i]); - } - curcounts[2] = updateDotCount(currents[0]+currents[1], curcounts[2]); - drawDots(g, point1, lead1, curcounts[2]); - drawPosts(g); - } - - double getCurrentIntoNode(int n) { - if (n == 0) - return -currents[0]-currents[1]; - return currents[n-1]; - } - - Rectangle getSwitchRect() { - return new Rectangle(lead1).union(new Rectangle(swpoles[0])).union(new Rectangle(swpoles[1])); - } - - Point getPost(int n) { - return (n == 0) ? point1 : swposts[n-1]; - } - - int getPostCount() { return 3; } - - void setCurrent(int vn, double c) { - // set current for voltage source vn to c - if (vn == voltSources[0]) - currents[both ? 0 : position/2] = c; - else if (vn == voltSources[1]) - currents[1] = c; - } - void calculateCurrent() { - // make sure current of unconnected pole is zero - if (!both) - currents[1-(position/2)] = 0; - } - void setVoltageSource(int n, int v) { - voltSources[n] = v; - } - void stamp() { - int vs = 0; - if (both || position == 0) - sim.stampVoltageSource(nodes[0], nodes[1], voltSources[vs++], 0); - if (both || position == 2) - sim.stampVoltageSource(nodes[0], nodes[2], voltSources[vs++], 0); - } - - // connection is implemented by voltage source with voltage = 0. - // need two for both loads connected, otherwise one. - int getVoltageSourceCount() { - both = (position == 1 || position == 3); - return (both) ? 2 : 1; - } - void toggle() { - super.toggle(); - if (link != 0) { - int i; - for (i = 0; i != sim.elmList.size(); i++) { - Object o = sim.elmList.elementAt(i); - if (o instanceof MBBSwitchElm) { - MBBSwitchElm s2 = (MBBSwitchElm) o; - if (s2.link == link) - s2.position = position; - } - } - } - } - boolean getConnection(int n1, int n2) { - if (both) - return true; - return comparePair(n1, n2, 0, 1+position/2); - } - - // do not optimize out, even though isWireEquivalent() is true (because it may have 3 nodes to merge - // and calcWireClosure() doesn't handle that case) - boolean isRemovableWire() { return false; } - boolean isWireEquivalent() { return true; } - - void getInfo(String arr[]) { - arr[0] = "switch (" + (link == 0 ? "S" : "D") + "PDT, MBB)"; - arr[1] = "I = " + getCurrentDText(getCurrent()); - } - public EditInfo getEditInfo(int n) { - if (n == 1) - return new EditInfo("Switch Group", link, 0, 100).setDimensionless(); - return super.getEditInfo(n); - } - public void setEditValue(int n, EditInfo ei) { - if (n == 1) { - link = (int) ei.value; - } else - super.setEditValue(n, ei); - } - int getShortcut() { return 0; } +public class MBBSwitchElm extends SwitchElm { + int link; + int voltSources[]; + double currents[]; + double curcounts[]; + boolean both; + + public MBBSwitchElm(int xx, int yy) { + super(xx, yy, false); + setup(); + } + + void setup() { + noDiagonal = true; + voltSources = new int[2]; + currents = new double[2]; + curcounts = new double[3]; + } + + public MBBSwitchElm(int xa, int ya, int xb, int yb, int f, + StringTokenizer st) { + super(xa, ya, xb, yb, f, st); + link = new Integer(st.nextToken()).intValue(); + setup(); + } + + int getDumpType() { + return 416; + } + + String dump() { + return super.dump() + " " + link; + } + + final int openhs = 16; + Point swposts[], swpoles[]; + + void setPoints() { + super.setPoints(); + calcLeads(32); + swposts = newPointArray(2); + swpoles = newPointArray(2 + 2); + int i; + for (i = 0; i != 2; i++) { + int hs = -openhs * (i - (2 - 1) / 2); + if (i == 0) + hs = openhs; + interpPoint(lead1, lead2, swpoles[i], 1, hs); + interpPoint(point1, point2, swposts[i], 1, hs); + } + + // 4 positions (pole 1, both, pole 2, both) + posCount = 4; + } + + void draw(Graphics g) { + + setBbox(point1, point2, openhs); + adjustBbox(swposts[0], swposts[1]); + + // draw first lead + setVoltageColor(g, volts[0]); + drawThickLine(g, point1, lead1); + + // draw other leads + int i; + for (i = 0; i != 2; i++) { + setVoltageColor(g, volts[i + 1]); + drawThickLine(g, swpoles[i], swposts[i]); + } + + // draw switch + if (!needsHighlight()) + g.setColor(whiteColor); + if (both || position == 0) + drawThickLine(g, lead1, swpoles[0]); + if (both || position == 2) + drawThickLine(g, lead1, swpoles[1]); + + // draw current + for (i = 0; i != 2; i++) { + curcounts[i] = updateDotCount(currents[i], curcounts[i]); + drawDots(g, swpoles[i], swposts[i], curcounts[i]); + } + curcounts[2] = updateDotCount(currents[0] + currents[1], curcounts[2]); + drawDots(g, point1, lead1, curcounts[2]); + drawPosts(g); + } + + double getCurrentIntoNode(int n) { + if (n == 0) + return -currents[0] - currents[1]; + return currents[n - 1]; + } + + Rectangle getSwitchRect() { + return new Rectangle(lead1).union(new Rectangle(swpoles[0])).union(new Rectangle(swpoles[1])); + } + + Point getPost(int n) { + return (n == 0) ? point1 : swposts[n - 1]; + } + + int getPostCount() { + return 3; + } + + void setCurrent(int vn, double c) { + // set current for voltage source vn to c + if (vn == voltSources[0]) + currents[both ? 0 : position / 2] = c; + else if (vn == voltSources[1]) + currents[1] = c; + } + + void calculateCurrent() { + // make sure current of unconnected pole is zero + if (!both) + currents[1 - (position / 2)] = 0; + } + + void setVoltageSource(int n, int v) { + voltSources[n] = v; + } + + void stamp() { + int vs = 0; + if (both || position == 0) + sim.stampVoltageSource(nodes[0], nodes[1], voltSources[vs++], 0); + if (both || position == 2) + sim.stampVoltageSource(nodes[0], nodes[2], voltSources[vs++], 0); + } + + // connection is implemented by voltage source with voltage = 0. + // need two for both loads connected, otherwise one. + int getVoltageSourceCount() { + both = (position == 1 || position == 3); + return (both) ? 2 : 1; + } + + void toggle() { + super.toggle(); + if (link != 0) { + int i; + for (i = 0; i != sim.elmList.size(); i++) { + Object o = sim.elmList.elementAt(i); + if (o instanceof MBBSwitchElm) { + MBBSwitchElm s2 = (MBBSwitchElm) o; + if (s2.link == link) + s2.position = position; + } + } + } + } + + boolean getConnection(int n1, int n2) { + if (both) + return true; + return comparePair(n1, n2, 0, 1 + position / 2); + } + + // do not optimize out, even though isWireEquivalent() is true (because it may have 3 nodes to merge + // and calcWireClosure() doesn't handle that case) + boolean isRemovableWire() { + return false; + } + + boolean isWireEquivalent() { + return true; + } + + void getInfo(String arr[]) { + arr[0] = "switch (" + (link == 0 ? "S" : "D") + "PDT, MBB)"; + arr[1] = "I = " + getCurrentDText(getCurrent()); + } + + public EditInfo getEditInfo(int n) { + if (n == 1) + return new EditInfo("Switch Group", link, 0, 100).setDimensionless(); + return super.getEditInfo(n); + } + + public void setEditValue(int n, EditInfo ei) { + if (n == 1) { + link = (int) ei.value; + } else + super.setEditValue(n, ei); + } + + int getShortcut() { + return 0; } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/MemristorElm.java b/src/main/java/com/lushprojects/circuitjs1/client/MemristorElm.java index fbc8964..f75a2f3 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/MemristorElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/MemristorElm.java @@ -21,139 +21,160 @@ import com.lushprojects.circuitjs1.client.util.Locale; -class MemristorElm extends CircuitElm { +public class MemristorElm extends CircuitElm { double r_on, r_off, dopeWidth, totalWidth, mobility, resistance; + public MemristorElm(int xx, int yy) { - super(xx, yy); - r_on = 100; - r_off = 160*r_on; - dopeWidth = 0; - totalWidth = 10e-9; // meters - mobility = 1e-10; // m^2/sV - resistance = 100; + super(xx, yy); + r_on = 100; + r_off = 160 * r_on; + dopeWidth = 0; + totalWidth = 10e-9; // meters + mobility = 1e-10; // m^2/sV + resistance = 100; } + public MemristorElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f); - r_on = new Double(st.nextToken()).doubleValue(); - r_off = new Double(st.nextToken()).doubleValue(); - dopeWidth = new Double(st.nextToken()).doubleValue(); - totalWidth = new Double(st.nextToken()).doubleValue(); - mobility = new Double(st.nextToken()).doubleValue(); - try { - current = Double.parseDouble(st.nextToken()); - } catch (Exception e) {} - resistance = 100; - } - int getDumpType() { return 'm'; } + StringTokenizer st) { + super(xa, ya, xb, yb, f); + r_on = new Double(st.nextToken()).doubleValue(); + r_off = new Double(st.nextToken()).doubleValue(); + dopeWidth = new Double(st.nextToken()).doubleValue(); + totalWidth = new Double(st.nextToken()).doubleValue(); + mobility = new Double(st.nextToken()).doubleValue(); + try { + current = Double.parseDouble(st.nextToken()); + } catch (Exception e) { + } + resistance = 100; + } + + int getDumpType() { + return 'm'; + } + String dump() { - return super.dump() + " " + r_on + " " + r_off + " " + dopeWidth + " " + - totalWidth + " " + mobility + " " + current; + return super.dump() + " " + r_on + " " + r_off + " " + dopeWidth + " " + + totalWidth + " " + mobility + " " + current; } Point ps3, ps4; + void setPoints() { - super.setPoints(); - calcLeads(32); - ps3 = new Point(); - ps4 = new Point(); + super.setPoints(); + calcLeads(32); + ps3 = new Point(); + ps4 = new Point(); } - + void draw(Graphics g) { - int segments = 6; - int i; - int ox = 0; - double v1 = volts[0]; - double v2 = volts[1]; - int hs = 2+(int) (8*(1-dopeWidth/totalWidth)); - setBbox(point1, point2, hs); - draw2Leads(g); - setPowerColor(g, true); - double segf = 1./segments; - - // draw zigzag - for (i = 0; i <= segments; i++) { - int nx = (i & 1) == 0 ? 1 : -1; - if (i == segments) - nx = 0; - double v = v1+(v2-v1)*i/segments; - setVoltageColor(g, v); - interpPoint(lead1, lead2, ps1, i*segf, hs*ox); - interpPoint(lead1, lead2, ps2, i*segf, hs*nx); - drawThickLine(g, ps1, ps2); - if (i == segments) - break; - interpPoint(lead1, lead2, ps1, (i+1)*segf, hs*nx); - drawThickLine(g, ps1, ps2); - ox = nx; - } - - doDots(g); - drawPosts(g); + int segments = 6; + int i; + int ox = 0; + double v1 = volts[0]; + double v2 = volts[1]; + int hs = 2 + (int) (8 * (1 - dopeWidth / totalWidth)); + setBbox(point1, point2, hs); + draw2Leads(g); + setPowerColor(g, true); + double segf = 1. / segments; + + // draw zigzag + for (i = 0; i <= segments; i++) { + int nx = (i & 1) == 0 ? 1 : -1; + if (i == segments) + nx = 0; + double v = v1 + (v2 - v1) * i / segments; + setVoltageColor(g, v); + interpPoint(lead1, lead2, ps1, i * segf, hs * ox); + interpPoint(lead1, lead2, ps2, i * segf, hs * nx); + drawThickLine(g, ps1, ps2); + if (i == segments) + break; + interpPoint(lead1, lead2, ps1, (i + 1) * segf, hs * nx); + drawThickLine(g, ps1, ps2); + ox = nx; + } + + doDots(g); + drawPosts(g); } - - boolean nonLinear() { return true; } + + boolean nonLinear() { + return true; + } + void calculateCurrent() { - current = (volts[0]-volts[1])/resistance; + current = (volts[0] - volts[1]) / resistance; } + void reset() { - dopeWidth = 0; + dopeWidth = 0; } + void startIteration() { - double wd = dopeWidth/totalWidth; - dopeWidth += sim.timeStep*mobility*r_on*current/totalWidth; - if (dopeWidth < 0) - dopeWidth = 0; - if (dopeWidth > totalWidth) - dopeWidth = totalWidth; - resistance = r_on * wd + r_off * (1-wd); + double wd = dopeWidth / totalWidth; + dopeWidth += sim.timeStep * mobility * r_on * current / totalWidth; + if (dopeWidth < 0) + dopeWidth = 0; + if (dopeWidth > totalWidth) + dopeWidth = totalWidth; + resistance = r_on * wd + r_off * (1 - wd); } + void stamp() { - sim.stampNonLinear(nodes[0]); - sim.stampNonLinear(nodes[1]); + sim.stampNonLinear(nodes[0]); + sim.stampNonLinear(nodes[1]); } + void doStep() { - sim.stampResistor(nodes[0], nodes[1], resistance); + sim.stampResistor(nodes[0], nodes[1], resistance); } + void getInfo(String arr[]) { - arr[0] = "memristor"; - getBasicInfo(arr); - arr[3] = "R = " + getUnitText(resistance, Locale.ohmString); - arr[4] = "P = " + getUnitText(getPower(), "W"); + arr[0] = "memristor"; + getBasicInfo(arr); + arr[3] = "R = " + getUnitText(resistance, Locale.ohmString); + arr[4] = "P = " + getUnitText(getPower(), "W"); } + double getScopeValue(int x) { - return (x == Scope.VAL_R) ? resistance : super.getScopeValue(x); + return (x == Scope.VAL_R) ? resistance : super.getScopeValue(x); } + int getScopeUnits(int x) { - return (x == Scope.VAL_R) ? Scope.UNITS_OHMS : super.getScopeUnits(x); + return (x == Scope.VAL_R) ? Scope.UNITS_OHMS : super.getScopeUnits(x); } + boolean canShowValueInScope(int x) { - return x == Scope.VAL_R; + return x == Scope.VAL_R; } + public EditInfo getEditInfo(int n) { - if (n == 0) - return new EditInfo("Min Resistance (ohms)", r_on, 0, 0); - if (n == 1) - return new EditInfo("Max Resistance (ohms)", r_off, 0, 0); - if (n == 2) - return new EditInfo("Width of Doped Region (nm)", dopeWidth*1e9, 0, 0); - if (n == 3) - return new EditInfo("Total Width (nm)", totalWidth*1e9, 0, 0); - if (n == 4) - return new EditInfo("Mobility (um^2/(s*V))", mobility*1e12, 0, 0); - return null; + if (n == 0) + return new EditInfo("Min Resistance (ohms)", r_on, 0, 0); + if (n == 1) + return new EditInfo("Max Resistance (ohms)", r_off, 0, 0); + if (n == 2) + return new EditInfo("Width of Doped Region (nm)", dopeWidth * 1e9, 0, 0); + if (n == 3) + return new EditInfo("Total Width (nm)", totalWidth * 1e9, 0, 0); + if (n == 4) + return new EditInfo("Mobility (um^2/(s*V))", mobility * 1e12, 0, 0); + return null; } + public void setEditValue(int n, EditInfo ei) { - if (n == 0) - r_on = ei.value; - if (n == 1) - r_off = ei.value; - if (n == 2) - dopeWidth = ei.value*1e-9; - if (n == 3) - totalWidth = ei.value*1e-9; - if (n == 4) - mobility = ei.value*1e-12; + if (n == 0) + r_on = ei.value; + if (n == 1) + r_off = ei.value; + if (n == 2) + dopeWidth = ei.value * 1e-9; + if (n == 3) + totalWidth = ei.value * 1e-9; + if (n == 4) + mobility = ei.value * 1e-12; } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/MonostableElm.java b/src/main/java/com/lushprojects/circuitjs1/client/MonostableElm.java index 5c4a1b8..87d192a 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/MonostableElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/MonostableElm.java @@ -19,90 +19,104 @@ package com.lushprojects.circuitjs1.client; - class MonostableElm extends ChipElm { - - //Used to detect rising edge - private boolean prevInputValue=false; - private boolean retriggerable=false; - private boolean triggered=false; - private double lastRisingEdge=0; - private double delay=0.01; - - public MonostableElm(int xx, int yy) { - super(xx, yy); - reset(); - } - public MonostableElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f, st); - retriggerable=new Boolean(st.nextToken()).booleanValue(); - delay=new Double(st.nextToken()).doubleValue(); - reset(); - } - String getChipName() { return "Monostable"; } - void setupPins() { - sizeX = 2; - sizeY = 2; - pins = new Pin[getPostCount()]; - pins[0] = new Pin(0, SIDE_W, ""); - pins[0].clock = true; - pins[1] = new Pin(0, SIDE_E, "Q"); - pins[1].output=true; - pins[2] = new Pin(1, SIDE_E, "Q"); - pins[2].output=true; - pins[2].lineOver=true; - } - - void reset() { - super.reset(); - pins[2].value = true; - triggered = prevInputValue = false; - } - int getPostCount() { - return 3; - } - int getVoltageSourceCount() { return 2; } - - void execute() { - - if(pins[0].value&&prevInputValue!=pins[0].value&&(retriggerable||!triggered)){ - lastRisingEdge=sim.t; - pins[1].value=true; - pins[2].value=false; - triggered=true; - } - - if(triggered&&sim.t>lastRisingEdge+delay) - { - pins[1].value=false; - pins[2].value=true; - triggered=false; - } - prevInputValue=pins[0].value; - } - String dump(){ - return super.dump() + " " + retriggerable + " " + delay; - } - int getDumpType() { return 194; } - public EditInfo getChipEditInfo(int n) { - if (n == 0) { - EditInfo ei = new EditInfo("", 0, -1, -1); - ei.checkbox=new Checkbox("Retriggerable",retriggerable); - return ei; - } - if (n == 1) { - EditInfo ei = new EditInfo("Period (s)",delay, 0.001,0.1); - return ei; - } - return super.getChipEditInfo(n); - } - public void setChipEditValue(int n, EditInfo ei) { - if (n == 0) { - retriggerable=ei.checkbox.getState(); - } - if (n == 1) { - delay=ei.value; - } - super.setChipEditValue(n, ei); - } +public class MonostableElm extends ChipElm { + + //Used to detect rising edge + private boolean prevInputValue = false; + private boolean retriggerable = false; + private boolean triggered = false; + private double lastRisingEdge = 0; + private double delay = 0.01; + + public MonostableElm(int xx, int yy) { + super(xx, yy); + reset(); + } + + public MonostableElm(int xa, int ya, int xb, int yb, int f, + StringTokenizer st) { + super(xa, ya, xb, yb, f, st); + retriggerable = new Boolean(st.nextToken()).booleanValue(); + delay = new Double(st.nextToken()).doubleValue(); + reset(); + } + + String getChipName() { + return "Monostable"; + } + + void setupPins() { + sizeX = 2; + sizeY = 2; + pins = new Pin[getPostCount()]; + pins[0] = new Pin(0, SIDE_W, ""); + pins[0].clock = true; + pins[1] = new Pin(0, SIDE_E, "Q"); + pins[1].output = true; + pins[2] = new Pin(1, SIDE_E, "Q"); + pins[2].output = true; + pins[2].lineOver = true; + } + + void reset() { + super.reset(); + pins[2].value = true; + triggered = prevInputValue = false; + } + + int getPostCount() { + return 3; + } + + int getVoltageSourceCount() { + return 2; + } + + void execute() { + + if (pins[0].value && prevInputValue != pins[0].value && (retriggerable || !triggered)) { + lastRisingEdge = sim.t; + pins[1].value = true; + pins[2].value = false; + triggered = true; + } + + if (triggered && sim.t > lastRisingEdge + delay) { + pins[1].value = false; + pins[2].value = true; + triggered = false; + } + prevInputValue = pins[0].value; + } + + String dump() { + return super.dump() + " " + retriggerable + " " + delay; + } + + int getDumpType() { + return 194; + } + + public EditInfo getChipEditInfo(int n) { + if (n == 0) { + EditInfo ei = new EditInfo("", 0, -1, -1); + ei.checkbox = new Checkbox("Retriggerable", retriggerable); + return ei; + } + if (n == 1) { + EditInfo ei = new EditInfo("Period (s)", delay, 0.001, 0.1); + return ei; + } + return super.getChipEditInfo(n); + } + + public void setChipEditValue(int n, EditInfo ei) { + if (n == 0) { + retriggerable = ei.checkbox.getState(); + } + if (n == 1) { + delay = ei.value; + } + super.setChipEditValue(n, ei); } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/MosfetElm.java b/src/main/java/com/lushprojects/circuitjs1/client/MosfetElm.java index 7302259..7f855ec 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/MosfetElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/MosfetElm.java @@ -21,518 +21,564 @@ import com.lushprojects.circuitjs1.client.util.Locale; -class MosfetElm extends CircuitElm { - int pnp; - int FLAG_PNP = 1; - int FLAG_SHOWVT = 2; - int FLAG_DIGITAL = 4; - int FLAG_FLIP = 8; - int FLAG_HIDE_BULK = 16; - int FLAG_BODY_DIODE = 32; - int FLAG_BODY_TERMINAL = 64; - int FLAGS_GLOBAL = (FLAG_HIDE_BULK|FLAG_DIGITAL); - int bodyTerminal; - - double vt; - // beta = 1/(RdsON*(Vgs-Vt)) - double beta; - static int globalFlags; - Diode diodeB1, diodeB2; - double diodeCurrent1, diodeCurrent2, bodyCurrent; - double curcount_body1, curcount_body2; - static double lastBeta; - - MosfetElm(int xx, int yy, boolean pnpflag) { - super(xx, yy); - pnp = (pnpflag) ? -1 : 1; - flags = (pnpflag) ? FLAG_PNP : 0; - flags |= FLAG_BODY_DIODE; - noDiagonal = true; - setupDiodes(); - beta = getDefaultBeta(); - vt = getDefaultThreshold(); - } - - public MosfetElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f); - pnp = ((f & FLAG_PNP) != 0) ? -1 : 1; - noDiagonal = true; - setupDiodes(); - vt = getDefaultThreshold(); - beta = getBackwardCompatibilityBeta(); - try { - vt = new Double(st.nextToken()).doubleValue(); - beta = new Double(st.nextToken()).doubleValue(); - } catch (Exception e) {} - globalFlags = flags & (FLAGS_GLOBAL); - allocNodes(); // make sure volts[] has the right number of elements when hasBodyTerminal() is true - } - - // set up body diodes - void setupDiodes() { - // diode from node 1 to body terminal - diodeB1 = new Diode(sim); - diodeB1.setupForDefaultModel(); - // diode from node 2 to body terminal - diodeB2 = new Diode(sim); - diodeB2.setupForDefaultModel(); - } - - double getDefaultThreshold() { return 1.5; } - - // default beta for new elements - double getDefaultBeta() { return lastBeta == 0 ? getBackwardCompatibilityBeta() : lastBeta; } - - // default for elements in old files with no configurable beta. JfetElm overrides this. - // Not sure where this value came from, but the ZVP3306A has a beta of about .027. Power MOSFETs have much higher betas (like 80 or more) - double getBackwardCompatibilityBeta() { return .02; } - - boolean nonLinear() { return true; } - boolean drawDigital() { return (flags & FLAG_DIGITAL) != 0; } - boolean showBulk() { return (flags & (FLAG_DIGITAL|FLAG_HIDE_BULK)) == 0; } - boolean hasBodyTerminal() { return (flags & FLAG_BODY_TERMINAL) != 0 && doBodyDiode(); } - boolean doBodyDiode() { return (flags & FLAG_BODY_DIODE) != 0 && showBulk(); } - void reset() { - lastv1 = lastv2 = volts[0] = volts[1] = volts[2] = curcount = 0; - curcount_body1 = curcount_body2 = 0; - diodeB1.reset(); - diodeB2.reset(); - if (doBodyDiode()) - volts[bodyTerminal] = 0; - } - String dump() { - return super.dump() + " " + vt + " " + beta; - } - int getDumpType() { return 'f'; } - final int hs = 16; - - void draw(Graphics g) { - // pick up global flags changes - if ((flags & FLAGS_GLOBAL) != globalFlags) - setPoints(); - - setBbox(point1, point2, hs); - - // draw source/drain terminals - setVoltageColor(g, volts[1]); - drawThickLine(g, src[0], src[1]); - setVoltageColor(g, volts[2]); - drawThickLine(g, drn[0], drn[1]); - - // draw line connecting source and drain - int segments = 6; - int i; - setPowerColor(g, true); - boolean power = sim.powerCheckItem.getState(); - double segf = 1./segments; - boolean enhancement = vt > 0 && showBulk(); - for (i = 0; i != segments; i++) { - if ((i == 1 || i == 4) && enhancement) continue; - double v = volts[1]+(volts[2]-volts[1])*i/segments; - if (!power) - setVoltageColor(g, v); - interpPoint(src[1], drn[1], ps1, i*segf); - interpPoint(src[1], drn[1], ps2, (i+1)*segf); - drawThickLine(g, ps1, ps2); - } - - // draw little extensions of that line - if (!power) - setVoltageColor(g, volts[1]); - drawThickLine(g, src[1], src[2]); - if (!power) - setVoltageColor(g, volts[2]); - drawThickLine(g, drn[1], drn[2]); - - // draw bulk connection - if (showBulk()) { - setVoltageColor(g, volts[bodyTerminal]); - if (!hasBodyTerminal()) - drawThickLine(g, pnp == -1 ? drn[0] : src[0], body[0]); - drawThickLine(g, body[0], body[1]); - } - - // draw arrow - if (!drawDigital()) { - setVoltageColor(g, volts[bodyTerminal]); - g.fillPolygon(arrowPoly); - } - if (power) - g.setColor(Color.gray); - - // draw gate - setVoltageColor(g, volts[0]); - drawThickLine(g, point1, gate[1]); - drawThickLine(g, gate[0], gate[2]); - if (drawDigital() && pnp == -1) - drawThickCircle(g, pcircle.x, pcircle.y, pcircler); - - if ((flags & FLAG_SHOWVT) != 0) { - String s = "" + (vt*pnp); - g.setColor(whiteColor); - g.setFont(unitsFont); - drawCenteredText(g, s, x2+2, y2, false); - } - curcount = updateDotCount(-ids, curcount); - drawDots(g, src[0], src[1], curcount); - drawDots(g, src[1], drn[1], curcount); - drawDots(g, drn[1], drn[0], curcount); - - if (showBulk()) { - curcount_body1 = updateDotCount(diodeCurrent1, curcount_body1); - curcount_body2 = updateDotCount(diodeCurrent2, curcount_body2); - drawDots(g, src [0], body[0], -curcount_body1); - drawDots(g, body[0], drn [0], curcount_body2); - } - - // label pins when highlighted - if (needsHighlight() || sim.dragElm == this) { - g.setColor(whiteColor); - g.setFont(unitsFont); - - // make fiddly adjustments to pin label locations depending on orientation - int dsx = sign(dx); - int dsy = sign(dy); - int dsyn = dy == 0 ? 0 : 1; - - g.drawString("G", gate[1].x - (dx < 0 ? -2 : 12), gate[1].y + ((dy > 0) ? -5 : 12)); - g.drawString(pnp == -1 ? "D" : "S", src[0].x-3+9*(dsx-dsyn*pnp), src[0].y+4); - g.drawString(pnp == -1 ? "S" : "D", drn[0].x-3+9*(dsx-dsyn*pnp), drn[0].y+4); - if (hasBodyTerminal()) - g.drawString("B", body[0].x-3+9*(dsx-dsyn*pnp), body[0].y+4); - } - - drawPosts(g); - } - - // post 0 = gate, 1 = source for NPN, 2 = drain for NPN, 3 = body (if present) - // for PNP, 1 is drain, 2 is source - Point getPost(int n) { - return (n == 0) ? point1 : (n == 1) ? src[0] : - (n == 2) ? drn[0] : body[0]; - } - - double getCurrent() { return ids; } - double getPower() { - return ids*(volts[2]-volts[1]) - diodeCurrent1*(volts[1]-volts[bodyTerminal]) - diodeCurrent2*(volts[2]-volts[bodyTerminal]); - } - int getPostCount() { return hasBodyTerminal() ? 4 : 3; } - - int pcircler; - - // points for source and drain (these are swapped on PNP mosfets) - Point src[], drn[]; - - // points for gate, body, and the little circle on PNP mosfets - Point gate[], body[], pcircle; - Polygon arrowPoly; - - void setPoints() { - super.setPoints(); - - // these two flags apply to all mosfets - flags &= ~FLAGS_GLOBAL; - flags |= globalFlags; - - // find the coordinates of the various points we need to draw - // the MOSFET. - int hs2 = hs*dsign; - if ((flags & FLAG_FLIP) != 0) - hs2 = -hs2; - src = newPointArray(3); - drn = newPointArray(3); - interpPoint2(point1, point2, src[0], drn[0], 1, -hs2); - interpPoint2(point1, point2, src[1], drn[1], 1-22/dn, -hs2); - interpPoint2(point1, point2, src[2], drn[2], 1-22/dn, -hs2*4/3); - - gate = newPointArray(3); - interpPoint2(point1, point2, gate[0], gate[2], 1-28/dn, hs2/2); // was 1-20/dn - interpPoint(gate[0], gate[2], gate[1], .5); - - if (showBulk()) { - body = newPointArray(2); - interpPoint(src[0], drn[0], body[0], .5); - interpPoint(src[1], drn[1], body[1], .5); - } - - if (!drawDigital()) { - if (pnp == 1) { - if (!showBulk()) - arrowPoly = calcArrow(src[1], src[0], 10, 4); - else - arrowPoly = calcArrow(body[0], body[1], 12, 5); - } else { - if (!showBulk()) - arrowPoly = calcArrow(drn[0], drn[1], 12, 5); - else - arrowPoly = calcArrow(body[1], body[0], 12, 5); - } - } else if (pnp == -1) { - interpPoint(point1, point2, gate[1], 1-36/dn); - int dist = (dsign < 0) ? 32 : 31; - pcircle = interpPoint(point1, point2, 1-dist/dn); - pcircler = 3; - } - } - - double lastv1, lastv2; - double ids; - int mode = 0; - double gm = 0; - - void stamp() { - sim.stampNonLinear(nodes[1]); - sim.stampNonLinear(nodes[2]); - - if (hasBodyTerminal()) - bodyTerminal = 3; - else - bodyTerminal = (pnp == -1) ? 2 : 1; - - if (doBodyDiode()) { - if (pnp == -1) { - // pnp: diodes conduct when S or D are higher than body - diodeB1.stamp(nodes[1], nodes[bodyTerminal]); - diodeB2.stamp(nodes[2], nodes[bodyTerminal]); - } else { - // npn: diodes conduct when body is higher than S or D - diodeB1.stamp(nodes[bodyTerminal], nodes[1]); - diodeB2.stamp(nodes[bodyTerminal], nodes[2]); - } - } - } - - boolean nonConvergence(double last, double now) { - double diff = Math.abs(last-now); - - // high beta MOSFETs are more sensitive to small differences, so we are more strict about convergence testing - if (beta > 1) - diff *= 100; - - // difference of less than 10mV is fine - if (diff < .01) - return false; - // larger differences are fine if value is large - if (sim.subIterations > 10 && diff < Math.abs(now)*.001) - return false; - // if we're having trouble converging, get more lenient - if (sim.subIterations > 100 && diff < .01+(sim.subIterations-100)*.0001) - return false; - return true; - } - - void stepFinished() { - calculate(true); - - // fix current if body is connected to source or drain - if (bodyTerminal == 1) - diodeCurrent1 = -diodeCurrent2; - if (bodyTerminal == 2) - diodeCurrent2 = -diodeCurrent1; - } - - void doStep() { - calculate(false); - } - - double lastv0; - - // this is called in doStep to stamp the matrix, and also called in stepFinished() to calculate the current - void calculate(boolean finished) { - double vs[]; - if (finished) - vs = volts; - else { - // limit voltage changes to .5V - vs = new double[3]; - vs[0] = volts[0]; - vs[1] = volts[1]; - vs[2] = volts[2]; - if (vs[1] > lastv1 + .5) - vs[1] = lastv1 + .5; - if (vs[1] < lastv1 - .5) - vs[1] = lastv1 - .5; - if (vs[2] > lastv2 + .5) - vs[2] = lastv2 + .5; - if (vs[2] < lastv2 - .5) - vs[2] = lastv2 - .5; - } - - int source = 1; - int drain = 2; - - // if source voltage > drain (for NPN), swap source and drain - // (opposite for PNP) - if (pnp*vs[1] > pnp*vs[2]) { - source = 2; - drain = 1; - } - int gate = 0; - double vgs = vs[gate ]-vs[source]; - double vds = vs[drain]-vs[source]; - if (!finished && (nonConvergence(lastv1, vs[1]) || nonConvergence(lastv2, vs[2]) || nonConvergence(lastv0, vs[0]))) - sim.converged = false; - lastv0 = vs[0]; - lastv1 = vs[1]; - lastv2 = vs[2]; - double realvgs = vgs; - double realvds = vds; - vgs *= pnp; - vds *= pnp; - ids = 0; - gm = 0; - double Gds = 0; - if (vgs < vt) { - // should be all zero, but that causes a singular matrix, - // so instead we treat it as a large resistor - Gds = 1e-8; - ids = vds*Gds; - mode = 0; - } else if (vds < vgs-vt) { - // linear - ids = beta*((vgs-vt)*vds - vds*vds*.5); - gm = beta*vds; - Gds = beta*(vgs-vds-vt); - mode = 1; - } else { - // saturation; Gds = 0 - gm = beta*(vgs-vt); - // use very small Gds to avoid nonconvergence - Gds = 1e-8; - ids = .5*beta*(vgs-vt)*(vgs-vt) + (vds-(vgs-vt))*Gds; - mode = 2; - } - - if (doBodyDiode()) { - diodeB1.doStep(pnp*(volts[bodyTerminal]-volts[1])); - diodeCurrent1 = diodeB1.calculateCurrent(pnp*(volts[bodyTerminal]-volts[1]))*pnp; - diodeB2.doStep(pnp*(volts[bodyTerminal]-volts[2])); - diodeCurrent2 = diodeB2.calculateCurrent(pnp*(volts[bodyTerminal]-volts[2]))*pnp; - } else - diodeCurrent1 = diodeCurrent2 = 0; - - double ids0 = ids; - - // flip ids if we swapped source and drain above - if (source == 2 && pnp == 1 || - source == 1 && pnp == -1) - ids = -ids; - - if (finished) - return; - - double rs = -pnp*ids0 + Gds*realvds + gm*realvgs; - sim.stampMatrix(nodes[drain], nodes[drain], Gds); - sim.stampMatrix(nodes[drain], nodes[source], -Gds-gm); - sim.stampMatrix(nodes[drain], nodes[gate], gm); - - sim.stampMatrix(nodes[source], nodes[drain], -Gds); - sim.stampMatrix(nodes[source], nodes[source], Gds+gm); - sim.stampMatrix(nodes[source], nodes[gate], -gm); - - sim.stampRightSide(nodes[drain], rs); - sim.stampRightSide(nodes[source], -rs); - } - - void getFetInfo(String arr[], String n) { - arr[0] = Locale.LS(((pnp == -1) ? "p-" : "n-") + n); - arr[0] += " (Vt=" + getVoltageText(pnp*vt); - arr[0] += ", \u03b2=" + beta + ")"; - arr[1] = ((pnp == 1) ? "Ids = " : "Isd = ") + getCurrentText(ids); - arr[2] = "Vgs = " + getVoltageText(volts[0]-volts[pnp == -1 ? 2 : 1]); - arr[3] = ((pnp == 1) ? "Vds = " : "Vsd = ") + getVoltageText(volts[2]-volts[1]); - arr[4] = Locale.LS((mode == 0) ? "off" : - (mode == 1) ? "linear" : "saturation"); - arr[5] = "gm = " + getUnitText(gm, "A/V"); - arr[6] = "P = " + getUnitText(getPower(), "W"); - if (showBulk()) - arr[7] = "Ib = " + getUnitText(bodyTerminal == 1 ? -diodeCurrent1 : bodyTerminal == 2 ? diodeCurrent2 : -pnp*(diodeCurrent1+diodeCurrent2), "A"); - } - void getInfo(String arr[]) { - getFetInfo(arr, "MOSFET"); - } - @Override String getScopeText(int v) { - return Locale.LS(((pnp == -1) ? "p-" : "n-") + "MOSFET"); - } - boolean canViewInScope() { return true; } - double getVoltageDiff() { return volts[2] - volts[1]; } - boolean getConnection(int n1, int n2) { - return !(n1 == 0 || n2 == 0); - } - public EditInfo getEditInfo(int n) { - if (n == 0) - return new EditInfo("Threshold Voltage", pnp*vt, .01, 5); - if (n == 1) - return new EditInfo(EditInfo.makeLink("mosfet-beta.html", "Beta"), beta, .01, 5); - if (n == 2) { - EditInfo ei = new EditInfo("", 0, -1, -1); - ei.checkbox = new Checkbox("Show Bulk", showBulk()); - return ei; - } - if (n == 3) { - EditInfo ei = new EditInfo("", 0, -1, -1); - ei.checkbox = new Checkbox("Swap D/S", (flags & FLAG_FLIP) != 0); - return ei; - } - if (n == 4 && !showBulk()) { - EditInfo ei = new EditInfo("", 0, -1, -1); - ei.checkbox = new Checkbox("Digital Symbol", drawDigital()); - return ei; - } - if (n == 4 && showBulk()) { - EditInfo ei = new EditInfo("", 0, -1, -1); - ei.checkbox = new Checkbox("Simulate Body Diode", (flags & FLAG_BODY_DIODE) != 0); - return ei; - } - if (n == 5 && doBodyDiode()) { - EditInfo ei = new EditInfo("", 0, -1, -1); - ei.checkbox = new Checkbox("Body Terminal", (flags & FLAG_BODY_TERMINAL) != 0); - return ei; - } - - return null; - } - public void setEditValue(int n, EditInfo ei) { - if (n == 0) - vt = pnp*ei.value; - if (n == 1 && ei.value > 0) - beta = lastBeta = ei.value; - if (n == 2) { - globalFlags = (!ei.checkbox.getState()) ? (globalFlags|FLAG_HIDE_BULK) : - (globalFlags & ~(FLAG_HIDE_BULK|FLAG_DIGITAL)); +public class MosfetElm extends CircuitElm { + int pnp; + int FLAG_PNP = 1; + int FLAG_SHOWVT = 2; + int FLAG_DIGITAL = 4; + int FLAG_FLIP = 8; + int FLAG_HIDE_BULK = 16; + int FLAG_BODY_DIODE = 32; + int FLAG_BODY_TERMINAL = 64; + int FLAGS_GLOBAL = (FLAG_HIDE_BULK | FLAG_DIGITAL); + int bodyTerminal; + + double vt; + // beta = 1/(RdsON*(Vgs-Vt)) + double beta; + static int globalFlags; + Diode diodeB1, diodeB2; + double diodeCurrent1, diodeCurrent2, bodyCurrent; + double curcount_body1, curcount_body2; + static double lastBeta; + + MosfetElm(int xx, int yy, boolean pnpflag) { + super(xx, yy); + pnp = (pnpflag) ? -1 : 1; + flags = (pnpflag) ? FLAG_PNP : 0; + flags |= FLAG_BODY_DIODE; + noDiagonal = true; + setupDiodes(); + beta = getDefaultBeta(); + vt = getDefaultThreshold(); + } + + public MosfetElm(int xa, int ya, int xb, int yb, int f, + StringTokenizer st) { + super(xa, ya, xb, yb, f); + pnp = ((f & FLAG_PNP) != 0) ? -1 : 1; + noDiagonal = true; + setupDiodes(); + vt = getDefaultThreshold(); + beta = getBackwardCompatibilityBeta(); + try { + vt = new Double(st.nextToken()).doubleValue(); + beta = new Double(st.nextToken()).doubleValue(); + } catch (Exception e) { + } + globalFlags = flags & (FLAGS_GLOBAL); + allocNodes(); // make sure volts[] has the right number of elements when hasBodyTerminal() is true + } + + // set up body diodes + void setupDiodes() { + // diode from node 1 to body terminal + diodeB1 = new Diode(sim); + diodeB1.setupForDefaultModel(); + // diode from node 2 to body terminal + diodeB2 = new Diode(sim); + diodeB2.setupForDefaultModel(); + } + + double getDefaultThreshold() { + return 1.5; + } + + // default beta for new elements + double getDefaultBeta() { + return lastBeta == 0 ? getBackwardCompatibilityBeta() : lastBeta; + } + + // default for elements in old files with no configurable beta. JfetElm overrides this. + // Not sure where this value came from, but the ZVP3306A has a beta of about .027. Power MOSFETs have much higher betas (like 80 or more) + double getBackwardCompatibilityBeta() { + return .02; + } + + boolean nonLinear() { + return true; + } + + boolean drawDigital() { + return (flags & FLAG_DIGITAL) != 0; + } + + boolean showBulk() { + return (flags & (FLAG_DIGITAL | FLAG_HIDE_BULK)) == 0; + } + + boolean hasBodyTerminal() { + return (flags & FLAG_BODY_TERMINAL) != 0 && doBodyDiode(); + } + + boolean doBodyDiode() { + return (flags & FLAG_BODY_DIODE) != 0 && showBulk(); + } + + void reset() { + lastv1 = lastv2 = volts[0] = volts[1] = volts[2] = curcount = 0; + curcount_body1 = curcount_body2 = 0; + diodeB1.reset(); + diodeB2.reset(); + if (doBodyDiode()) + volts[bodyTerminal] = 0; + } + + String dump() { + return super.dump() + " " + vt + " " + beta; + } + + int getDumpType() { + return 'f'; + } + + final int hs = 16; + + void draw(Graphics g) { + // pick up global flags changes + if ((flags & FLAGS_GLOBAL) != globalFlags) + setPoints(); + + setBbox(point1, point2, hs); + + // draw source/drain terminals + setVoltageColor(g, volts[1]); + drawThickLine(g, src[0], src[1]); + setVoltageColor(g, volts[2]); + drawThickLine(g, drn[0], drn[1]); + + // draw line connecting source and drain + int segments = 6; + int i; + setPowerColor(g, true); + boolean power = sim.powerCheckItem.getState(); + double segf = 1. / segments; + boolean enhancement = vt > 0 && showBulk(); + for (i = 0; i != segments; i++) { + if ((i == 1 || i == 4) && enhancement) continue; + double v = volts[1] + (volts[2] - volts[1]) * i / segments; + if (!power) + setVoltageColor(g, v); + interpPoint(src[1], drn[1], ps1, i * segf); + interpPoint(src[1], drn[1], ps2, (i + 1) * segf); + drawThickLine(g, ps1, ps2); + } + + // draw little extensions of that line + if (!power) + setVoltageColor(g, volts[1]); + drawThickLine(g, src[1], src[2]); + if (!power) + setVoltageColor(g, volts[2]); + drawThickLine(g, drn[1], drn[2]); + + // draw bulk connection + if (showBulk()) { + setVoltageColor(g, volts[bodyTerminal]); + if (!hasBodyTerminal()) + drawThickLine(g, pnp == -1 ? drn[0] : src[0], body[0]); + drawThickLine(g, body[0], body[1]); + } + + // draw arrow + if (!drawDigital()) { + setVoltageColor(g, volts[bodyTerminal]); + g.fillPolygon(arrowPoly); + } + if (power) + g.setColor(Color.gray); + + // draw gate + setVoltageColor(g, volts[0]); + drawThickLine(g, point1, gate[1]); + drawThickLine(g, gate[0], gate[2]); + if (drawDigital() && pnp == -1) + drawThickCircle(g, pcircle.x, pcircle.y, pcircler); + + if ((flags & FLAG_SHOWVT) != 0) { + String s = "" + (vt * pnp); + g.setColor(whiteColor); + g.setFont(unitsFont); + drawCenteredText(g, s, x2 + 2, y2, false); + } + curcount = updateDotCount(-ids, curcount); + drawDots(g, src[0], src[1], curcount); + drawDots(g, src[1], drn[1], curcount); + drawDots(g, drn[1], drn[0], curcount); + + if (showBulk()) { + curcount_body1 = updateDotCount(diodeCurrent1, curcount_body1); + curcount_body2 = updateDotCount(diodeCurrent2, curcount_body2); + drawDots(g, src[0], body[0], -curcount_body1); + drawDots(g, body[0], drn[0], curcount_body2); + } + + // label pins when highlighted + if (needsHighlight() || sim.dragElm == this) { + g.setColor(whiteColor); + g.setFont(unitsFont); + + // make fiddly adjustments to pin label locations depending on orientation + int dsx = sign(dx); + int dsy = sign(dy); + int dsyn = dy == 0 ? 0 : 1; + + g.drawString("G", gate[1].x - (dx < 0 ? -2 : 12), gate[1].y + ((dy > 0) ? -5 : 12)); + g.drawString(pnp == -1 ? "D" : "S", src[0].x - 3 + 9 * (dsx - dsyn * pnp), src[0].y + 4); + g.drawString(pnp == -1 ? "S" : "D", drn[0].x - 3 + 9 * (dsx - dsyn * pnp), drn[0].y + 4); + if (hasBodyTerminal()) + g.drawString("B", body[0].x - 3 + 9 * (dsx - dsyn * pnp), body[0].y + 4); + } + + drawPosts(g); + } + + // post 0 = gate, 1 = source for NPN, 2 = drain for NPN, 3 = body (if present) + // for PNP, 1 is drain, 2 is source + Point getPost(int n) { + return (n == 0) ? point1 : (n == 1) ? src[0] : + (n == 2) ? drn[0] : body[0]; + } + + double getCurrent() { + return ids; + } + + double getPower() { + return ids * (volts[2] - volts[1]) - diodeCurrent1 * (volts[1] - volts[bodyTerminal]) - diodeCurrent2 * (volts[2] - volts[bodyTerminal]); + } + + int getPostCount() { + return hasBodyTerminal() ? 4 : 3; + } + + int pcircler; + + // points for source and drain (these are swapped on PNP mosfets) + Point src[], drn[]; + + // points for gate, body, and the little circle on PNP mosfets + Point gate[], body[], pcircle; + Polygon arrowPoly; + + void setPoints() { + super.setPoints(); + + // these two flags apply to all mosfets + flags &= ~FLAGS_GLOBAL; + flags |= globalFlags; + + // find the coordinates of the various points we need to draw + // the MOSFET. + int hs2 = hs * dsign; + if ((flags & FLAG_FLIP) != 0) + hs2 = -hs2; + src = newPointArray(3); + drn = newPointArray(3); + interpPoint2(point1, point2, src[0], drn[0], 1, -hs2); + interpPoint2(point1, point2, src[1], drn[1], 1 - 22 / dn, -hs2); + interpPoint2(point1, point2, src[2], drn[2], 1 - 22 / dn, -hs2 * 4 / 3); + + gate = newPointArray(3); + interpPoint2(point1, point2, gate[0], gate[2], 1 - 28 / dn, hs2 / 2); // was 1-20/dn + interpPoint(gate[0], gate[2], gate[1], .5); + + if (showBulk()) { + body = newPointArray(2); + interpPoint(src[0], drn[0], body[0], .5); + interpPoint(src[1], drn[1], body[1], .5); + } + + if (!drawDigital()) { + if (pnp == 1) { + if (!showBulk()) + arrowPoly = calcArrow(src[1], src[0], 10, 4); + else + arrowPoly = calcArrow(body[0], body[1], 12, 5); + } else { + if (!showBulk()) + arrowPoly = calcArrow(drn[0], drn[1], 12, 5); + else + arrowPoly = calcArrow(body[1], body[0], 12, 5); + } + } else if (pnp == -1) { + interpPoint(point1, point2, gate[1], 1 - 36 / dn); + int dist = (dsign < 0) ? 32 : 31; + pcircle = interpPoint(point1, point2, 1 - dist / dn); + pcircler = 3; + } + } + + double lastv1, lastv2; + double ids; + int mode = 0; + double gm = 0; + + void stamp() { + sim.stampNonLinear(nodes[1]); + sim.stampNonLinear(nodes[2]); + + if (hasBodyTerminal()) + bodyTerminal = 3; + else + bodyTerminal = (pnp == -1) ? 2 : 1; + + if (doBodyDiode()) { + if (pnp == -1) { + // pnp: diodes conduct when S or D are higher than body + diodeB1.stamp(nodes[1], nodes[bodyTerminal]); + diodeB2.stamp(nodes[2], nodes[bodyTerminal]); + } else { + // npn: diodes conduct when body is higher than S or D + diodeB1.stamp(nodes[bodyTerminal], nodes[1]); + diodeB2.stamp(nodes[bodyTerminal], nodes[2]); + } + } + } + + boolean nonConvergence(double last, double now) { + double diff = Math.abs(last - now); + + // high beta MOSFETs are more sensitive to small differences, so we are more strict about convergence testing + if (beta > 1) + diff *= 100; + + // difference of less than 10mV is fine + if (diff < .01) + return false; + // larger differences are fine if value is large + if (sim.subIterations > 10 && diff < Math.abs(now) * .001) + return false; + // if we're having trouble converging, get more lenient + if (sim.subIterations > 100 && diff < .01 + (sim.subIterations - 100) * .0001) + return false; + return true; + } + + void stepFinished() { + calculate(true); + + // fix current if body is connected to source or drain + if (bodyTerminal == 1) + diodeCurrent1 = -diodeCurrent2; + if (bodyTerminal == 2) + diodeCurrent2 = -diodeCurrent1; + } + + void doStep() { + calculate(false); + } + + double lastv0; + + // this is called in doStep to stamp the matrix, and also called in stepFinished() to calculate the current + void calculate(boolean finished) { + double vs[]; + if (finished) + vs = volts; + else { + // limit voltage changes to .5V + vs = new double[3]; + vs[0] = volts[0]; + vs[1] = volts[1]; + vs[2] = volts[2]; + if (vs[1] > lastv1 + .5) + vs[1] = lastv1 + .5; + if (vs[1] < lastv1 - .5) + vs[1] = lastv1 - .5; + if (vs[2] > lastv2 + .5) + vs[2] = lastv2 + .5; + if (vs[2] < lastv2 - .5) + vs[2] = lastv2 - .5; + } + + int source = 1; + int drain = 2; + + // if source voltage > drain (for NPN), swap source and drain + // (opposite for PNP) + if (pnp * vs[1] > pnp * vs[2]) { + source = 2; + drain = 1; + } + int gate = 0; + double vgs = vs[gate] - vs[source]; + double vds = vs[drain] - vs[source]; + if (!finished && (nonConvergence(lastv1, vs[1]) || nonConvergence(lastv2, vs[2]) || nonConvergence(lastv0, vs[0]))) + sim.converged = false; + lastv0 = vs[0]; + lastv1 = vs[1]; + lastv2 = vs[2]; + double realvgs = vgs; + double realvds = vds; + vgs *= pnp; + vds *= pnp; + ids = 0; + gm = 0; + double Gds = 0; + if (vgs < vt) { + // should be all zero, but that causes a singular matrix, + // so instead we treat it as a large resistor + Gds = 1e-8; + ids = vds * Gds; + mode = 0; + } else if (vds < vgs - vt) { + // linear + ids = beta * ((vgs - vt) * vds - vds * vds * .5); + gm = beta * vds; + Gds = beta * (vgs - vds - vt); + mode = 1; + } else { + // saturation; Gds = 0 + gm = beta * (vgs - vt); + // use very small Gds to avoid nonconvergence + Gds = 1e-8; + ids = .5 * beta * (vgs - vt) * (vgs - vt) + (vds - (vgs - vt)) * Gds; + mode = 2; + } + + if (doBodyDiode()) { + diodeB1.doStep(pnp * (volts[bodyTerminal] - volts[1])); + diodeCurrent1 = diodeB1.calculateCurrent(pnp * (volts[bodyTerminal] - volts[1])) * pnp; + diodeB2.doStep(pnp * (volts[bodyTerminal] - volts[2])); + diodeCurrent2 = diodeB2.calculateCurrent(pnp * (volts[bodyTerminal] - volts[2])) * pnp; + } else + diodeCurrent1 = diodeCurrent2 = 0; + + double ids0 = ids; + + // flip ids if we swapped source and drain above + if (source == 2 && pnp == 1 || + source == 1 && pnp == -1) + ids = -ids; + + if (finished) + return; + + double rs = -pnp * ids0 + Gds * realvds + gm * realvgs; + sim.stampMatrix(nodes[drain], nodes[drain], Gds); + sim.stampMatrix(nodes[drain], nodes[source], -Gds - gm); + sim.stampMatrix(nodes[drain], nodes[gate], gm); + + sim.stampMatrix(nodes[source], nodes[drain], -Gds); + sim.stampMatrix(nodes[source], nodes[source], Gds + gm); + sim.stampMatrix(nodes[source], nodes[gate], -gm); + + sim.stampRightSide(nodes[drain], rs); + sim.stampRightSide(nodes[source], -rs); + } + + void getFetInfo(String arr[], String n) { + arr[0] = Locale.LS(((pnp == -1) ? "p-" : "n-") + n); + arr[0] += " (Vt=" + getVoltageText(pnp * vt); + arr[0] += ", \u03b2=" + beta + ")"; + arr[1] = ((pnp == 1) ? "Ids = " : "Isd = ") + getCurrentText(ids); + arr[2] = "Vgs = " + getVoltageText(volts[0] - volts[pnp == -1 ? 2 : 1]); + arr[3] = ((pnp == 1) ? "Vds = " : "Vsd = ") + getVoltageText(volts[2] - volts[1]); + arr[4] = Locale.LS((mode == 0) ? "off" : + (mode == 1) ? "linear" : "saturation"); + arr[5] = "gm = " + getUnitText(gm, "A/V"); + arr[6] = "P = " + getUnitText(getPower(), "W"); + if (showBulk()) + arr[7] = "Ib = " + getUnitText(bodyTerminal == 1 ? -diodeCurrent1 : bodyTerminal == 2 ? diodeCurrent2 : -pnp * (diodeCurrent1 + diodeCurrent2), "A"); + } + + void getInfo(String arr[]) { + getFetInfo(arr, "MOSFET"); + } + + @Override + String getScopeText(int v) { + return Locale.LS(((pnp == -1) ? "p-" : "n-") + "MOSFET"); + } + + boolean canViewInScope() { + return true; + } + + double getVoltageDiff() { + return volts[2] - volts[1]; + } + + boolean getConnection(int n1, int n2) { + return !(n1 == 0 || n2 == 0); + } + + public EditInfo getEditInfo(int n) { + if (n == 0) + return new EditInfo("Threshold Voltage", pnp * vt, .01, 5); + if (n == 1) + return new EditInfo(EditInfo.makeLink("mosfet-beta.html", "Beta"), beta, .01, 5); + if (n == 2) { + EditInfo ei = new EditInfo("", 0, -1, -1); + ei.checkbox = new Checkbox("Show Bulk", showBulk()); + return ei; + } + if (n == 3) { + EditInfo ei = new EditInfo("", 0, -1, -1); + ei.checkbox = new Checkbox("Swap D/S", (flags & FLAG_FLIP) != 0); + return ei; + } + if (n == 4 && !showBulk()) { + EditInfo ei = new EditInfo("", 0, -1, -1); + ei.checkbox = new Checkbox("Digital Symbol", drawDigital()); + return ei; + } + if (n == 4 && showBulk()) { + EditInfo ei = new EditInfo("", 0, -1, -1); + ei.checkbox = new Checkbox("Simulate Body Diode", (flags & FLAG_BODY_DIODE) != 0); + return ei; + } + if (n == 5 && doBodyDiode()) { + EditInfo ei = new EditInfo("", 0, -1, -1); + ei.checkbox = new Checkbox("Body Terminal", (flags & FLAG_BODY_TERMINAL) != 0); + return ei; + } + + return null; + } + + public void setEditValue(int n, EditInfo ei) { + if (n == 0) + vt = pnp * ei.value; + if (n == 1 && ei.value > 0) + beta = lastBeta = ei.value; + if (n == 2) { + globalFlags = (!ei.checkbox.getState()) ? (globalFlags | FLAG_HIDE_BULK) : + (globalFlags & ~(FLAG_HIDE_BULK | FLAG_DIGITAL)); // setPoints(); - ei.newDialog = true; - } - if (n == 3) { - flags = (ei.checkbox.getState()) ? (flags | FLAG_FLIP) : - (flags & ~FLAG_FLIP); + ei.newDialog = true; + } + if (n == 3) { + flags = (ei.checkbox.getState()) ? (flags | FLAG_FLIP) : + (flags & ~FLAG_FLIP); // setPoints(); - } - if (n == 4 && !showBulk()) { - globalFlags = (ei.checkbox.getState()) ? (globalFlags|FLAG_DIGITAL) : - (globalFlags & ~FLAG_DIGITAL); + } + if (n == 4 && !showBulk()) { + globalFlags = (ei.checkbox.getState()) ? (globalFlags | FLAG_DIGITAL) : + (globalFlags & ~FLAG_DIGITAL); // setPoints(); - } - if (n == 4 && showBulk()) { - flags = ei.changeFlag(flags, FLAG_BODY_DIODE); - ei.newDialog = true; - } - if (n == 5) { - flags = ei.changeFlag(flags, FLAG_BODY_TERMINAL); - } - - // lots of different cases where the body terminal might have gotten removed/added so just do this all the time - allocNodes(); - setPoints(); - } - double getCurrentIntoNode(int n) { - if (n == 0) - return 0; - if (n == 3) - return -diodeCurrent1 - diodeCurrent2; - if (n == 1) - return ids + diodeCurrent1; - return -ids + diodeCurrent2; - } + } + if (n == 4 && showBulk()) { + flags = ei.changeFlag(flags, FLAG_BODY_DIODE); + ei.newDialog = true; + } + if (n == 5) { + flags = ei.changeFlag(flags, FLAG_BODY_TERMINAL); + } + + // lots of different cases where the body terminal might have gotten removed/added so just do this all the time + allocNodes(); + setPoints(); + } + + double getCurrentIntoNode(int n) { + if (n == 0) + return 0; + if (n == 3) + return -diodeCurrent1 - diodeCurrent2; + if (n == 1) + return ids + diodeCurrent1; + return -ids + diodeCurrent2; } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/MultiplexerElm.java b/src/main/java/com/lushprojects/circuitjs1/client/MultiplexerElm.java index 31bfa75..3f81e7d 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/MultiplexerElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/MultiplexerElm.java @@ -21,121 +21,139 @@ // contributed by Edward Calver - class MultiplexerElm extends ChipElm { - final int FLAG_INVERTED_OUTPUT = 1<<1; - final int FLAG_STROBE = 1<<2; - int selectBitCount; - int outputCount; - int strobe; - int outputPin; - - boolean hasReset() {return false;} - public MultiplexerElm(int xx, int yy) { - super(xx, yy); - selectBitCount = 2; - setupPins(); - } - public MultiplexerElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f, st); - selectBitCount = 2; - try { - selectBitCount = Integer.parseInt(st.nextToken()); - } catch (Exception e) {} - setupPins(); - } - String getChipName() { return "Multiplexer"; } - String dump() { return super.dump() + " " + selectBitCount; } - void setupPins() { - sizeX = selectBitCount+1; - outputCount = 1; - int i; - for (i = 0; i != selectBitCount; i++) - outputCount <<= 1; - sizeY = outputCount+1; - - pins = new Pin[getPostCount()]; - - for (i = 0; i != outputCount; i++) - pins[i] = new Pin(i, SIDE_W, "I" + i); - - int n = outputCount; - for (i = 0; i != selectBitCount; i++, n++) - pins[n] = new Pin(i+1, SIDE_S, "S" + i); - - pins[n] = new Pin(0, SIDE_E, "Q"); - pins[n].output=true; - outputPin = n; - if (hasFlag(FLAG_INVERTED_OUTPUT)) { - n++; - pins[n] = new Pin(1, SIDE_E, "Q"); - pins[n].lineOver = true; - pins[n].output=true; - pins[n].bubble = true; - } - if (hasFlag(FLAG_STROBE)) { - n++; - pins[n] = new Pin(0, SIDE_S, "STR"); - strobe = n; - } else - strobe = -1; - - allocNodes(); - - } - int getPostCount() { - return outputCount + selectBitCount + 1 + (hasFlag(FLAG_INVERTED_OUTPUT) ? 1 : 0) + (hasFlag(FLAG_STROBE) ? 1 : 0); - } - int getVoltageSourceCount() {return hasFlag(FLAG_INVERTED_OUTPUT) ? 2 : 1;} - - void execute() { - int selectedValue=0; - int i; - for (i = 0; i != selectBitCount; i++) - if (pins[outputCount+i].value) - selectedValue |= 1<= 1 && ei.value <= 6) { + selectBitCount = (int) ei.value; + setupPins(); + setPoints(); + return; } - - public void setChipEditValue(int n, EditInfo ei) { - if (n == 0 && ei.value >= 1 && ei.value <= 6) { - selectBitCount = (int) ei.value; - setupPins(); - setPoints(); - return; - } - if (n == 1) { - flags = ei.changeFlag(flags, FLAG_INVERTED_OUTPUT); - setupPins(); - setPoints(); - return; - } - if (n == 2) { - flags = ei.changeFlag(flags, FLAG_STROBE); - setupPins(); - setPoints(); - return; - } - super.setChipEditValue(n, ei); + if (n == 1) { + flags = ei.changeFlag(flags, FLAG_INVERTED_OUTPUT); + setupPins(); + setPoints(); + return; } - + if (n == 2) { + flags = ei.changeFlag(flags, FLAG_STROBE); + setupPins(); + setPoints(); + return; + } + super.setChipEditValue(n, ei); } + +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/MyCommand.java b/src/main/java/com/lushprojects/circuitjs1/client/MyCommand.java index b286536..cb33a20 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/MyCommand.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/MyCommand.java @@ -22,16 +22,16 @@ import com.google.gwt.user.client.Command; public class MyCommand implements Command { - private final String menuName; - private final String itemName; - - public MyCommand(String name, String item){ - menuName=name; - itemName=item; - } - - public void execute() { - circuitjs1.mysim.menuPerformed(menuName, itemName); - } + private final String menuName; + private final String itemName; + + public MyCommand(String name, String item) { + menuName = name; + itemName = item; + } + + public void execute() { + circuitjs1.mysim.menuPerformed(menuName, itemName); + } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/NDarlingtonElm.java b/src/main/java/com/lushprojects/circuitjs1/client/NDarlingtonElm.java index 5c80758..e7d53de 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/NDarlingtonElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/NDarlingtonElm.java @@ -3,13 +3,12 @@ public class NDarlingtonElm extends DarlingtonElm { - public NDarlingtonElm(int xx, int yy) { - super(xx, yy, false); + super(xx, yy, false); } Class getDumpClass() { - return DarlingtonElm.class; + return DarlingtonElm.class; } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/NJfetElm.java b/src/main/java/com/lushprojects/circuitjs1/client/NJfetElm.java index 3109f49..60a6186 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/NJfetElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/NJfetElm.java @@ -19,13 +19,23 @@ package com.lushprojects.circuitjs1.client; -class NJfetElm extends JfetElm { - public NJfetElm(int xx, int yy) { super(xx, yy, false); } - Class getDumpClass() { return JfetElm.class; } +public class NJfetElm extends JfetElm { + public NJfetElm(int xx, int yy) { + super(xx, yy, false); } - class PJfetElm extends JfetElm { - public PJfetElm(int xx, int yy) { super(xx, yy, true); } - Class getDumpClass() { return JfetElm.class; } + Class getDumpClass() { + return JfetElm.class; } +} + +public class PJfetElm extends JfetElm { + public PJfetElm(int xx, int yy) { + super(xx, yy, true); + } + + Class getDumpClass() { + return JfetElm.class; + } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/NMosfetElm.java b/src/main/java/com/lushprojects/circuitjs1/client/NMosfetElm.java index 22acfc9..8bed0ae 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/NMosfetElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/NMosfetElm.java @@ -19,8 +19,16 @@ package com.lushprojects.circuitjs1.client; -class NMosfetElm extends MosfetElm { - public NMosfetElm(int xx, int yy) { super(xx, yy, false); } - Class getDumpClass() { return MosfetElm.class; } - int getShortcut() { return 'N'; } +public class NMosfetElm extends MosfetElm { + public NMosfetElm(int xx, int yy) { + super(xx, yy, false); } + + Class getDumpClass() { + return MosfetElm.class; + } + + int getShortcut() { + return 'N'; + } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/NTransistorElm.java b/src/main/java/com/lushprojects/circuitjs1/client/NTransistorElm.java index 099a83f..7948361 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/NTransistorElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/NTransistorElm.java @@ -19,10 +19,17 @@ package com.lushprojects.circuitjs1.client; -class NTransistorElm extends TransistorElm { - public NTransistorElm(int xx, int yy) { super(xx, yy, false); } - Class getDumpClass() { return TransistorElm.class; } - - int getShortcut() { return 'n'; } - +public class NTransistorElm extends TransistorElm { + public NTransistorElm(int xx, int yy) { + super(xx, yy, false); } + + Class getDumpClass() { + return TransistorElm.class; + } + + int getShortcut() { + return 'n'; + } + +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/NandGateElm.java b/src/main/java/com/lushprojects/circuitjs1/client/NandGateElm.java index aeb2f89..d9ac9d3 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/NandGateElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/NandGateElm.java @@ -19,14 +19,29 @@ package com.lushprojects.circuitjs1.client; - class NandGateElm extends AndGateElm { - public NandGateElm(int xx, int yy) { super(xx, yy); } - public NandGateElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f, st); - } - boolean isInverting() { return true; } - String getGateName() { return "NAND gate"; } - int getDumpType() { return 151; } - int getShortcut() { return '@'; } +public class NandGateElm extends AndGateElm { + public NandGateElm(int xx, int yy) { + super(xx, yy); } + + public NandGateElm(int xa, int ya, int xb, int yb, int f, + StringTokenizer st) { + super(xa, ya, xb, yb, f, st); + } + + boolean isInverting() { + return true; + } + + String getGateName() { + return "NAND gate"; + } + + int getDumpType() { + return 151; + } + + int getShortcut() { + return '@'; + } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/NoiseElm.java b/src/main/java/com/lushprojects/circuitjs1/client/NoiseElm.java index 0d0afaf..120c1ad 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/NoiseElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/NoiseElm.java @@ -19,15 +19,20 @@ package com.lushprojects.circuitjs1.client; - class NoiseElm extends RailElm { - public NoiseElm(int xx, int yy) { super(xx, yy, WF_NOISE); } - public NoiseElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f, st); - waveform = WF_NOISE; - } - - // dump this class as a RailElm. The 'n' dump type is still used in CirSim.createCe to read old files +public class NoiseElm extends RailElm { + public NoiseElm(int xx, int yy) { + super(xx, yy, WF_NOISE); + } + + public NoiseElm(int xa, int ya, int xb, int yb, int f, + StringTokenizer st) { + super(xa, ya, xb, yb, f, st); + waveform = WF_NOISE; + } + + // dump this class as a RailElm. The 'n' dump type is still used in CirSim.createCe to read old files // int getDumpType() { return 'n'; } - int getShortcut() { return 0; } + int getShortcut() { + return 0; } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/NorGateElm.java b/src/main/java/com/lushprojects/circuitjs1/client/NorGateElm.java index 7bd9b86..6c3a112 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/NorGateElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/NorGateElm.java @@ -19,14 +19,29 @@ package com.lushprojects.circuitjs1.client; - class NorGateElm extends OrGateElm { - public NorGateElm(int xx, int yy) { super(xx, yy); } - public NorGateElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f, st); - } - String getGateName() { return "NOR gate"; } - boolean isInverting() { return true; } - int getDumpType() { return 153; } - int getShortcut() { return '#'; } +public class NorGateElm extends OrGateElm { + public NorGateElm(int xx, int yy) { + super(xx, yy); } + + public NorGateElm(int xa, int ya, int xb, int yb, int f, + StringTokenizer st) { + super(xa, ya, xb, yb, f, st); + } + + String getGateName() { + return "NOR gate"; + } + + boolean isInverting() { + return true; + } + + int getDumpType() { + return 153; + } + + int getShortcut() { + return '#'; + } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/OTAElm.java b/src/main/java/com/lushprojects/circuitjs1/client/OTAElm.java index 764d67f..9f09c83 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/OTAElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/OTAElm.java @@ -3,7 +3,7 @@ public class OTAElm extends CompositeElm { private static String modelString = "RailElm 4\rRailElm 10\rNTransistorElm 1 2 3\rNTransistorElm 3 1 4\rNTransistorElm 3 3 4\rNTransistorElm 5 6 2\rNTransistorElm 7 8 2\rPTransistorElm 9 6 10\rPTransistorElm 9 9 10\rPTransistorElm 6 12 9\rPTransistorElm 11 8 10\rPTransistorElm 11 11 10\rPTransistorElm 8 13 11\rNTransistorElm 14 14 4\rNTransistorElm 14 12 4\rNTransistorElm 12 13 14\rNTransistorElm 15 15 5\rNTransistorElm 15 15 7"; - private static int[] modelExternalNodes = { 7, 5, 15, 1, 13 }; + private static int[] modelExternalNodes = {7, 5, 15, 1, 13}; // private static String modelString="NTransistorElm 1 1 2\rNTransistorElm 1 // 2 3\rNTransistorElm 1 3 4\rNTransistorElm 1 4 5"; // private static int[] modelExternalNodes = { 1, 2, 3 , 4, 5}; @@ -27,149 +27,149 @@ public class OTAElm extends CompositeElm { double negVolt = -9.0; public OTAElm(int xx, int yy) { - super(xx, yy, modelString, modelExternalNodes); - noDiagonal = true; - initOTA(); + super(xx, yy, modelString, modelExternalNodes); + noDiagonal = true; + initOTA(); } public OTAElm(int xa, int ya, int xb, int yb, int f, StringTokenizer st) { - super(xa, ya, xb, yb, f, st, modelString, modelExternalNodes); - noDiagonal = true; - negVolt = ((RailElm) compElmList.get(0)).maxVoltage; - posVolt = ((RailElm) compElmList.get(1)).maxVoltage; + super(xa, ya, xb, yb, f, st, modelString, modelExternalNodes); + noDiagonal = true; + negVolt = ((RailElm) compElmList.get(0)).maxVoltage; + posVolt = ((RailElm) compElmList.get(1)).maxVoltage; } private void initOTA() { - ((RailElm) compElmList.get(0)).maxVoltage = negVolt; - ((RailElm) compElmList.get(1)).maxVoltage = posVolt; + ((RailElm) compElmList.get(0)).maxVoltage = negVolt; + ((RailElm) compElmList.get(1)).maxVoltage = posVolt; } public void reset() { - super.reset(); - curCount0 = curCount1 = curCount2 = curCount3 = 0; + super.reset(); + curCount0 = curCount1 = curCount2 = curCount3 = 0; } public boolean getConnection(int n1, int n2) { - return false; + return false; } void draw(Graphics g) { - setBbox(point1, point2, 3 * opheight / 2); - setVoltageColor(g, volts[0]); - drawThickLine(g, in1p[0], in1p[1]); - setVoltageColor(g, volts[1]); - drawThickLine(g, in2p[0], in2p[1]); - setVoltageColor(g, volts[2]); - drawThickLine(g, in3p[0], in3p[1]); - setVoltageColor(g, volts[3]); - drawThickLine(g, in4p[0], in4p[1]); - g.setColor(needsHighlight() ? selectColor : lightGrayColor); - setPowerColor(g, true); - drawThickPolygon(g, triangle); - g.fillPolygon(arrowPoly1); - g.fillPolygon(arrowPoly2); - drawThickLine(g, bar1[0], bar1[1]); - drawThickLine(g, bar2[0], bar2[1]); - drawThickCircle(g, circCent[0].x, circCent[0].y, circDiam / 2); - drawThickCircle(g, circCent[1].x, circCent[1].y, circDiam / 2); - g.setFont(plusFont); - drawCenteredText(g, "+", textp[0].x, textp[0].y - 2, true); - drawCenteredText(g, "-", textp[1].x, textp[1].y, true); - // setVoltageColor(g, volts[2]); - // drawThickLine(g, lead2, point2); - curCount0 = updateDotCount(-getCurrentIntoNode(0), curCount0); - drawDots(g, in1p[0], in1p[1], curCount0); - curCount1 = updateDotCount(-getCurrentIntoNode(1), curCount1); - drawDots(g, in2p[0], in2p[1], curCount0); - curCount2 = updateDotCount(-getCurrentIntoNode(2), curCount2); - drawDots(g, in3p[0], in3p[1], curCount2); - curCount3 = updateDotCount(-getCurrentIntoNode(3), curCount3); - drawDots(g, in4p[0], in4p[1], curCount3); - drawPosts(g); + setBbox(point1, point2, 3 * opheight / 2); + setVoltageColor(g, volts[0]); + drawThickLine(g, in1p[0], in1p[1]); + setVoltageColor(g, volts[1]); + drawThickLine(g, in2p[0], in2p[1]); + setVoltageColor(g, volts[2]); + drawThickLine(g, in3p[0], in3p[1]); + setVoltageColor(g, volts[3]); + drawThickLine(g, in4p[0], in4p[1]); + g.setColor(needsHighlight() ? selectColor : lightGrayColor); + setPowerColor(g, true); + drawThickPolygon(g, triangle); + g.fillPolygon(arrowPoly1); + g.fillPolygon(arrowPoly2); + drawThickLine(g, bar1[0], bar1[1]); + drawThickLine(g, bar2[0], bar2[1]); + drawThickCircle(g, circCent[0].x, circCent[0].y, circDiam / 2); + drawThickCircle(g, circCent[1].x, circCent[1].y, circDiam / 2); + g.setFont(plusFont); + drawCenteredText(g, "+", textp[0].x, textp[0].y - 2, true); + drawCenteredText(g, "-", textp[1].x, textp[1].y, true); + // setVoltageColor(g, volts[2]); + // drawThickLine(g, lead2, point2); + curCount0 = updateDotCount(-getCurrentIntoNode(0), curCount0); + drawDots(g, in1p[0], in1p[1], curCount0); + curCount1 = updateDotCount(-getCurrentIntoNode(1), curCount1); + drawDots(g, in2p[0], in2p[1], curCount0); + curCount2 = updateDotCount(-getCurrentIntoNode(2), curCount2); + drawDots(g, in3p[0], in3p[1], curCount2); + curCount3 = updateDotCount(-getCurrentIntoNode(3), curCount3); + drawDots(g, in4p[0], in4p[1], curCount3); + drawPosts(g); } void setPoints() { - super.setPoints(); - int ww = opwidth; - int wtot = ww * 2 + 2 * circDiam - circOverlap; - - if (dn > wtot) { - lead1 = interpPoint(point1, point2, 1.0 - wtot / dn, 0); - lead2 = point2; - point2bis = point2; - } else { - lead1 = point1; - lead2 = interpPoint(point1, point2, wtot / dn, 0); - point2bis = lead2; - } - int hs = opheight * dsign; - // if ((flags & FLAG_SWAP) != 0) - // hs = -hs; - in1p = newPointArray(2); - in2p = newPointArray(2); - in3p = newPointArray(2); - in4p = newPointArray(2); - textp = newPointArray(2); - bar1 = newPointArray(2); - bar2 = newPointArray(2); - circCent = newPointArray(2); - interpPoint2(point1, point2bis, in1p[0], in2p[0], 0, hs); - interpPoint2(lead1, lead2, in1p[1], in2p[1], 0, hs); - interpPoint2(lead1, lead2, textp[0], textp[1], .1, hs); - in3p[0] = point1; - in3p[1] = lead1; - in4p[0] = interpPoint(lead1, lead2, 1.0 - (16.0 / wtot), 32); - in4p[1] = interpPoint(lead1, lead2, 1.0 - (16.0 / wtot), 8); - // in4p[0].x=sim.snapGrid(in4p[0].x); - // in4p[0].y=sim.snapGrid(in4p[0].y); - Point tris[] = newPointArray(3); - interpPoint2(lead1, lead2, tris[0], tris[1], 0, 3 * hs / 2); - tris[2] = interpPoint(lead1, lead2, (2.0 * ww) / wtot); - triangle = createPolygon(tris[0], tris[1], tris[2]); - circCent[0] = interpPoint(lead1, lead2, 1.0 - (circDiam / (2.0 * wtot)), 0); - circCent[1] = interpPoint(lead1, lead2, 1.0 - (3 * circDiam / 2.0 - circOverlap) / wtot, 0); - Point d1, d2; - d1 = interpPoint(in3p[1], in1p[1], 0.3333); - d2 = interpPoint(in3p[1], in1p[1], 0.6666); - arrowPoly1 = calcArrow(d1, d2, 8, 4); - interpPoint2(d1, d2, bar1[0], bar1[1], 1.0, 4); - d1 = interpPoint(in3p[1], in2p[1], 0.3333); - d2 = interpPoint(in3p[1], in2p[1], 0.6666); - arrowPoly2 = calcArrow(d1, d2, 8, 4); - interpPoint2(d1, d2, bar2[0], bar2[1], 1.0, 4); - plusFont = new Font("SansSerif", 0, 14); - setPost(0, in1p[0]); - setPost(1, in2p[0]); - setPost(2, in3p[0]); - setPost(3, in4p[0]); - setPost(4, point2bis); + super.setPoints(); + int ww = opwidth; + int wtot = ww * 2 + 2 * circDiam - circOverlap; + + if (dn > wtot) { + lead1 = interpPoint(point1, point2, 1.0 - wtot / dn, 0); + lead2 = point2; + point2bis = point2; + } else { + lead1 = point1; + lead2 = interpPoint(point1, point2, wtot / dn, 0); + point2bis = lead2; + } + int hs = opheight * dsign; + // if ((flags & FLAG_SWAP) != 0) + // hs = -hs; + in1p = newPointArray(2); + in2p = newPointArray(2); + in3p = newPointArray(2); + in4p = newPointArray(2); + textp = newPointArray(2); + bar1 = newPointArray(2); + bar2 = newPointArray(2); + circCent = newPointArray(2); + interpPoint2(point1, point2bis, in1p[0], in2p[0], 0, hs); + interpPoint2(lead1, lead2, in1p[1], in2p[1], 0, hs); + interpPoint2(lead1, lead2, textp[0], textp[1], .1, hs); + in3p[0] = point1; + in3p[1] = lead1; + in4p[0] = interpPoint(lead1, lead2, 1.0 - (16.0 / wtot), 32); + in4p[1] = interpPoint(lead1, lead2, 1.0 - (16.0 / wtot), 8); + // in4p[0].x=sim.snapGrid(in4p[0].x); + // in4p[0].y=sim.snapGrid(in4p[0].y); + Point tris[] = newPointArray(3); + interpPoint2(lead1, lead2, tris[0], tris[1], 0, 3 * hs / 2); + tris[2] = interpPoint(lead1, lead2, (2.0 * ww) / wtot); + triangle = createPolygon(tris[0], tris[1], tris[2]); + circCent[0] = interpPoint(lead1, lead2, 1.0 - (circDiam / (2.0 * wtot)), 0); + circCent[1] = interpPoint(lead1, lead2, 1.0 - (3 * circDiam / 2.0 - circOverlap) / wtot, 0); + Point d1, d2; + d1 = interpPoint(in3p[1], in1p[1], 0.3333); + d2 = interpPoint(in3p[1], in1p[1], 0.6666); + arrowPoly1 = calcArrow(d1, d2, 8, 4); + interpPoint2(d1, d2, bar1[0], bar1[1], 1.0, 4); + d1 = interpPoint(in3p[1], in2p[1], 0.3333); + d2 = interpPoint(in3p[1], in2p[1], 0.6666); + arrowPoly2 = calcArrow(d1, d2, 8, 4); + interpPoint2(d1, d2, bar2[0], bar2[1], 1.0, 4); + plusFont = new Font("SansSerif", 0, 14); + setPost(0, in1p[0]); + setPost(1, in2p[0]); + setPost(2, in3p[0]); + setPost(3, in4p[0]); + setPost(4, point2bis); } @Override public int getDumpType() { - return 402; + return 402; } void getInfo(String arr[]) { - arr[0] = "OTA (LM13700 style)"; - arr[1] = "Iabc = " + getCurrentText(-getCurrentIntoNode(3)); - arr[2] = "V+ - V- = " + getVoltageText(volts[0] - volts[1]); + arr[0] = "OTA (LM13700 style)"; + arr[1] = "Iabc = " + getCurrentText(-getCurrentIntoNode(3)); + arr[2] = "V+ - V- = " + getVoltageText(volts[0] - volts[1]); } public EditInfo getEditInfo(int n) { - if (n == 0) - return new EditInfo("Positive Supply Voltage (5-20V)", posVolt, 5, 20); - if (n == 1) - return new EditInfo("Negative Supply Voltage (V)", negVolt, -20, -5); - return null; + if (n == 0) + return new EditInfo("Positive Supply Voltage (5-20V)", posVolt, 5, 20); + if (n == 1) + return new EditInfo("Negative Supply Voltage (V)", negVolt, -20, -5); + return null; } public void setEditValue(int n, EditInfo ei) { - if (n == 0) - posVolt = ei.value; - if (n == 1) - negVolt = ei.value; - initOTA(); + if (n == 0) + posVolt = ei.value; + if (n == 1) + negVolt = ei.value; + initOTA(); } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/OhmMeterElm.java b/src/main/java/com/lushprojects/circuitjs1/client/OhmMeterElm.java index acd3f1d..58c5bdd 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/OhmMeterElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/OhmMeterElm.java @@ -3,62 +3,70 @@ import com.lushprojects.circuitjs1.client.util.Locale; public class OhmMeterElm extends CurrentElm { - public OhmMeterElm(int xx, int yy) { - super(xx, yy); - } - public OhmMeterElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f, st); - } - int getDumpType() { return 216; } + public OhmMeterElm(int xx, int yy) { + super(xx, yy); + } - void setPoints() { - super.setPoints(); - calcLeads(26); - } - void draw(Graphics g) { - int cr = 12; - draw2Leads(g); - setVoltageColor(g, (volts[0]+volts[1])/2); - setPowerColor(g, false); - - drawThickCircle(g, center.x, center.y, cr); - drawCenteredText(g, Locale.ohmString, center.x, center.y, true); + public OhmMeterElm(int xa, int ya, int xb, int yb, int f, + StringTokenizer st) { + super(xa, ya, xb, yb, f, st); + } - setBbox(point1, point2, cr); - doDots(g); - if (sim.showValuesCheckItem.getState() && current != 0) { - String s = getShortUnitText(getVoltageDiff()/current, Locale.ohmString); - if (dx == 0 || dy == 0) - drawValues(g, s, cr); - } - drawPosts(g); - } - double getScopeValue(int x) { - return (x == Scope.VAL_R) ? getVoltageDiff()/current : super.getScopeValue(x); - } - int getScopeUnits(int x) { - return (x == Scope.VAL_R) ? Scope.UNITS_OHMS : super.getScopeUnits(x); - } - boolean canShowValueInScope(int x) { - return x == Scope.VAL_R; - } + int getDumpType() { + return 216; + } - /* - public EditInfo getEditInfo(int n) { - if (n == 0) - return new EditInfo("Current (A)", currentValue, 0, .1); - return null; - } - public void setEditValue(int n, EditInfo ei) { - currentValue = ei.value; - } - */ - void getInfo(String arr[]) { - arr[0] = "ohmmeter"; - if (current == 0) - arr[1] = "R = \u221e"; - else - arr[1] = "R = " + getUnitText(getVoltageDiff()/current, Locale.ohmString); - } + void setPoints() { + super.setPoints(); + calcLeads(26); + } + + void draw(Graphics g) { + int cr = 12; + draw2Leads(g); + setVoltageColor(g, (volts[0] + volts[1]) / 2); + setPowerColor(g, false); + + drawThickCircle(g, center.x, center.y, cr); + drawCenteredText(g, Locale.ohmString, center.x, center.y, true); + + setBbox(point1, point2, cr); + doDots(g); + if (sim.showValuesCheckItem.getState() && current != 0) { + String s = getShortUnitText(getVoltageDiff() / current, Locale.ohmString); + if (dx == 0 || dy == 0) + drawValues(g, s, cr); + } + drawPosts(g); + } + + double getScopeValue(int x) { + return (x == Scope.VAL_R) ? getVoltageDiff() / current : super.getScopeValue(x); + } + + int getScopeUnits(int x) { + return (x == Scope.VAL_R) ? Scope.UNITS_OHMS : super.getScopeUnits(x); + } + + boolean canShowValueInScope(int x) { + return x == Scope.VAL_R; + } + + /* + public EditInfo getEditInfo(int n) { + if (n == 0) + return new EditInfo("Current (A)", currentValue, 0, .1); + return null; + } + public void setEditValue(int n, EditInfo ei) { + currentValue = ei.value; + } + */ + void getInfo(String arr[]) { + arr[0] = "ohmmeter"; + if (current == 0) + arr[1] = "R = \u221e"; + else + arr[1] = "R = " + getUnitText(getVoltageDiff() / current, Locale.ohmString); + } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/OpAmpElm.java b/src/main/java/com/lushprojects/circuitjs1/client/OpAmpElm.java index 904e00b..0a7c7fa 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/OpAmpElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/OpAmpElm.java @@ -19,197 +19,236 @@ package com.lushprojects.circuitjs1.client; - class OpAmpElm extends CircuitElm { - int opsize, opheight, opwidth, opaddtext; - double maxOut, minOut, gain, gbw; - boolean reset; - final int FLAG_SWAP = 1; - final int FLAG_SMALL = 2; - final int FLAG_LOWGAIN = 4; - final int FLAG_GAIN = 8; - public OpAmpElm(int xx, int yy) { - super(xx, yy); - noDiagonal = true; - maxOut = 15; - minOut = -15; - gbw = 1e6; - flags = FLAG_GAIN; // need to do this before setSize() - gain = 100000; - setSize(sim.smallGridCheckItem.getState() ? 1 : 2); - } - public OpAmpElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f); - maxOut = 15; - minOut = -15; - // GBW has no effect in this version of the simulator, but we - // retain it to keep the file format the same - gbw = 1e6; - try { - maxOut = new Double(st.nextToken()).doubleValue(); - minOut = new Double(st.nextToken()).doubleValue(); - gbw = new Double(st.nextToken()).doubleValue(); - volts[0] = new Double(st.nextToken()).doubleValue(); - volts[1] = new Double(st.nextToken()).doubleValue(); - gain = new Double(st.nextToken()).doubleValue(); - } catch (Exception e) { - } - noDiagonal = true; - setSize((f & FLAG_SMALL) != 0 ? 1 : 2); - setGain(); - } - void setGain() { - if ((flags & FLAG_GAIN) != 0) - return; - - // gain of 100000 breaks e-amp-dfdx.txt - // gain was 1000, but it broke amp-schmitt.txt - gain = ((flags & FLAG_LOWGAIN) != 0) ? 1000 : 100000; - } - String dump() { - flags |= FLAG_GAIN; - return super.dump() + " " + maxOut + " " + minOut + " " + gbw + " " + volts[0] + " " + volts[1] + " " + gain; - } - boolean nonLinear() { return true; } - void draw(Graphics g) { - setBbox(point1, point2, opheight*2); - setVoltageColor(g, volts[0]); - drawThickLine(g, in1p[0], in1p[1]); - setVoltageColor(g, volts[1]); - drawThickLine(g, in2p[0], in2p[1]); - setVoltageColor(g, volts[2]); - drawThickLine(g, lead2, point2); - g.setColor(needsHighlight() ? selectColor : lightGrayColor); - setPowerColor(g, true); - drawThickPolygon(g, triangle); - g.setFont(plusFont); - drawCenteredText(g, "-", textp[0].x, textp[0].y-2, true); - drawCenteredText(g, "+", textp[1].x, textp[1].y , true); - curcount = updateDotCount(current, curcount); - drawDots(g, point2, lead2, curcount); - drawPosts(g); - } - double getPower() { return volts[2]*current; } - Point in1p[], in2p[], textp[]; - Polygon triangle; - Font plusFont; - void setSize(int s) { - opsize = s; - opheight = 8*s; - opwidth = 13*s; - flags = (flags & ~FLAG_SMALL) | ((s == 1) ? FLAG_SMALL : 0); - } - void setPoints() { - super.setPoints(); - if (dn > 150 && this == sim.dragElm) - setSize(2); - int ww = opwidth; - if (ww > dn/2) - ww = (int) (dn/2); - calcLeads(ww*2); - int hs = opheight*dsign; - if ((flags & FLAG_SWAP) != 0) - hs = -hs; - in1p = newPointArray(2); - in2p = newPointArray(2); - textp = newPointArray(2); - interpPoint2(point1, point2, in1p[0], in2p[0], 0, hs); - interpPoint2(lead1 , lead2, in1p[1], in2p[1], 0, hs); - interpPoint2(lead1 , lead2, textp[0], textp[1], .2, hs); - Point tris[] = newPointArray(2); - interpPoint2(lead1, lead2, tris[0], tris[1], 0, hs*2); - triangle = createPolygon(tris[0], tris[1], lead2); - plusFont = new Font("SansSerif", 0, opsize == 2 ? 14 : 10); - } - int getPostCount() { return 3; } - Point getPost(int n) { - return (n == 0) ? in1p[0] : (n == 1) ? in2p[0] : point2; - } - int getVoltageSourceCount() { return 1; } - void getInfo(String arr[]) { - arr[0] = "op-amp"; - arr[1] = "V+ = " + getVoltageText(volts[1]); - arr[2] = "V- = " + getVoltageText(volts[0]); - // sometimes the voltage goes slightly outside range, to make - // convergence easier. so we hide that here. - double vo = Math.max(Math.min(volts[2], maxOut), minOut); - arr[3] = "Vout = " + getVoltageText(vo); - arr[4] = "Iout = " + getCurrentText(-current); - arr[5] = "range = " + getVoltageText(minOut) + " to " + - getVoltageText(maxOut); - } - - double lastvd; - - void stamp() { - int vn = sim.nodeList.size()+voltSource; - sim.stampNonLinear(vn); - sim.stampMatrix(nodes[2], vn, 1); - } - void doStep() { - double vd = volts[1] - volts[0]; - double midpoint = (maxOut+minOut)*.5; - if (Math.abs(lastvd-vd) > .1) - sim.converged = false; - else if (volts[2] > maxOut+.1 || volts[2] < minOut-.1) - sim.converged = false; - double x = 0; - int vn = sim.nodeList.size()+voltSource; - double dx = 0; - double maxAdj = maxOut-midpoint; - double minAdj = minOut-midpoint; - if (vd >= maxAdj/gain && (lastvd >= 0 || sim.getrand(4) == 1)) { - dx = 1e-4; - x = maxOut - dx*maxAdj/gain; - } else if (vd <= minAdj/gain && (lastvd <= 0 || sim.getrand(4) == 1)) { - dx = 1e-4; - x = minOut - dx*minAdj/gain; - } else { - dx = gain; - x = midpoint; - } - //System.out.println("opamp " + vd + " " + volts[2] + " " + dx + " " + x + " " + lastvd + " " + sim.converged); - - // newton-raphson - sim.stampMatrix(vn, nodes[0], dx); - sim.stampMatrix(vn, nodes[1], -dx); - sim.stampMatrix(vn, nodes[2], 1); - sim.stampRightSide(vn, x); - - lastvd = vd; +public class OpAmpElm extends CircuitElm { + int opsize, opheight, opwidth, opaddtext; + double maxOut, minOut, gain, gbw; + boolean reset; + final int FLAG_SWAP = 1; + final int FLAG_SMALL = 2; + final int FLAG_LOWGAIN = 4; + final int FLAG_GAIN = 8; + + public OpAmpElm(int xx, int yy) { + super(xx, yy); + noDiagonal = true; + maxOut = 15; + minOut = -15; + gbw = 1e6; + flags = FLAG_GAIN; // need to do this before setSize() + gain = 100000; + setSize(sim.smallGridCheckItem.getState() ? 1 : 2); + } + + public OpAmpElm(int xa, int ya, int xb, int yb, int f, + StringTokenizer st) { + super(xa, ya, xb, yb, f); + maxOut = 15; + minOut = -15; + // GBW has no effect in this version of the simulator, but we + // retain it to keep the file format the same + gbw = 1e6; + try { + maxOut = new Double(st.nextToken()).doubleValue(); + minOut = new Double(st.nextToken()).doubleValue(); + gbw = new Double(st.nextToken()).doubleValue(); + volts[0] = new Double(st.nextToken()).doubleValue(); + volts[1] = new Double(st.nextToken()).doubleValue(); + gain = new Double(st.nextToken()).doubleValue(); + } catch (Exception e) { + } + noDiagonal = true; + setSize((f & FLAG_SMALL) != 0 ? 1 : 2); + setGain(); + } + + void setGain() { + if ((flags & FLAG_GAIN) != 0) + return; + + // gain of 100000 breaks e-amp-dfdx.txt + // gain was 1000, but it broke amp-schmitt.txt + gain = ((flags & FLAG_LOWGAIN) != 0) ? 1000 : 100000; + } + + String dump() { + flags |= FLAG_GAIN; + return super.dump() + " " + maxOut + " " + minOut + " " + gbw + " " + volts[0] + " " + volts[1] + " " + gain; + } + + boolean nonLinear() { + return true; + } + + void draw(Graphics g) { + setBbox(point1, point2, opheight * 2); + setVoltageColor(g, volts[0]); + drawThickLine(g, in1p[0], in1p[1]); + setVoltageColor(g, volts[1]); + drawThickLine(g, in2p[0], in2p[1]); + setVoltageColor(g, volts[2]); + drawThickLine(g, lead2, point2); + g.setColor(needsHighlight() ? selectColor : lightGrayColor); + setPowerColor(g, true); + drawThickPolygon(g, triangle); + g.setFont(plusFont); + drawCenteredText(g, "-", textp[0].x, textp[0].y - 2, true); + drawCenteredText(g, "+", textp[1].x, textp[1].y, true); + curcount = updateDotCount(current, curcount); + drawDots(g, point2, lead2, curcount); + drawPosts(g); + } + + double getPower() { + return volts[2] * current; + } + + Point in1p[], in2p[], textp[]; + Polygon triangle; + Font plusFont; + + void setSize(int s) { + opsize = s; + opheight = 8 * s; + opwidth = 13 * s; + flags = (flags & ~FLAG_SMALL) | ((s == 1) ? FLAG_SMALL : 0); + } + + void setPoints() { + super.setPoints(); + if (dn > 150 && this == sim.dragElm) + setSize(2); + int ww = opwidth; + if (ww > dn / 2) + ww = (int) (dn / 2); + calcLeads(ww * 2); + int hs = opheight * dsign; + if ((flags & FLAG_SWAP) != 0) + hs = -hs; + in1p = newPointArray(2); + in2p = newPointArray(2); + textp = newPointArray(2); + interpPoint2(point1, point2, in1p[0], in2p[0], 0, hs); + interpPoint2(lead1, lead2, in1p[1], in2p[1], 0, hs); + interpPoint2(lead1, lead2, textp[0], textp[1], .2, hs); + Point tris[] = newPointArray(2); + interpPoint2(lead1, lead2, tris[0], tris[1], 0, hs * 2); + triangle = createPolygon(tris[0], tris[1], lead2); + plusFont = new Font("SansSerif", 0, opsize == 2 ? 14 : 10); + } + + int getPostCount() { + return 3; + } + + Point getPost(int n) { + return (n == 0) ? in1p[0] : (n == 1) ? in2p[0] : point2; + } + + int getVoltageSourceCount() { + return 1; + } + + void getInfo(String arr[]) { + arr[0] = "op-amp"; + arr[1] = "V+ = " + getVoltageText(volts[1]); + arr[2] = "V- = " + getVoltageText(volts[0]); + // sometimes the voltage goes slightly outside range, to make + // convergence easier. so we hide that here. + double vo = Math.max(Math.min(volts[2], maxOut), minOut); + arr[3] = "Vout = " + getVoltageText(vo); + arr[4] = "Iout = " + getCurrentText(-current); + arr[5] = "range = " + getVoltageText(minOut) + " to " + + getVoltageText(maxOut); + } + + double lastvd; + + void stamp() { + int vn = sim.nodeList.size() + voltSource; + sim.stampNonLinear(vn); + sim.stampMatrix(nodes[2], vn, 1); + } + + void doStep() { + double vd = volts[1] - volts[0]; + double midpoint = (maxOut + minOut) * .5; + if (Math.abs(lastvd - vd) > .1) + sim.converged = false; + else if (volts[2] > maxOut + .1 || volts[2] < minOut - .1) + sim.converged = false; + double x = 0; + int vn = sim.nodeList.size() + voltSource; + double dx = 0; + double maxAdj = maxOut - midpoint; + double minAdj = minOut - midpoint; + if (vd >= maxAdj / gain && (lastvd >= 0 || sim.getrand(4) == 1)) { + dx = 1e-4; + x = maxOut - dx * maxAdj / gain; + } else if (vd <= minAdj / gain && (lastvd <= 0 || sim.getrand(4) == 1)) { + dx = 1e-4; + x = minOut - dx * minAdj / gain; + } else { + dx = gain; + x = midpoint; + } + //System.out.println("opamp " + vd + " " + volts[2] + " " + dx + " " + x + " " + lastvd + " " + sim.converged); + + // newton-raphson + sim.stampMatrix(vn, nodes[0], dx); + sim.stampMatrix(vn, nodes[1], -dx); + sim.stampMatrix(vn, nodes[2], 1); + sim.stampRightSide(vn, x); + + lastvd = vd; /*if (sim.converged) System.out.println((volts[1]-volts[0]) + " " + volts[2] + " " + initvd);*/ - } - // there is no current path through the op-amp inputs, but there - // is an indirect path through the output to ground. - boolean getConnection(int n1, int n2) { return false; } - boolean hasGroundConnection(int n1) { - return (n1 == 2); - } - double getVoltageDiff() { return volts[2] - volts[1]; } - int getDumpType() { return 'a'; } - public EditInfo getEditInfo(int n) { - if (n == 0) - return new EditInfo("Max Output (V)", maxOut, 1, 20); - if (n == 1) - return new EditInfo("Min Output (V)", minOut, -20, 0); - if (n == 2) - return new EditInfo("Gain", gain, 10, 1000000); - return null; - } - public void setEditValue(int n, EditInfo ei) { - if (n == 0) - maxOut = ei.value; - if (n == 1) - minOut = ei.value; - if (n == 2 && ei.value > 0) - gain = ei.value; - } - int getShortcut() { return 'a'; } - - @Override double getCurrentIntoNode(int n) { - if (n==2) - return -current; - return 0; - } } + + // there is no current path through the op-amp inputs, but there + // is an indirect path through the output to ground. + boolean getConnection(int n1, int n2) { + return false; + } + + boolean hasGroundConnection(int n1) { + return (n1 == 2); + } + + double getVoltageDiff() { + return volts[2] - volts[1]; + } + + int getDumpType() { + return 'a'; + } + + public EditInfo getEditInfo(int n) { + if (n == 0) + return new EditInfo("Max Output (V)", maxOut, 1, 20); + if (n == 1) + return new EditInfo("Min Output (V)", minOut, -20, 0); + if (n == 2) + return new EditInfo("Gain", gain, 10, 1000000); + return null; + } + + public void setEditValue(int n, EditInfo ei) { + if (n == 0) + maxOut = ei.value; + if (n == 1) + minOut = ei.value; + if (n == 2 && ei.value > 0) + gain = ei.value; + } + + int getShortcut() { + return 'a'; + } + + @Override + double getCurrentIntoNode(int n) { + if (n == 2) + return -current; + return 0; + } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/OpAmpRealElm.java b/src/main/java/com/lushprojects/circuitjs1/client/OpAmpRealElm.java index 7549e54..263280c 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/OpAmpRealElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/OpAmpRealElm.java @@ -4,52 +4,52 @@ public class OpAmpRealElm extends CompositeElm { // from https://commons.wikimedia.org/wiki/File:OpAmpTransistorLevel_Colored_Labeled.svg private static String model741String = - "NTransistorElm 3 8 9\rNTransistorElm 2 8 10\rPTransistorElm 11 12 9\rPTransistorElm 11 13 10\rNTransistorElm 14 12 1\r" + // Q1-5 - "NTransistorElm 14 13 5\rNTransistorElm 12 7 14\rPTransistorElm 8 8 7\rPTransistorElm 8 11 7\rNTransistorElm 17 11 16\r" + // Q6-10 - "NTransistorElm 17 17 4\rPTransistorElm 18 18 7\rPTransistorElm 18 20 7\rNTransistorElm 20 7 25\rNTransistorElm 13 22 24\r" + // Q11-15 - "NTransistorElm 21 20 22\rNTransistorElm 25 20 6\rNTransistorElm 24 22 23\rPTransistorElm 22 4 15\rNTransistorElm 23 13 4\r" + // Q16-22 (no Q18, Q21) - "CapacitorElm 13 20\r" + - "ResistorElm 15 6\rResistorElm 6 25\r" + // output resistors - "ResistorElm 4 1\rResistorElm 4 14\rResistorElm 4 5\rResistorElm 4 16\rResistorElm 4 24\rResistorElm 4 23\rResistorElm 17 18\r" + - "ResistorElm 22 21\rResistorElm 21 20\r"; - private static int[] model741ExternalNodes = { 2, 3, 6, 7, 4 }; // , 1, 5 }; + "NTransistorElm 3 8 9\rNTransistorElm 2 8 10\rPTransistorElm 11 12 9\rPTransistorElm 11 13 10\rNTransistorElm 14 12 1\r" + // Q1-5 + "NTransistorElm 14 13 5\rNTransistorElm 12 7 14\rPTransistorElm 8 8 7\rPTransistorElm 8 11 7\rNTransistorElm 17 11 16\r" + // Q6-10 + "NTransistorElm 17 17 4\rPTransistorElm 18 18 7\rPTransistorElm 18 20 7\rNTransistorElm 20 7 25\rNTransistorElm 13 22 24\r" + // Q11-15 + "NTransistorElm 21 20 22\rNTransistorElm 25 20 6\rNTransistorElm 24 22 23\rPTransistorElm 22 4 15\rNTransistorElm 23 13 4\r" + // Q16-22 (no Q18, Q21) + "CapacitorElm 13 20\r" + + "ResistorElm 15 6\rResistorElm 6 25\r" + // output resistors + "ResistorElm 4 1\rResistorElm 4 14\rResistorElm 4 5\rResistorElm 4 16\rResistorElm 4 24\rResistorElm 4 23\rResistorElm 17 18\r" + + "ResistorElm 22 21\rResistorElm 21 20\r"; + private static int[] model741ExternalNodes = {2, 3, 6, 7, 4}; // , 1, 5 }; // 0 = input -, 1 = input +, 2 = output, 3 = V+, 4 = V-, 5, 6 = offset null - + private static String lm324ModelString = - "TransistorElm 1 2 3\rCurrentElm 4 3\rTransistorElm 2 2 5\rTransistorElm 2 6 5\rCapacitorElm 6 7\rCurrentElm 4 8\rCurrentElm 4 7\rTransistorElm 8 4 9\r" + - "TransistorElm 7 4 10\rTransistorElm 10 4 11\rTransistorElm 11 7 12\rResistorElm 11 12\rTransistorElm 7 5 12\rCurrentElm 12 5\rTransistorElm 6 5 8\r" + - "ResistorElm 9 5\rTransistorElm 9 7 5\rTransistorElm 13 6 3"; - private static int[] lm324ExternalNodes = { 1, 13, 12, 4, 5 }; + "TransistorElm 1 2 3\rCurrentElm 4 3\rTransistorElm 2 2 5\rTransistorElm 2 6 5\rCapacitorElm 6 7\rCurrentElm 4 8\rCurrentElm 4 7\rTransistorElm 8 4 9\r" + + "TransistorElm 7 4 10\rTransistorElm 10 4 11\rTransistorElm 11 7 12\rResistorElm 11 12\rTransistorElm 7 5 12\rCurrentElm 12 5\rTransistorElm 6 5 8\r" + + "ResistorElm 9 5\rTransistorElm 9 7 5\rTransistorElm 13 6 3"; + private static int[] lm324ExternalNodes = {1, 13, 12, 4, 5}; private static String lm324ModelDump = - "0 -1 -0 0 10000/0 0.000006/0 1 0 0 100/0 1 0 0 100/0 1e-11 0/0 0.000004/0 0.0001/0 1 0 0 100/0 1 0 0 100/0 1 0 0 100/0 1 0 0 100/0 25/0 -1 0 0 100/0 0.00005/" + - "0 -1 0 0 100/0 10000/0 1 0 0 100/0 -1 0 0 10000"; - + "0 -1 -0 0 10000/0 0.000006/0 1 0 0 100/0 1 0 0 100/0 1e-11 0/0 0.000004/0 0.0001/0 1 0 0 100/0 1 0 0 100/0 1 0 0 100/0 1 0 0 100/0 25/0 -1 0 0 100/0 0.00005/" + + "0 -1 0 0 100/0 10000/0 1 0 0 100/0 -1 0 0 10000"; + // from LM324 spice model, ON SEMICONDUCTOR NEXT GEN MODEL 9/27/2018 private static String lm324v2ModelString = - "ResistorElm 4 6\rCurrentElm 4 7\rResistorElm 4 29\rResistorElm 8 30\rResistorElm 9 31\rTransistorElm 30 29 31 \rResistorElm 4 32\rResistorElm 2 33\rResistorElm 10 34\r" + - "TransistorElm 33 32 34 \rResistorElm 9 35\rResistorElm 9 36\rResistorElm 11 37\rTransistorElm 36 35 37 \rResistorElm 10 38\rResistorElm 10 39\rResistorElm 11 40\r" + - "TransistorElm 39 38 40 \rResistorElm 12 41\rTransistorElm 13 41 4 \rResistorElm 13 42\rTransistorElm 13 42 4 \rResistorElm 4 43\rTransistorElm 12 43 14 \rResistorElm 3 44\r" + - "TransistorElm 14 44 6 \rResistorElm 15 45\rTransistorElm 6 45 4 \rResistorElm 3 46\rTransistorElm 15 46 16 \rResistorElm 3 47\rTransistorElm 16 47 17 \rResistorElm 17 16\r" + - "ResistorElm 5 17\rResistorElm 4 48\rTransistorElm 15 48 5 \rResistorElm 15 49\rTransistorElm 17 49 5 \rCurrentElm 18 3\rCurrentElm 19 3\rCurrentElm 20 3\rResistorElm 11 50\r" + - "TransistorElm 18 50 3 \rResistorElm 14 51\rTransistorElm 19 51 3 \rResistorElm 5 52\rTransistorElm 7 52 4 \rResistorElm 15 53\rTransistorElm 20 53 3 \rCapacitorElm 21 22\r" + - "ResistorElm 12 21\rResistorElm 12 15\rVCVSElm 3 0 23 8\rVoltageElm 23 1\rCurrentElm 3 4\rResistorElm 4 3\rResistorElm 12 54\rTransistorElm 9 54 11 \rResistorElm 13 55\r" + - "TransistorElm 10 55 11 \rCapacitorElm 12 13\rCapacitorElm 6 15\rCapacitorElm 3 24\rResistorElm 11 24\rCapacitorElm 1 2\rCapacitorElm 2 0\rCapacitorElm 1 0\r" + - "VCVSElm 15 0 22 0\rCapacitorElm 5 0\rResistorElm 25 56\rTransistorElm 25 56 0 \rVCCSElm 27 0 4 3\rCurrentElm 0 25\rVoltageElm 25 26\rResistorElm 0 26\r" + - "VCVSElm 28 26 27 0\rResistorElm 0 27\rVoltageElm 28 0\rResistorElm 0 28"; - private static int[] lm324v2ExternalNodes = { 2, 1, 5, 3, 4 }; + "ResistorElm 4 6\rCurrentElm 4 7\rResistorElm 4 29\rResistorElm 8 30\rResistorElm 9 31\rTransistorElm 30 29 31 \rResistorElm 4 32\rResistorElm 2 33\rResistorElm 10 34\r" + + "TransistorElm 33 32 34 \rResistorElm 9 35\rResistorElm 9 36\rResistorElm 11 37\rTransistorElm 36 35 37 \rResistorElm 10 38\rResistorElm 10 39\rResistorElm 11 40\r" + + "TransistorElm 39 38 40 \rResistorElm 12 41\rTransistorElm 13 41 4 \rResistorElm 13 42\rTransistorElm 13 42 4 \rResistorElm 4 43\rTransistorElm 12 43 14 \rResistorElm 3 44\r" + + "TransistorElm 14 44 6 \rResistorElm 15 45\rTransistorElm 6 45 4 \rResistorElm 3 46\rTransistorElm 15 46 16 \rResistorElm 3 47\rTransistorElm 16 47 17 \rResistorElm 17 16\r" + + "ResistorElm 5 17\rResistorElm 4 48\rTransistorElm 15 48 5 \rResistorElm 15 49\rTransistorElm 17 49 5 \rCurrentElm 18 3\rCurrentElm 19 3\rCurrentElm 20 3\rResistorElm 11 50\r" + + "TransistorElm 18 50 3 \rResistorElm 14 51\rTransistorElm 19 51 3 \rResistorElm 5 52\rTransistorElm 7 52 4 \rResistorElm 15 53\rTransistorElm 20 53 3 \rCapacitorElm 21 22\r" + + "ResistorElm 12 21\rResistorElm 12 15\rVCVSElm 3 0 23 8\rVoltageElm 23 1\rCurrentElm 3 4\rResistorElm 4 3\rResistorElm 12 54\rTransistorElm 9 54 11 \rResistorElm 13 55\r" + + "TransistorElm 10 55 11 \rCapacitorElm 12 13\rCapacitorElm 6 15\rCapacitorElm 3 24\rResistorElm 11 24\rCapacitorElm 1 2\rCapacitorElm 2 0\rCapacitorElm 1 0\r" + + "VCVSElm 15 0 22 0\rCapacitorElm 5 0\rResistorElm 25 56\rTransistorElm 25 56 0 \rVCCSElm 27 0 4 3\rCurrentElm 0 25\rVoltageElm 25 26\rResistorElm 0 26\r" + + "VCVSElm 28 26 27 0\rResistorElm 0 27\rVoltageElm 28 0\rResistorElm 0 28"; + private static int[] lm324v2ExternalNodes = {2, 1, 5, 3, 4}; private static String lm324v2ModelDump = - "0 40000/0 5e-7/0 380/0 1700/0 5/0 -1 0 0 306 xlm324v2-qpi/0 380/0 1700/0 5/0 -1 0 0 300 xlm324v2-qpa/0 380/0 1700/0 5/0 -1 0 0 306 xlm324v2-qpi/0 380/0 1700/0 5/" + - "0 -1 0 0 306 xlm324v2-qpi/0 25/0 1 0 0 100 xlm324v2-qnq/0 25/0 1 0 0 100 xlm324v2-qnq/0 300/0 -1 0 0 100 xlm324v2-qpq/0 25/0 1 0 0 100 xlm324v2-qnq/0 25/0 1 0 0 100 xlm324v2-qnq/" + - "0 25/0 1 0 0 100 xlm324v2-qnq/0 25/0 1 0 0 100 xlm324v2-qnq/0 40000/0 18/0 300/0 -1 0 0 100 xlm324v2-qpq/0 25/0 1 0 0 100 xlm324v2-qnq/0 1.2e-7/0 6e-8/0 0.000001/0 300/" + - "0 -1 0 0 100 xlm324v2-qpq/0 300/0 -1 0 0 100 xlm324v2-qpq/0 25/0 1 0 0 100 xlm324v2-qnq/0 300/0 -1 0 0 100 xlm324v2-qpq/2 4.8e-12 0 0/0 3/0 3000000000/0 2 -0.00001*(a-b)/" + - "0 0 0 -0.00156/0 0.000005/0 450000/0 300/0 -1 0 0 100 xlm324v2-qpq/0 300/0 -1 0 0 100 xlm324v2-qpq/2 8e-12 0 0/2 1e-12 0 0/2 1e-13 0 0/0 300000/2 2.3e-13 0 0/2 7.9e-13 0 0/" + - "2 7.9e-13 0 0/0 2 2*(a-b)/2 5e-14 0 0/0 25/0 1 0 0 100 xlm324v2-qnq/0 2 0.0003*(a-b)/0 0.001/0 0 0 -0.25/0 1000000/0 2 1*(a-b)/0 1000000/0 0 0 -0.55/0 1000000"; + "0 40000/0 5e-7/0 380/0 1700/0 5/0 -1 0 0 306 xlm324v2-qpi/0 380/0 1700/0 5/0 -1 0 0 300 xlm324v2-qpa/0 380/0 1700/0 5/0 -1 0 0 306 xlm324v2-qpi/0 380/0 1700/0 5/" + + "0 -1 0 0 306 xlm324v2-qpi/0 25/0 1 0 0 100 xlm324v2-qnq/0 25/0 1 0 0 100 xlm324v2-qnq/0 300/0 -1 0 0 100 xlm324v2-qpq/0 25/0 1 0 0 100 xlm324v2-qnq/0 25/0 1 0 0 100 xlm324v2-qnq/" + + "0 25/0 1 0 0 100 xlm324v2-qnq/0 25/0 1 0 0 100 xlm324v2-qnq/0 40000/0 18/0 300/0 -1 0 0 100 xlm324v2-qpq/0 25/0 1 0 0 100 xlm324v2-qnq/0 1.2e-7/0 6e-8/0 0.000001/0 300/" + + "0 -1 0 0 100 xlm324v2-qpq/0 300/0 -1 0 0 100 xlm324v2-qpq/0 25/0 1 0 0 100 xlm324v2-qnq/0 300/0 -1 0 0 100 xlm324v2-qpq/2 4.8e-12 0 0/0 3/0 3000000000/0 2 -0.00001*(a-b)/" + + "0 0 0 -0.00156/0 0.000005/0 450000/0 300/0 -1 0 0 100 xlm324v2-qpq/0 300/0 -1 0 0 100 xlm324v2-qpq/2 8e-12 0 0/2 1e-12 0 0/2 1e-13 0 0/0 300000/2 2.3e-13 0 0/2 7.9e-13 0 0/" + + "2 7.9e-13 0 0/0 2 2*(a-b)/2 5e-14 0 0/0 25/0 1 0 0 100 xlm324v2-qnq/0 2 0.0003*(a-b)/0 0.001/0 0 0 -0.25/0 1000000/0 2 1*(a-b)/0 1000000/0 0 0 -0.55/0 1000000"; static final int MODEL_741 = 0; static final int MODEL_324 = 1; static final int MODEL_324v2 = 2; - - private static double[] model741resistances = { 50, 25, 1e3, 50e3, 1e3, 5e3, 50e3, 50, 39e3, 7500, 4500 }; + + private static double[] model741resistances = {50, 25, 1e3, 50e3, 1e3, 5e3, 50e3, 50, 39e3, 7500, 4500}; int modelType; final int opheight = 16; @@ -62,105 +62,112 @@ public class OpAmpRealElm extends CompositeElm { final int FLAG_SWAP = 2; public OpAmpRealElm(int xx, int yy) { - super(xx, yy); // , model741String, model741ExternalNodes); - noDiagonal = true; - slewRate = .6; - currentLimit = defaultCurrentLimit; - modelType = MODEL_741; - initModel(); + super(xx, yy); // , model741String, model741ExternalNodes); + noDiagonal = true; + slewRate = .6; + currentLimit = defaultCurrentLimit; + modelType = MODEL_741; + initModel(); } public OpAmpRealElm(int xa, int ya, int xb, int yb, int f, StringTokenizer st) { - super(xa, ya, xb, yb, f); // , null, model741String, model741ExternalNodes); - noDiagonal = true; - slewRate = Double.parseDouble(st.nextToken()); - capValue = Double.parseDouble(st.nextToken()); - currentLimit = defaultCurrentLimit; - modelType = MODEL_741; - try { - currentLimit = Double.parseDouble(st.nextToken()); - modelType = Integer.parseInt(st.nextToken()); - } catch (Exception e) {} - initModel(); + super(xa, ya, xb, yb, f); // , null, model741String, model741ExternalNodes); + noDiagonal = true; + slewRate = Double.parseDouble(st.nextToken()); + capValue = Double.parseDouble(st.nextToken()); + currentLimit = defaultCurrentLimit; + modelType = MODEL_741; + try { + currentLimit = Double.parseDouble(st.nextToken()); + modelType = Integer.parseInt(st.nextToken()); + } catch (Exception e) { + } + initModel(); } private void initModel() { - flags |= FLAG_ESCAPE; - switch (modelType) { - case MODEL_741: init741(); break; - case MODEL_324: init324(); break; - case MODEL_324v2: init324v2(); break; - } - curCounts = new double[5]; - setPoints(); + flags |= FLAG_ESCAPE; + switch (modelType) { + case MODEL_741: + init741(); + break; + case MODEL_324: + init324(); + break; + case MODEL_324v2: + init324v2(); + break; + } + curCounts = new double[5]; + setPoints(); } - + private void init741() { - loadComposite(null, model741String, model741ExternalNodes); - - // adjust capacitor value to get desired slew rate - getCapacitor().capacitance = 30e-12 / (slewRate/.6); - getCapacitor().voltdiff = capValue; - - // set resistor values - int i; - for (i = 0; i != 11; i++) - ((ResistorElm) compElmList.get(21+i)).resistance = model741resistances[i]; - - // adjust output stage resistor values and transistor betas to increase current if desired - double currentMult = currentLimit / defaultCurrentLimit; - ((ResistorElm) compElmList.get(21)).resistance /= currentMult; - ((ResistorElm) compElmList.get(22)).resistance /= currentMult; - ((TransistorElm) compElmList.get(13)).setBeta(currentMult * 100); // Q14 - ((TransistorElm) compElmList.get(18)).setBeta(currentMult * 100); // Q20 - + loadComposite(null, model741String, model741ExternalNodes); + + // adjust capacitor value to get desired slew rate + getCapacitor().capacitance = 30e-12 / (slewRate / .6); + getCapacitor().voltdiff = capValue; + + // set resistor values + int i; + for (i = 0; i != 11; i++) + ((ResistorElm) compElmList.get(21 + i)).resistance = model741resistances[i]; + + // adjust output stage resistor values and transistor betas to increase current if desired + double currentMult = currentLimit / defaultCurrentLimit; + ((ResistorElm) compElmList.get(21)).resistance /= currentMult; + ((ResistorElm) compElmList.get(22)).resistance /= currentMult; + ((TransistorElm) compElmList.get(13)).setBeta(currentMult * 100); // Q14 + ((TransistorElm) compElmList.get(18)).setBeta(currentMult * 100); // Q20 + } private void init324() { - StringTokenizer st = new StringTokenizer(lm324ModelDump, "/"); - loadComposite(st, lm324ModelString, lm324ExternalNodes); - - // adjust capacitor value to get desired slew rate - getCapacitor().capacitance = 10e-12 / (slewRate/.55); - getCapacitor().voltdiff = capValue; - - // adjust output stage resistor values and transistor betas to increase current if desired - double currentMult = currentLimit / defaultCurrentLimit; - ((ResistorElm) compElmList.get(11)).resistance /= currentMult; - ((TransistorElm) compElmList.get(9)).setBeta(currentMult * 100); - ((TransistorElm) compElmList.get(10)).setBeta(currentMult * 100); - ((TransistorElm) compElmList.get(12)).setBeta(currentMult * 100); - ((TransistorElm) compElmList.get(16)).setBeta(currentMult * 100); + StringTokenizer st = new StringTokenizer(lm324ModelDump, "/"); + loadComposite(st, lm324ModelString, lm324ExternalNodes); + + // adjust capacitor value to get desired slew rate + getCapacitor().capacitance = 10e-12 / (slewRate / .55); + getCapacitor().voltdiff = capValue; + + // adjust output stage resistor values and transistor betas to increase current if desired + double currentMult = currentLimit / defaultCurrentLimit; + ((ResistorElm) compElmList.get(11)).resistance /= currentMult; + ((TransistorElm) compElmList.get(9)).setBeta(currentMult * 100); + ((TransistorElm) compElmList.get(10)).setBeta(currentMult * 100); + ((TransistorElm) compElmList.get(12)).setBeta(currentMult * 100); + ((TransistorElm) compElmList.get(16)).setBeta(currentMult * 100); } - + private void init324v2() { - StringTokenizer st = new StringTokenizer(lm324v2ModelDump, "/"); - loadComposite(st, lm324v2ModelString, lm324v2ExternalNodes); + StringTokenizer st = new StringTokenizer(lm324v2ModelDump, "/"); + loadComposite(st, lm324v2ModelString, lm324v2ExternalNodes); } - + public void reset() { - super.reset(); - curCounts = new double[5]; + super.reset(); + curCounts = new double[5]; } CapacitorElm getCapacitor() { - if (modelType == MODEL_324v2) - return null; - return ((CapacitorElm) compElmList.get(modelType == MODEL_741 ? 20 : 4)); + if (modelType == MODEL_324v2) + return null; + return ((CapacitorElm) compElmList.get(modelType == MODEL_741 ? 20 : 4)); } - + public String dump() { - CapacitorElm elm = getCapacitor(); - double voltdiff = (elm == null) ? 0 : elm.voltdiff; - return super.dumpWithMask(0) + " " + slewRate + " " + voltdiff + " " + currentLimit + " " + modelType; + CapacitorElm elm = getCapacitor(); + double voltdiff = (elm == null) ? 0 : elm.voltdiff; + return super.dumpWithMask(0) + " " + slewRate + " " + voltdiff + " " + currentLimit + " " + modelType; } - + public boolean getConnection(int n1, int n2) { - return true; + return true; } void draw(Graphics g) { - setBbox(point1, point2, opheight*2); + setBbox(point1, point2, opheight * 2); setVoltageColor(g, volts[0]); drawThickLine(g, in1p[0], in1p[1]); setVoltageColor(g, volts[1]); @@ -175,20 +182,20 @@ void draw(Graphics g) { setPowerColor(g, true); drawThickPolygon(g, triangle); g.setFont(plusFont); - drawCenteredText(g, "-", textp[0].x, textp[0].y-2, true); - drawCenteredText(g, "+", textp[1].x, textp[1].y , true); + drawCenteredText(g, "-", textp[0].x, textp[0].y - 2, true); + drawCenteredText(g, "+", textp[1].x, textp[1].y, true); int i; for (i = 0; i != 5; i++) curCounts[i] = updateDotCount(getCurrentIntoNode(i), curCounts[i]); drawDots(g, in1p[1], in1p[0], curCounts[0]); drawDots(g, in2p[1], in2p[0], curCounts[1]); - drawDots(g, lead2, point2, curCounts[2]); + drawDots(g, lead2, point2, curCounts[2]); // these two segments may not be an event multiple of gridSize so we draw them the other way so the dots line up - drawDots(g, rail1p[0], rail1p[1], -curCounts[3]); - drawDots(g, rail2p[0], rail2p[1], -curCounts[4]); + drawDots(g, rail1p[0], rail1p[1], -curCounts[3]); + drawDots(g, rail2p[0], rail2p[1], -curCounts[4]); drawPosts(g); } - + Point in1p[], in2p[], textp[], rail1p[], rail2p[]; Polygon triangle; Font plusFont; @@ -196,10 +203,10 @@ void draw(Graphics g) { void setPoints() { super.setPoints(); int ww = opwidth; - if (ww > dn/2) - ww = (int) (dn/2); - calcLeads(ww*2); - int hs = opheight*dsign; + if (ww > dn / 2) + ww = (int) (dn / 2); + calcLeads(ww * 2); + int hs = opheight * dsign; int hsswap = hs; if ((flags & FLAG_SWAP) != 0) hsswap = -hsswap; @@ -208,17 +215,17 @@ void setPoints() { textp = newPointArray(2); rail1p = newPointArray(2); rail2p = newPointArray(2); - interpPoint2(point1, point2, in1p[0], in2p[0], 0, hsswap); - interpPoint2(lead1 , lead2, in1p[1], in2p[1], 0, hsswap); - interpPoint2(lead1 , lead2, textp[0], textp[1], .2, hsswap); - + interpPoint2(point1, point2, in1p[0], in2p[0], 0, hsswap); + interpPoint2(lead1, lead2, in1p[1], in2p[1], 0, hsswap); + interpPoint2(lead1, lead2, textp[0], textp[1], .2, hsswap); + // position rails; ideally in middle, but may need to be off-center to fit grid - double railPos = .5 - ((dn/2) % sim.gridSize)/(ww*2); - interpPoint2(lead1 , lead2, rail1p[1], rail2p[1], railPos, hs*2*(1-railPos)); - interpPoint2(lead1 , lead2, rail1p[0], rail2p[0], railPos, hs*2); - + double railPos = .5 - ((dn / 2) % sim.gridSize) / (ww * 2); + interpPoint2(lead1, lead2, rail1p[1], rail2p[1], railPos, hs * 2 * (1 - railPos)); + interpPoint2(lead1, lead2, rail1p[0], rail2p[0], railPos, hs * 2); + Point tris[] = newPointArray(2); - interpPoint2(lead1, lead2, tris[0], tris[1], 0, hs*2); + interpPoint2(lead1, lead2, tris[0], tris[1], 0, hs * 2); triangle = createPolygon(tris[0], tris[1], lead2); plusFont = new Font("SansSerif", 0, 14); setPost(0, in1p[0]); @@ -231,31 +238,31 @@ void setPoints() { @Override public int getDumpType() { - return 409; + return 409; } void getInfo(String arr[]) { - String type = (modelType == MODEL_741) ? "LM741" : "LM324"; + String type = (modelType == MODEL_741) ? "LM741" : "LM324"; arr[0] = "op-amp (" + type + ")"; arr[1] = "V+ = " + getVoltageText(volts[1]); arr[2] = "V- = " + getVoltageText(volts[0]); arr[3] = "Vout = " + getVoltageText(volts[2]); arr[4] = "Iout = " + getCurrentText(getCurrentIntoNode(2)); } - + public EditInfo getEditInfo(int n) { if (n == 0) { - EditInfo ei = new EditInfo(EditInfo.makeLink("opampreal.html", "Model"), modelType); + EditInfo ei = new EditInfo(EditInfo.makeLink("opampreal.html", "Model"), modelType); ei.choice = new Choice(); ei.choice.add("LM741"); // hide old 324 model if (modelType == MODEL_324) { - ei.choice.add("LM324, old"); - ei.choice.add("LM324, fixed"); + ei.choice.add("LM324, old"); + ei.choice.add("LM324, fixed"); ei.choice.select(modelType); } else { - ei.choice.add("LM324"); - ei.choice.select(modelType == MODEL_741 ? 0 : 1); + ei.choice.add("LM324"); + ei.choice.select(modelType == MODEL_741 ? 0 : 1); } return ei; } @@ -266,33 +273,34 @@ public EditInfo getEditInfo(int n) { } if (modelType == MODEL_324v2) return null; - if (n == 2) - return new EditInfo("Slew Rate (V/usec)", slewRate); - if (n == 3) - return new EditInfo("Output Current Limit (A)", currentLimit); - return null; + if (n == 2) + return new EditInfo("Slew Rate (V/usec)", slewRate); + if (n == 3) + return new EditInfo("Output Current Limit (A)", currentLimit); + return null; } + public void setEditValue(int n, EditInfo ei) { - if (n == 0) { - modelType = ei.choice.getSelectedIndex(); + if (n == 0) { + modelType = ei.choice.getSelectedIndex(); if (ei.choice.getItemCount() == 2 && modelType == 1) - modelType = MODEL_324v2; - capValue = 0; - initModel(); - ei.newDialog = true; - } - if (n == 1) { - flags = ei.changeFlag(flags, FLAG_SWAP); - setPoints(); - } - if (n == 2) { - slewRate = ei.value; - initModel(); - } - if (n == 3) { - currentLimit = ei.value; - initModel(); - } + modelType = MODEL_324v2; + capValue = 0; + initModel(); + ei.newDialog = true; + } + if (n == 1) { + flags = ei.changeFlag(flags, FLAG_SWAP); + setPoints(); + } + if (n == 2) { + slewRate = ei.value; + initModel(); + } + if (n == 3) { + currentLimit = ei.value; + initModel(); + } } - + } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/OpAmpSwapElm.java b/src/main/java/com/lushprojects/circuitjs1/client/OpAmpSwapElm.java index 2376d98..b6077ad 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/OpAmpSwapElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/OpAmpSwapElm.java @@ -19,11 +19,17 @@ package com.lushprojects.circuitjs1.client; -class OpAmpSwapElm extends OpAmpElm { - public OpAmpSwapElm(int xx, int yy) { - super(xx, yy); - flags |= FLAG_SWAP; - } - Class getDumpClass() { return OpAmpElm.class; } - int getShortcut() { return 'A'; } +public class OpAmpSwapElm extends OpAmpElm { + public OpAmpSwapElm(int xx, int yy) { + super(xx, yy); + flags |= FLAG_SWAP; } + + Class getDumpClass() { + return OpAmpElm.class; + } + + int getShortcut() { + return 'A'; + } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/OptocouplerElm.java b/src/main/java/com/lushprojects/circuitjs1/client/OptocouplerElm.java index 86f29d8..47c020e 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/OptocouplerElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/OptocouplerElm.java @@ -6,56 +6,56 @@ public class OptocouplerElm extends CompositeElm { double curCounts[]; private static String modelString = "DiodeElm 6 1\rCCCSElm 1 2 3 4\rNTransistorElm 3 4 5"; - private static int[] modelExternalNodes = { 6, 2, 4, 5 }; + private static int[] modelExternalNodes = {6, 2, 4, 5}; DiodeElm diode; TransistorElm transistor; public OptocouplerElm(int xx, int yy) { - super(xx, yy, modelString, modelExternalNodes); - noDiagonal = true; - initOptocoupler(); + super(xx, yy, modelString, modelExternalNodes); + noDiagonal = true; + initOptocoupler(); } public OptocouplerElm(int xa, int ya, int xb, int yb, int f, StringTokenizer st) { - // pass st=null since we don't need to undump any of the sub-elements - super(xa, ya, xb, yb, f, null, modelString, modelExternalNodes); - noDiagonal = true; - initOptocoupler(); + // pass st=null since we don't need to undump any of the sub-elements + super(xa, ya, xb, yb, f, null, modelString, modelExternalNodes); + noDiagonal = true; + initOptocoupler(); } public String dump() { - return dumpWithMask(0); + return dumpWithMask(0); } - + private void initOptocoupler() { - csize = 2; - cspc = 8*2; - cspc2 = cspc*2; - diode = (DiodeElm) compElmList.get(0); - CCCSElm cccs = (CCCSElm) compElmList.get(1); - - // from http://www.cel.com/pdf/appnotes/an3017.pdf - cccs.setExpr("max(0,min(.0001, select(i-.003, (-80000000000*(i)^5+800000000*(i)^4-3000000*(i)^3+5177.2*(i)^2+.2453*(i)-.00005)*1.04/700, (9000000*(i)^5-998113*(i)^4+42174*(i)^3-861.32*(i)^2+9.0836*(i)-.0078)*.945/700)))"); - - transistor = (TransistorElm) compElmList.get(2); - transistor.setBeta(700); - curCounts = new double[4]; + csize = 2; + cspc = 8 * 2; + cspc2 = cspc * 2; + diode = (DiodeElm) compElmList.get(0); + CCCSElm cccs = (CCCSElm) compElmList.get(1); + + // from http://www.cel.com/pdf/appnotes/an3017.pdf + cccs.setExpr("max(0,min(.0001, select(i-.003, (-80000000000*(i)^5+800000000*(i)^4-3000000*(i)^3+5177.2*(i)^2+.2453*(i)-.00005)*1.04/700, (9000000*(i)^5-998113*(i)^4+42174*(i)^3-861.32*(i)^2+9.0836*(i)-.0078)*.945/700)))"); + + transistor = (TransistorElm) compElmList.get(2); + transistor.setBeta(700); + curCounts = new double[4]; } public void reset() { - super.reset(); - curCounts = new double[4]; + super.reset(); + curCounts = new double[4]; } public boolean getConnection(int n1, int n2) { - return n1/2 == n2/2; + return n1 / 2 == n2 / 2; } void draw(Graphics g) { g.setColor(needsHighlight() ? selectColor : lightGrayColor); drawThickPolygon(g, rectPointsX, rectPointsY, 4); - + // draw stubs int i; for (i = 0; i != 4; i++) { @@ -66,43 +66,44 @@ void draw(Graphics g) { curCounts[i] = updateDotCount(-getCurrentIntoNode(i), curCounts[i]); drawDots(g, a, b, curCounts[i]); } - + diode.draw(g); transistor.draw(g); - + drawPosts(g); // draw little arrows g.setColor(lightGrayColor); - int dx = isFlippedX() ? -1 : 1; - int sx = stubs[0].x+2*dx; - int sy = (stubs[0].y+stubs[1].y)/2; + int dx = isFlippedX() ? -1 : 1; + int sx = stubs[0].x + 2 * dx; + int sy = (stubs[0].y + stubs[1].y) / 2; for (i = 0; i != 2; i++) { - int y = sy+i*10-5; - Point p1 = new Point(sx, y); - Point p2 = new Point(sx+20*dx, y); + int y = sy + i * 10 - 5; + Point p1 = new Point(sx, y); + Point p2 = new Point(sx + 20 * dx, y); Polygon p = calcArrow(p1, p2, 5, 2); g.fillPolygon(p); - g.drawLine(sx+10*dx, y, sx+15*dx, y); + g.drawLine(sx + 10 * dx, y, sx + 15 * dx, y); } } Point stubs[]; - + void setPoints() { - super.setPoints(); - - // adapted from ChipElm + super.setPoints(); + + // adapted from ChipElm int hs = cspc; - int x0 = x+cspc2; int y0 = y; - int xr = x0-cspc; - int yr = y0-cspc/2; + int x0 = x + cspc2; + int y0 = y; + int xr = x0 - cspc; + int yr = y0 - cspc / 2; int sizeX = 2; int sizeY = 2; - int xs = sizeX*cspc2; - int ys = sizeY*cspc2-cspc; - rectPointsX = new int[] { xr, xr+xs, xr+xs, xr }; - rectPointsY = new int[] { yr, yr, yr+ys, yr+ys }; + int xs = sizeX * cspc2; + int ys = sizeY * cspc2 - cspc; + 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]); stubs = new Point[4]; // setPin(0, x0, y0, 1, 0, 0, -1, 0, 0); @@ -111,45 +112,47 @@ void setPoints() { // setPin(3, x0, y0, 1, 0, 0, 1, 0, ys-cspc2); setPin(0, x0, y0, 0, 1, -1, 0, 0, 0); setPin(1, x0, y0, 0, 1, -1, 0, 0, 0); - setPin(2, x0, y0, 0, 1, 1, 0, xs-cspc2, 0); - setPin(3, x0, y0, 0, 1, 1, 0, xs-cspc2, 0); + setPin(2, x0, y0, 0, 1, 1, 0, xs - cspc2, 0); + setPin(3, x0, y0, 0, 1, 1, 0, xs - cspc2, 0); int dx = isFlippedX() ? -1 : 1; - diode.setPosition(posts[0].x+32*dx, posts[0].y, posts[1].x+32*dx, posts[1].y); + diode.setPosition(posts[0].x + 32 * dx, posts[0].y, posts[1].x + 32 * dx, posts[1].y); stubs[0] = diode.getPost(0); stubs[1] = diode.getPost(1); - - int midp = (posts[2].y+posts[3].y)/2; - transistor.setPosition(posts[2].x-40*dx, midp, posts[2].x-24*dx, midp); + + int midp = (posts[2].y + posts[3].y) / 2; + transistor.setPosition(posts[2].x - 40 * dx, midp, posts[2].x - 24 * dx, midp); stubs[2] = transistor.getPost(1); stubs[3] = transistor.getPost(2); } - boolean isFlippedX() { return (flags & ChipElm.FLAG_FLIP_X) != 0; } + boolean isFlippedX() { + return (flags & ChipElm.FLAG_FLIP_X) != 0; + } void setPin(int n, int px, int py, int dx, int dy, int dax, int day, int sx, int sy) { - int pos = n % 2; - if (isFlippedX()) { - dx = -dx; - dax = -dax; - px += cspc2; - sx = -sx; - } - - int xa = px+cspc2*dx*pos+sx; - int ya = py+cspc2*dy*pos+sy; - setPost(n, new Point(xa+dax*cspc2, ya+day*cspc2)); - stubs[n] = new Point(xa+dax*cspc , ya+day*cspc ); + int pos = n % 2; + if (isFlippedX()) { + dx = -dx; + dax = -dax; + px += cspc2; + sx = -sx; + } + + int xa = px + cspc2 * dx * pos + sx; + int ya = py + cspc2 * dy * pos + sy; + setPost(n, new Point(xa + dax * cspc2, ya + day * cspc2)); + stubs[n] = new Point(xa + dax * cspc, ya + day * cspc); } - + @Override public int getDumpType() { - return 407; + return 407; } void getInfo(String arr[]) { - arr[0] = "optocoupler"; - arr[1] = "Iin = " + getCurrentText(getCurrentIntoNode(0)); - arr[2] = "Iout = " + getCurrentText(getCurrentIntoNode(2)); + arr[0] = "optocoupler"; + arr[1] = "Iin = " + getCurrentText(getCurrentIntoNode(0)); + arr[2] = "Iout = " + getCurrentText(getCurrentIntoNode(2)); } public EditInfo getEditInfo(int n) { diff --git a/src/main/java/com/lushprojects/circuitjs1/client/OrGateElm.java b/src/main/java/com/lushprojects/circuitjs1/client/OrGateElm.java index d7e0fcb..3766983 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/OrGateElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/OrGateElm.java @@ -19,100 +19,114 @@ package com.lushprojects.circuitjs1.client; - class OrGateElm extends GateElm { - public OrGateElm(int xx, int yy) { super(xx, yy); } - public OrGateElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f, st); - } - String getGateName() { return "OR gate"; } - - void drawGatePolygon(Graphics g) { - g.setLineWidth(3.0); - g.context.beginPath(); - g.context.moveTo(gatePoly.xpoints[0], gatePoly.ypoints[0]); - g.context.lineTo(gatePoly.xpoints[1], gatePoly.ypoints[1]); - g.context.bezierCurveTo( - gatePoly.xpoints[2], gatePoly.ypoints[2], - gatePoly.xpoints[2], gatePoly.ypoints[2], - gatePoly.xpoints[3], gatePoly.ypoints[3]); - g.context.bezierCurveTo( - gatePoly.xpoints[4], gatePoly.ypoints[4], - gatePoly.xpoints[4], gatePoly.ypoints[4], - gatePoly.xpoints[5], gatePoly.ypoints[5]); - g.context.lineTo(gatePoly.xpoints[6], gatePoly.ypoints[6]); +public class OrGateElm extends GateElm { + public OrGateElm(int xx, int yy) { + super(xx, yy); + } + + public OrGateElm(int xa, int ya, int xb, int yb, int f, + StringTokenizer st) { + super(xa, ya, xb, yb, f, st); + } + + String getGateName() { + return "OR gate"; + } + + void drawGatePolygon(Graphics g) { + g.setLineWidth(3.0); + g.context.beginPath(); + g.context.moveTo(gatePoly.xpoints[0], gatePoly.ypoints[0]); + g.context.lineTo(gatePoly.xpoints[1], gatePoly.ypoints[1]); + g.context.bezierCurveTo( + gatePoly.xpoints[2], gatePoly.ypoints[2], + gatePoly.xpoints[2], gatePoly.ypoints[2], + gatePoly.xpoints[3], gatePoly.ypoints[3]); + g.context.bezierCurveTo( + gatePoly.xpoints[4], gatePoly.ypoints[4], + gatePoly.xpoints[4], gatePoly.ypoints[4], + gatePoly.xpoints[5], gatePoly.ypoints[5]); + g.context.lineTo(gatePoly.xpoints[6], gatePoly.ypoints[6]); + g.context.bezierCurveTo( + gatePoly.xpoints[7], gatePoly.ypoints[7], + gatePoly.xpoints[7], gatePoly.ypoints[7], + gatePoly.xpoints[0], gatePoly.ypoints[0]); + g.context.closePath(); + + if (this instanceof XorGateElm) { + g.context.moveTo(gatePoly.xpoints[8], gatePoly.ypoints[8]); g.context.bezierCurveTo( - gatePoly.xpoints[7], gatePoly.ypoints[7], - gatePoly.xpoints[7], gatePoly.ypoints[7], - gatePoly.xpoints[0], gatePoly.ypoints[0]); - g.context.closePath(); - - if (this instanceof XorGateElm) { - g.context.moveTo(gatePoly.xpoints[8], gatePoly.ypoints[8]); - g.context.bezierCurveTo( - gatePoly.xpoints[10], gatePoly.ypoints[10], - gatePoly.xpoints[10], gatePoly.ypoints[10], - gatePoly.xpoints[9], gatePoly.ypoints[9]); - } + gatePoly.xpoints[10], gatePoly.ypoints[10], + gatePoly.xpoints[10], gatePoly.ypoints[10], + gatePoly.xpoints[9], gatePoly.ypoints[9]); + } + + g.context.stroke(); + g.setLineWidth(1.0); + } + + double getLeadAdjustment(int ix) { + if (useEuroGates()) + return 0; + if (inputCount > 3 && (ix == 0 || ix == inputCount - 1)) + return -.05; + if (inputCount > 7 && (ix == 1 || ix == inputCount - 2)) + return -.05; + if (inputCount >= 12 && (ix == 2 || ix == inputCount - 3)) + return -.05; + return 0; + } - g.context.stroke(); - g.setLineWidth(1.0); - } - - double getLeadAdjustment(int ix) { - if (useEuroGates()) - return 0; - if (inputCount > 3 && (ix == 0 || ix == inputCount-1)) - return -.05; - if (inputCount > 7 && (ix == 1 || ix == inputCount-2)) - return -.05; - if (inputCount >= 12 && (ix == 2 || ix == inputCount-3)) - return -.05; - return 0; - } - - void setPoints() { - super.setPoints(); - - if (useEuroGates()) { - createEuroGatePolygon(); - linePoints = null; - } else { - // 0 - top left, 1 - start of top curve, 2 - control point for top curve - // 3 - right, 4 - control point for bottom curve, 5 - start of bottom curve, 6 - bottom right, 7 - control point for left curve + void setPoints() { + super.setPoints(); + + if (useEuroGates()) { + createEuroGatePolygon(); + linePoints = null; + } else { + // 0 - top left, 1 - start of top curve, 2 - control point for top curve + // 3 - right, 4 - control point for bottom curve, 5 - start of bottom curve, 6 - bottom right, 7 - control point for left curve // if (this instanceof XorGateElm) // linePoints = new Point[5]; - - Point triPoints[] = newPointArray(11); - interpPoint2(lead1, lead2, triPoints[0], triPoints[6], -.05, hs2); - interpPoint2(lead1, lead2, triPoints[1], triPoints[5], .3, hs2); - triPoints[3] = lead2; - interpPoint2(lead1, lead2, triPoints[2], triPoints[4], .7, hs2*.81); - interpPoint(lead1, lead2, triPoints[7], .08); // was .15 - - if (this instanceof XorGateElm) { - double ww2 = (ww == 0) ? dn*2 : ww*2; - interpPoint2(lead1, lead2, triPoints[8], triPoints[9], -.05-5/ww2, hs2); - interpPoint(lead1, lead2, triPoints[10], .08-5/ww2); - } - - gatePoly = createPolygon(triPoints); - } - if (isInverting()) { - pcircle = interpPoint(point1, point2, .5+(ww+4)/dn); - lead2 = interpPoint(point1, point2, .5+(ww+8)/dn); - } - } - - String getGateText() { return "\u22651"; } - - boolean calcFunction() { - int i; - boolean f = false; - for (i = 0; i != inputCount; i++) - f |= getInput(i); - return f; - } - int getDumpType() { return 152; } - int getShortcut() { return '3'; } + + Point triPoints[] = newPointArray(11); + interpPoint2(lead1, lead2, triPoints[0], triPoints[6], -.05, hs2); + interpPoint2(lead1, lead2, triPoints[1], triPoints[5], .3, hs2); + triPoints[3] = lead2; + interpPoint2(lead1, lead2, triPoints[2], triPoints[4], .7, hs2 * .81); + interpPoint(lead1, lead2, triPoints[7], .08); // was .15 + + if (this instanceof XorGateElm) { + double ww2 = (ww == 0) ? dn * 2 : ww * 2; + interpPoint2(lead1, lead2, triPoints[8], triPoints[9], -.05 - 5 / ww2, hs2); + interpPoint(lead1, lead2, triPoints[10], .08 - 5 / ww2); + } + + gatePoly = createPolygon(triPoints); + } + if (isInverting()) { + pcircle = interpPoint(point1, point2, .5 + (ww + 4) / dn); + lead2 = interpPoint(point1, point2, .5 + (ww + 8) / dn); + } + } + + String getGateText() { + return "\u22651"; + } + + boolean calcFunction() { + int i; + boolean f = false; + for (i = 0; i != inputCount; i++) + f |= getInput(i); + return f; + } + + int getDumpType() { + return 152; + } + + int getShortcut() { + return '3'; } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/OutputElm.java b/src/main/java/com/lushprojects/circuitjs1/client/OutputElm.java index f38cbf7..696977d 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/OutputElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/OutputElm.java @@ -21,98 +21,121 @@ import com.lushprojects.circuitjs1.client.util.Locale; -class OutputElm extends CircuitElm { - final int FLAG_VALUE = 1; - final int FLAG_FIXED = 2; - int scale; - public OutputElm(int xx, int yy) { - super(xx, yy); - scale = SCALE_AUTO; - } - public OutputElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f); - scale = SCALE_AUTO; - try { - scale = Integer.parseInt(st.nextToken()); - } catch (Exception e) {} - } - - String dump() { - return super.dump() + " " + scale; - } - int getDumpType() { return 'O'; } - int getPostCount() { return 1; } - void setPoints() { - super.setPoints(); - lead1 = new Point(); - } - void draw(Graphics g) { - g.save(); - boolean selected = needsHighlight(); - Font f = new Font("SansSerif", selected ? Font.BOLD : 0, 14); - g.setFont(f); - g.setColor(selected ? selectColor : whiteColor); - String s = showVoltage() ? getUnitTextWithScale(volts[0], "V", scale, isFixed()) : Locale.LS("out"); +public class OutputElm extends CircuitElm { + final int FLAG_VALUE = 1; + final int FLAG_FIXED = 2; + int scale; + + public OutputElm(int xx, int yy) { + super(xx, yy); + scale = SCALE_AUTO; + } + + public OutputElm(int xa, int ya, int xb, int yb, int f, + StringTokenizer st) { + super(xa, ya, xb, yb, f); + scale = SCALE_AUTO; + try { + scale = Integer.parseInt(st.nextToken()); + } catch (Exception e) { + } + } + + String dump() { + return super.dump() + " " + scale; + } + + int getDumpType() { + return 'O'; + } + + int getPostCount() { + return 1; + } + + void setPoints() { + super.setPoints(); + lead1 = new Point(); + } + + void draw(Graphics g) { + g.save(); + boolean selected = needsHighlight(); + Font f = new Font("SansSerif", selected ? Font.BOLD : 0, 14); + g.setFont(f); + g.setColor(selected ? selectColor : whiteColor); + String s = showVoltage() ? getUnitTextWithScale(volts[0], "V", scale, isFixed()) : Locale.LS("out"); // FontMetrics fm = g.getFontMetrics(); - if (this == sim.plotXElm) - s = "X"; - if (this == sim.plotYElm) - s = "Y"; - interpPoint(point1, point2, lead1, 1-((int)g.context.measureText(s).getWidth()/2+8)/dn); - setBbox(point1, lead1, 0); - drawCenteredText(g, s, x2, y2, true); - setVoltageColor(g, volts[0]); - if (selected) - g.setColor(selectColor); - drawThickLine(g, point1, lead1); - drawPosts(g); - g.restore(); - } - double getVoltageDiff() { return volts[0]; } - void getInfo(String arr[]) { - arr[0] = "output"; - arr[1] = "V = " + getVoltageText(volts[0]); - } - public EditInfo getEditInfo(int n) { - if (n == 0) - return EditInfo.createCheckbox("Show Voltage", showVoltage()); - if (!showVoltage()) - return null; - if (n == 1) { - EditInfo ei = new EditInfo("Scale", 0); - ei.choice = new Choice(); - ei.choice.add("Auto"); - ei.choice.add("V"); - ei.choice.add("mV"); - ei.choice.add(Locale.muString + "V"); - ei.choice.select(scale); - return ei; - } - if (scale == SCALE_AUTO) - return null; - if (n == 2) - return EditInfo.createCheckbox("Fixed Precision", isFixed()); - return null; - } - boolean isFixed() { return (flags & FLAG_FIXED) != 0; } - boolean showVoltage() { return (flags & FLAG_VALUE) != 0; } - public void setEditValue(int n, EditInfo ei) { - if (n == 0) { - flags = ei.changeFlag(flags, FLAG_VALUE); - ei.newDialog = true; - } - if (n==1) { - scale = ei.choice.getSelectedIndex(); - ei.newDialog = true; - } - if (n == 2) - flags = ei.changeFlag(flags, FLAG_FIXED); - } + if (this == sim.plotXElm) + s = "X"; + if (this == sim.plotYElm) + s = "Y"; + interpPoint(point1, point2, lead1, 1 - ((int) g.context.measureText(s).getWidth() / 2 + 8) / dn); + setBbox(point1, lead1, 0); + drawCenteredText(g, s, x2, y2, true); + setVoltageColor(g, volts[0]); + if (selected) + g.setColor(selectColor); + drawThickLine(g, point1, lead1); + drawPosts(g); + g.restore(); + } + + double getVoltageDiff() { + return volts[0]; + } + + void getInfo(String arr[]) { + arr[0] = "output"; + arr[1] = "V = " + getVoltageText(volts[0]); + } + + public EditInfo getEditInfo(int n) { + if (n == 0) + return EditInfo.createCheckbox("Show Voltage", showVoltage()); + if (!showVoltage()) + return null; + if (n == 1) { + EditInfo ei = new EditInfo("Scale", 0); + ei.choice = new Choice(); + ei.choice.add("Auto"); + ei.choice.add("V"); + ei.choice.add("mV"); + ei.choice.add(Locale.muString + "V"); + ei.choice.select(scale); + return ei; + } + if (scale == SCALE_AUTO) + return null; + if (n == 2) + return EditInfo.createCheckbox("Fixed Precision", isFixed()); + return null; + } + + boolean isFixed() { + return (flags & FLAG_FIXED) != 0; + } + + boolean showVoltage() { + return (flags & FLAG_VALUE) != 0; + } + + public void setEditValue(int n, EditInfo ei) { + if (n == 0) { + flags = ei.changeFlag(flags, FLAG_VALUE); + ei.newDialog = true; + } + if (n == 1) { + scale = ei.choice.getSelectedIndex(); + ei.newDialog = true; + } + if (n == 2) + flags = ei.changeFlag(flags, FLAG_FIXED); + } // void drawHandles(Graphics g, Color c) { // g.setColor(c); // g.fillRect(x-3, y-3, 7, 7); // } - - } + +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/PDarlingtonElm.java b/src/main/java/com/lushprojects/circuitjs1/client/PDarlingtonElm.java index c9f0f79..0d6dab4 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/PDarlingtonElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/PDarlingtonElm.java @@ -4,14 +4,13 @@ public class PDarlingtonElm extends DarlingtonElm { - public PDarlingtonElm(int xx, int yy) { - super(xx, yy, true); + super(xx, yy, true); } Class getDumpClass() { - return DarlingtonElm.class; + return DarlingtonElm.class; } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/PMosfetElm.java b/src/main/java/com/lushprojects/circuitjs1/client/PMosfetElm.java index 15c6274..a2696e8 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/PMosfetElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/PMosfetElm.java @@ -19,8 +19,16 @@ package com.lushprojects.circuitjs1.client; -class PMosfetElm extends MosfetElm { - public PMosfetElm(int xx, int yy) { super(xx, yy, true); } - Class getDumpClass() { return MosfetElm.class; } - int getShortcut() { return 'P'; } +public class PMosfetElm extends MosfetElm { + public PMosfetElm(int xx, int yy) { + super(xx, yy, true); } + + Class getDumpClass() { + return MosfetElm.class; + } + + int getShortcut() { + return 'P'; + } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/PTransistorElm.java b/src/main/java/com/lushprojects/circuitjs1/client/PTransistorElm.java index 391f0d4..aea1381 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/PTransistorElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/PTransistorElm.java @@ -19,10 +19,17 @@ package com.lushprojects.circuitjs1.client; -class PTransistorElm extends TransistorElm { - public PTransistorElm(int xx, int yy) { super(xx, yy, true); } - Class getDumpClass() { return TransistorElm.class; } - - int getShortcut() { return 'p'; } - +public class PTransistorElm extends TransistorElm { + public PTransistorElm(int xx, int yy) { + super(xx, yy, true); } + + Class getDumpClass() { + return TransistorElm.class; + } + + int getShortcut() { + return 'p'; + } + +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/PhaseCompElm.java b/src/main/java/com/lushprojects/circuitjs1/client/PhaseCompElm.java index 463bf29..cf9b652 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/PhaseCompElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/PhaseCompElm.java @@ -20,53 +20,75 @@ package com.lushprojects.circuitjs1.client; -class PhaseCompElm extends ChipElm { - public PhaseCompElm(int xx, int yy) { super(xx, yy); } +public class PhaseCompElm extends ChipElm { + public PhaseCompElm(int xx, int yy) { + super(xx, yy); + } + public PhaseCompElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f, st); + StringTokenizer st) { + super(xa, ya, xb, yb, f, st); + } + + String getChipName() { + return "phase comparator"; } - String getChipName() { return "phase comparator"; } + void setupPins() { - sizeX = 2; - sizeY = 2; - pins = new Pin[3]; - pins[0] = new Pin(0, SIDE_W, "I1"); - pins[1] = new Pin(1, SIDE_W, "I2"); - pins[2] = new Pin(0, SIDE_E, "O"); - pins[2].output = true; + sizeX = 2; + sizeY = 2; + pins = new Pin[3]; + pins[0] = new Pin(0, SIDE_W, "I1"); + pins[1] = new Pin(1, SIDE_W, "I2"); + pins[2] = new Pin(0, SIDE_E, "O"); + pins[2].output = true; + } + + boolean nonLinear() { + return true; } - boolean nonLinear() { return true; } + void stamp() { - int vn = sim.nodeList.size()+pins[2].voltSource; - sim.stampNonLinear(vn); - sim.stampNonLinear(0); - sim.stampNonLinear(nodes[2]); + int vn = sim.nodeList.size() + pins[2].voltSource; + sim.stampNonLinear(vn); + sim.stampNonLinear(0); + sim.stampNonLinear(nodes[2]); } + boolean ff1, ff2; + void doStep() { - boolean v1 = volts[0] > getThreshold(); - boolean v2 = volts[1] > getThreshold(); - if (v1 && !pins[0].value) - ff1 = true; - if (v2 && !pins[1].value) - ff2 = true; - if (ff1 && ff2) - ff1 = ff2 = false; - double out = (ff1) ? highVoltage : (ff2) ? 0 : -1; - //System.out.println(out + " " + v1 + " " + v2); - if (out != -1) - sim.stampVoltageSource(0, nodes[2], pins[2].voltSource, out); - else { - // tie current through output pin to 0 - int vn = sim.nodeList.size()+pins[2].voltSource; - sim.stampMatrix(vn, vn, 1); - } - pins[0].value = v1; - pins[1].value = v2; + boolean v1 = volts[0] > getThreshold(); + boolean v2 = volts[1] > getThreshold(); + if (v1 && !pins[0].value) + ff1 = true; + if (v2 && !pins[1].value) + ff2 = true; + if (ff1 && ff2) + ff1 = ff2 = false; + double out = (ff1) ? highVoltage : (ff2) ? 0 : -1; + //System.out.println(out + " " + v1 + " " + v2); + if (out != -1) + sim.stampVoltageSource(0, nodes[2], pins[2].voltSource, out); + else { + // tie current through output pin to 0 + int vn = sim.nodeList.size() + pins[2].voltSource; + sim.stampMatrix(vn, vn, 1); + } + pins[0].value = v1; + pins[1].value = v2; + } + + int getPostCount() { + return 3; + } + + int getVoltageSourceCount() { + return 1; + } + + int getDumpType() { + return 161; } - int getPostCount() { return 3; } - int getVoltageSourceCount() { return 1; } - int getDumpType() { return 161; } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/PisoShiftElm.java b/src/main/java/com/lushprojects/circuitjs1/client/PisoShiftElm.java index 9ef7148..70b6047 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/PisoShiftElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/PisoShiftElm.java @@ -21,127 +21,148 @@ // contributed by Edward Calver -class PisoShiftElm extends ChipElm { - final int FLAG_NEW_BEHAVIOR = 2; //SER and no extra output register - - boolean[] data = new boolean[0]; - int dataIndex = 0; - boolean clockState = false; - boolean loadState = false; - int dataPinIndex; // the register pins' starting index - - public PisoShiftElm(int xx, int yy) { - super(xx, yy); - data = new boolean[bits]; - flags |= FLAG_NEW_BEHAVIOR; - setupPins(); - } - public PisoShiftElm(int xa, int ya, int xb, int yb, int f, StringTokenizer st) { - super(xa, ya, xb, yb, f, st); - data = new boolean[bits]; - readBits(st, data); - setupPins(); - } - - String dump() { - //Normalize the circular array before exporting - boolean[] newData = new boolean[data.length]; - for (int i = 0; i < data.length; i++) - newData[i] = data[(i + dataIndex) % data.length]; - dataIndex = 0; - data = newData; - - return super.dump() + writeBits(data); - } - int getDumpType() { return 186; } - String getChipName() { return "PISO shift register"; } - - boolean needsBits() { return true; } - int defaultBitCount() { return 8; } - - boolean hasNewBhvr() { return (flags & FLAG_NEW_BEHAVIOR) != 0; } - - void reset() { - super.reset(); - data = new boolean[bits]; - } - - void setupPins() { - sizeX = bits + 2; - sizeY = 3; - pins = new Pin[getPostCount()]; - - pins[0] = new Pin(1, SIDE_W, "LD"); - pins[1] = new Pin(2, SIDE_W, ""); - pins[1].clock = true; - - pins[2] = new Pin(1, SIDE_E, "Q" + (hasNewBhvr() ? bits-1 : bits)); - pins[2].output = true; - - if (hasNewBhvr()) { - pins[3] = new Pin(0, SIDE_W, "SER"); - if (data != null && data.length > 0) - pins[2].value = data[0]; - dataPinIndex = 4; - } else { - dataPinIndex = 3; - } - - for (int i = 0; i < bits; i++) - pins[dataPinIndex + i] = new Pin(bits - i, SIDE_N, "D" + (bits - (i + 1))); - - allocNodes(); - } - int getPostCount() { return (hasNewBhvr() ? 4 : 3) + bits; } - int getVoltageSourceCount() { return 1; } - - void execute() { - //LOAD raised - if (pins[0].value != loadState) { - loadState = pins[0].value; - if (loadState && data.length > 0) { - if (hasNewBhvr()) { - pins[2].value = pins[dataPinIndex].value; //Set output immediately - dataIndex = 0; - } else { - dataIndex = -1; - } - for (int i = 0; i < data.length; i++) - data[i] = pins[dataPinIndex + i].value; - } - } - - //CLK raised: Rotate the circular array - if (pins[1].value != clockState) { - clockState = pins[1].value; - if (clockState) { - //Shift - if (dataIndex >= 0) - data[dataIndex] = hasNewBhvr() && pins[3].value; - dataIndex++; - if (dataIndex >= data.length) - dataIndex = 0; - - //Write - pins[2].value = data[dataIndex]; - } - } - } - - public EditInfo getChipEditInfo(int n) { - if (n == 0) - return new EditInfo("# of Bits", bits, 1, 1).setDimensionless(); - return null; - } - public void setChipEditValue(int n, EditInfo ei) { - if (n == 0) { - if (ei.value != bits && ei.value >= 1) { - bits = (int)ei.value; - data = new boolean[bits]; - setupPins(); - setPoints(); - } - return; - } - } +public class PisoShiftElm extends ChipElm { + final int FLAG_NEW_BEHAVIOR = 2; //SER and no extra output register + + boolean[] data = new boolean[0]; + int dataIndex = 0; + boolean clockState = false; + boolean loadState = false; + int dataPinIndex; // the register pins' starting index + + public PisoShiftElm(int xx, int yy) { + super(xx, yy); + data = new boolean[bits]; + flags |= FLAG_NEW_BEHAVIOR; + setupPins(); + } + + public PisoShiftElm(int xa, int ya, int xb, int yb, int f, StringTokenizer st) { + super(xa, ya, xb, yb, f, st); + data = new boolean[bits]; + readBits(st, data); + setupPins(); + } + + String dump() { + //Normalize the circular array before exporting + boolean[] newData = new boolean[data.length]; + for (int i = 0; i < data.length; i++) + newData[i] = data[(i + dataIndex) % data.length]; + dataIndex = 0; + data = newData; + + return super.dump() + writeBits(data); + } + + int getDumpType() { + return 186; + } + + String getChipName() { + return "PISO shift register"; + } + + boolean needsBits() { + return true; + } + + int defaultBitCount() { + return 8; + } + + boolean hasNewBhvr() { + return (flags & FLAG_NEW_BEHAVIOR) != 0; + } + + void reset() { + super.reset(); + data = new boolean[bits]; + } + + void setupPins() { + sizeX = bits + 2; + sizeY = 3; + pins = new Pin[getPostCount()]; + + pins[0] = new Pin(1, SIDE_W, "LD"); + pins[1] = new Pin(2, SIDE_W, ""); + pins[1].clock = true; + + pins[2] = new Pin(1, SIDE_E, "Q" + (hasNewBhvr() ? bits - 1 : bits)); + pins[2].output = true; + + if (hasNewBhvr()) { + pins[3] = new Pin(0, SIDE_W, "SER"); + if (data != null && data.length > 0) + pins[2].value = data[0]; + dataPinIndex = 4; + } else { + dataPinIndex = 3; + } + + for (int i = 0; i < bits; i++) + pins[dataPinIndex + i] = new Pin(bits - i, SIDE_N, "D" + (bits - (i + 1))); + + allocNodes(); + } + + int getPostCount() { + return (hasNewBhvr() ? 4 : 3) + bits; + } + + int getVoltageSourceCount() { + return 1; + } + + void execute() { + //LOAD raised + if (pins[0].value != loadState) { + loadState = pins[0].value; + if (loadState && data.length > 0) { + if (hasNewBhvr()) { + pins[2].value = pins[dataPinIndex].value; //Set output immediately + dataIndex = 0; + } else { + dataIndex = -1; + } + for (int i = 0; i < data.length; i++) + data[i] = pins[dataPinIndex + i].value; + } + } + + //CLK raised: Rotate the circular array + if (pins[1].value != clockState) { + clockState = pins[1].value; + if (clockState) { + //Shift + if (dataIndex >= 0) + data[dataIndex] = hasNewBhvr() && pins[3].value; + dataIndex++; + if (dataIndex >= data.length) + dataIndex = 0; + + //Write + pins[2].value = data[dataIndex]; + } + } + } + + public EditInfo getChipEditInfo(int n) { + if (n == 0) + return new EditInfo("# of Bits", bits, 1, 1).setDimensionless(); + return null; + } + + public void setChipEditValue(int n, EditInfo ei) { + if (n == 0) { + if (ei.value != bits && ei.value >= 1) { + bits = (int) ei.value; + data = new boolean[bits]; + setupPins(); + setPoints(); + } + return; + } + } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/Point.java b/src/main/java/com/lushprojects/circuitjs1/client/Point.java index 8e65027..6e35b97 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/Point.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/Point.java @@ -20,41 +20,45 @@ package com.lushprojects.circuitjs1.client; public class Point { - public int x; - public int y; - - public Point(int i, int j) { - x=i; - y=j; - } - - public Point(Point p) { - x=p.x; - y=p.y; - } - - public Point() { - x=0; - y=0; - } - - public void setLocation(Point p) { - x=p.x; - y=p.y; - } - - public String toString() { return "Point(" + x + "," + y + ")"; } - - @Override public boolean equals(Object other) { - boolean result = false; - if (other instanceof Point) { - Point that = (Point) other; - result = (this.x == that.x && this.y == that.y); - } - return result; - } - - @Override public int hashCode() { - return (41 * (41 + x) + y); - } + public int x; + public int y; + + public Point(int i, int j) { + x = i; + y = j; + } + + public Point(Point p) { + x = p.x; + y = p.y; + } + + public Point() { + x = 0; + y = 0; + } + + public void setLocation(Point p) { + x = p.x; + y = p.y; + } + + public String toString() { + return "Point(" + x + "," + y + ")"; + } + + @Override + public boolean equals(Object other) { + boolean result = false; + if (other instanceof Point) { + Point that = (Point) other; + result = (this.x == that.x && this.y == that.y); + } + return result; + } + + @Override + public int hashCode() { + return (41 * (41 + x) + y); + } } \ No newline at end of file diff --git a/src/main/java/com/lushprojects/circuitjs1/client/PolarCapacitorElm.java b/src/main/java/com/lushprojects/circuitjs1/client/PolarCapacitorElm.java index fb1e543..dac013e 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/PolarCapacitorElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/PolarCapacitorElm.java @@ -1,68 +1,80 @@ package com.lushprojects.circuitjs1.client; public class PolarCapacitorElm extends CapacitorElm { - double maxNegativeVoltage; - - public PolarCapacitorElm(int xx, int yy) { - super(xx, yy); - maxNegativeVoltage = 1; - } - public PolarCapacitorElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f, st); - maxNegativeVoltage = new Double(st.nextToken()).doubleValue(); - } - int getDumpType() { return 209; } - String dump() { - return super.dump() + " " + maxNegativeVoltage; - } - - Point plusPoint; - - void setPoints() { - super.setPoints(); - double f = (dn/2-4)/dn; - int i; - platePoints = newPointArray(14); - int maxI = platePoints.length-1; - double midI = maxI/2.; - for (i = 0; i <= maxI; i++) { - double q = (i - midI)*.9/midI; - platePoints[i] = interpPoint(plate2[0], plate2[1], i/(double) maxI, 5*(1-Math.sqrt(1-q*q))); - } - plusPoint = interpPoint(point1, point2, f-8/dn, -10*dsign); - if (y2 > y) - plusPoint.y += 4; - if (y > y2) - plusPoint.y += 3; - } - - void draw(Graphics g) { - super.draw(g); - g.setColor(whiteColor); - g.setFont(unitsFont); - int w = (int)g.context.measureText("+").getWidth();; - g.drawString("+", plusPoint.x-w/2, plusPoint.y); - } - void getInfo(String arr[]) { - super.getInfo(arr); - arr[0] = "capacitor (polarized)"; - } - public EditInfo getEditInfo(int n) { - if (n == 3) - return new EditInfo("Max Reverse Voltage", maxNegativeVoltage, 0, 0); - return super.getEditInfo(n); - } - public void setEditValue(int n, EditInfo ei) { - if (n == 3 && ei.value >= 0) - maxNegativeVoltage = ei.value; - super.setEditValue(n, ei); - } - - void stepFinished() { - if (getVoltageDiff() < 0 && getVoltageDiff() < -maxNegativeVoltage) - sim.stop("capacitor exceeded max reverse voltage", this); - super.stepFinished(); - } - int getShortcut() { return 'C'; } + double maxNegativeVoltage; + + public PolarCapacitorElm(int xx, int yy) { + super(xx, yy); + maxNegativeVoltage = 1; + } + + public PolarCapacitorElm(int xa, int ya, int xb, int yb, int f, + StringTokenizer st) { + super(xa, ya, xb, yb, f, st); + maxNegativeVoltage = new Double(st.nextToken()).doubleValue(); + } + + int getDumpType() { + return 209; + } + + String dump() { + return super.dump() + " " + maxNegativeVoltage; + } + + Point plusPoint; + + void setPoints() { + super.setPoints(); + double f = (dn / 2 - 4) / dn; + int i; + platePoints = newPointArray(14); + int maxI = platePoints.length - 1; + double midI = maxI / 2.; + for (i = 0; i <= maxI; i++) { + double q = (i - midI) * .9 / midI; + platePoints[i] = interpPoint(plate2[0], plate2[1], i / (double) maxI, 5 * (1 - Math.sqrt(1 - q * q))); + } + plusPoint = interpPoint(point1, point2, f - 8 / dn, -10 * dsign); + if (y2 > y) + plusPoint.y += 4; + if (y > y2) + plusPoint.y += 3; + } + + void draw(Graphics g) { + super.draw(g); + g.setColor(whiteColor); + g.setFont(unitsFont); + int w = (int) g.context.measureText("+").getWidth(); + ; + g.drawString("+", plusPoint.x - w / 2, plusPoint.y); + } + + void getInfo(String arr[]) { + super.getInfo(arr); + arr[0] = "capacitor (polarized)"; + } + + public EditInfo getEditInfo(int n) { + if (n == 3) + return new EditInfo("Max Reverse Voltage", maxNegativeVoltage, 0, 0); + return super.getEditInfo(n); + } + + public void setEditValue(int n, EditInfo ei) { + if (n == 3 && ei.value >= 0) + maxNegativeVoltage = ei.value; + super.setEditValue(n, ei); + } + + void stepFinished() { + if (getVoltageDiff() < 0 && getVoltageDiff() < -maxNegativeVoltage) + sim.stop("capacitor exceeded max reverse voltage", this); + super.stepFinished(); + } + + int getShortcut() { + return 'C'; + } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/Polygon.java b/src/main/java/com/lushprojects/circuitjs1/client/Polygon.java index 337e176..9448a20 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/Polygon.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/Polygon.java @@ -18,25 +18,25 @@ // via http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/awt/Polygon.java -class Polygon { +public class Polygon { // ArrayList poly; - - private static final int MIN_LENGTH = 4; - public int npoints; - public int xpoints[]; - public int ypoints[]; - - - public Polygon(){ + + private static final int MIN_LENGTH = 4; + public int npoints; + public int xpoints[]; + public int ypoints[]; + + + public Polygon() { // poly = new ArrayList(); xpoints = new int[MIN_LENGTH]; ypoints = new int[MIN_LENGTH]; - } - + } + // public void addPoint(int x, int y){ // poly.add(new Point(x,y)); // } - + public void addPoint(int x, int y) { if (npoints >= xpoints.length || npoints >= ypoints.length) { int newLength = npoints * 2; @@ -58,12 +58,12 @@ public void addPoint(int x, int y) { // updateBounds(x, y); // } } - + private int[] expand(int[] in, int newlen) { - int[] out=new int[newlen]; - for(int i=0; i abs(dy)) { - myLen = 2 * sim.gridSize * Integer.signum(dx) * ((((Integer)Math.abs(dx))+ 2 * sim.gridSize -1) / (2 * sim.gridSize)); - point2.x = point1.x + myLen; - offset = (dx < 0) ? dy : -dy; - point2.y = point1.y; - } else { - myLen = 2 * sim.gridSize * Integer.signum(dy) * ((((Integer)Math.abs(dy))+ 2 * sim.gridSize -1) / (2 * sim.gridSize)); - if (dy != 0) { - point2.y = point1.y + myLen; - offset = (dy > 0) ? dx : -dx; - point2.x = point1.x; - } - } + super.setPoints(); + int offset = 0; + int myLen = 0; + if (abs(dx) > abs(dy)) { + myLen = 2 * sim.gridSize * Integer.signum(dx) * ((((Integer) Math.abs(dx)) + 2 * sim.gridSize - 1) / (2 * sim.gridSize)); + point2.x = point1.x + myLen; + offset = (dx < 0) ? dy : -dy; + point2.y = point1.y; + } else { + myLen = 2 * sim.gridSize * Integer.signum(dy) * ((((Integer) Math.abs(dy)) + 2 * sim.gridSize - 1) / (2 * sim.gridSize)); + if (dy != 0) { + point2.y = point1.y + myLen; + offset = (dy > 0) ? dx : -dx; + point2.x = point1.x; + } + } // if (abs(dx) > abs(dy)) { // dx = Integer.signum(dx) * sim.snapGrid(Math.abs(dx) / 2) * 2; // point2.x = x2 = point1.x + dx; @@ -121,219 +129,231 @@ void setPoints() { // point2.x = point1.x; // } // } - if (offset == 0) - offset = sim.gridSize; - dn = distance(point1, point2); - int bodyLen = 32; - calcLeads(bodyLen); - position = slider.getValue() * .0099 + .005; - int soff = (int) ((position - .5) * bodyLen); - // int offset2 = offset - sign(offset)*4; - post3 = interpPoint(point1, point2, .5, offset); - corner2 = interpPoint(point1, point2, soff / dn + .5, offset); - arrowPoint = interpPoint(point1, point2, soff / dn + .5, 8 * sign(offset)); - midpoint = interpPoint(point1, point2, soff / dn + .5); - arrow1 = new Point(); - arrow2 = new Point(); - double clen = abs(offset) - 8; - interpPoint2(corner2, arrowPoint, arrow1, arrow2, (clen - 8) / clen, 8); - ps3 = new Point(); - ps4 = new Point(); + if (offset == 0) + offset = sim.gridSize; + dn = distance(point1, point2); + int bodyLen = 32; + calcLeads(bodyLen); + position = slider.getValue() * .0099 + .005; + int soff = (int) ((position - .5) * bodyLen); + // int offset2 = offset - sign(offset)*4; + post3 = interpPoint(point1, point2, .5, offset); + corner2 = interpPoint(point1, point2, soff / dn + .5, offset); + arrowPoint = interpPoint(point1, point2, soff / dn + .5, 8 * sign(offset)); + midpoint = interpPoint(point1, point2, soff / dn + .5); + arrow1 = new Point(); + arrow2 = new Point(); + double clen = abs(offset) - 8; + interpPoint2(corner2, arrowPoint, arrow1, arrow2, (clen - 8) / clen, 8); + ps3 = new Point(); + ps4 = new Point(); } - - + + void draw(Graphics g) { - int segments = 16; - int i; - int ox = 0; - int hs = sim.euroResistorCheckItem.getState() ? 6 : 8; - double v1 = volts[0]; - double v2 = volts[1]; - double v3 = volts[2]; - setBbox(point1, point2, hs); - draw2Leads(g); - setPowerColor(g, true); - double segf = 1./segments; - int divide = (int) (segments*position); - if (!sim.euroResistorCheckItem.getState()) { - // draw zigzag - for (i = 0; i != segments; i++) { - int nx = 0; - switch (i & 3) { - case 0: nx = 1; break; - case 2: nx = -1; break; - default: nx = 0; break; - } - double v = v1+(v3-v1)*i/divide; - if (i >= divide) - v = v3+(v2-v3)*(i-divide)/(segments-divide); - setVoltageColor(g, v); - interpPoint(lead1, lead2, ps1, i*segf, hs*ox); - interpPoint(lead1, lead2, ps2, (i+1)*segf, hs*nx); - drawThickLine(g, ps1, ps2); - ox = nx; - } - } else { - // draw rectangle - setVoltageColor(g, v1); - interpPoint2(lead1, lead2, ps1, ps2, 0, hs); - drawThickLine(g, ps1, ps2); - for (i = 0; i != segments; i++) { - double v = v1+(v3-v1)*i/divide; - if (i >= divide) - v = v3+(v2-v3)*(i-divide)/(segments-divide); - setVoltageColor(g, v); - interpPoint2(lead1, lead2, ps1, ps2, i*segf, hs); - interpPoint2(lead1, lead2, ps3, ps4, (i+1)*segf, hs); - drawThickLine(g, ps1, ps3); - drawThickLine(g, ps2, ps4); - } - interpPoint2(lead1, lead2, ps1, ps2, 1, hs); - drawThickLine(g, ps1, ps2); - } - setVoltageColor(g, v3); - drawThickLine(g, post3, corner2); - drawThickLine(g, corner2, arrowPoint); - drawThickLine(g, arrow1, arrowPoint); - drawThickLine(g, arrow2, arrowPoint); - curcount1 = updateDotCount(current1, curcount1); - curcount2 = updateDotCount(current2, curcount2); - curcount3 = updateDotCount(current3, curcount3); - if (sim.dragElm != this) { - drawDots(g, point1, midpoint, curcount1); - drawDots(g, point2, midpoint, curcount2); - drawDots(g, post3, corner2, curcount3); - drawDots(g, corner2, midpoint, addCurCount(curcount3, distance(post3, corner2))); - } - drawPosts(g); - - if (sim.showValuesCheckItem.getState() && resistance1 > 0 && (flags & FLAG_SHOW_VALUES) != 0) { - // check for vertical pot with 3rd terminal on left - boolean reverseY = (post3.x < lead1.x && lead1.x == lead2.x); - // check for horizontal pot with 3rd terminal on top - boolean reverseX = (post3.y < lead1.y && lead1.x != lead2.x); - // check if we need to swap texts (if leads are reversed, e.g. drawn right to left) - boolean rev = (lead1.x == lead2.x && lead1.y < lead2.y) || (lead1.y == lead2.y && lead1.x > lead2.x); - - // draw units - String s1 = getShortUnitText(rev ? resistance2 : resistance1, ""); - String s2 = getShortUnitText(rev ? resistance1 : resistance2, ""); - g.setFont(unitsFont); - g.setColor(whiteColor); - int ya = (int)g.currentFontSize/2; - int w; - w = (int)g.context.measureText(s1).getWidth(); - - // vertical? - if (lead1.x == lead2.x) - g.drawString(s1, !reverseY ? arrowPoint.x+2 : arrowPoint.x-2-w, Math.max(arrow1.y, arrow2.y)+5+ya); - else - g.drawString(s1, Math.min(arrow1.x, arrow2.x)-2-w, !reverseX ? arrowPoint.y+4+ya : arrowPoint.y-4); - - w = (int)g.context.measureText(s2).getWidth(); - if (lead1.x == lead2.x) - g.drawString(s2, !reverseY ? arrowPoint.x+2 : arrowPoint.x-2-w, Math.min(arrow1.y, arrow2.y)-3); - else - g.drawString(s2, Math.max(arrow1.x, arrow2.x)+2, !reverseX ? arrowPoint.y+4+ya : arrowPoint.y-4); - } + int segments = 16; + int i; + int ox = 0; + int hs = sim.euroResistorCheckItem.getState() ? 6 : 8; + double v1 = volts[0]; + double v2 = volts[1]; + double v3 = volts[2]; + setBbox(point1, point2, hs); + draw2Leads(g); + setPowerColor(g, true); + double segf = 1. / segments; + int divide = (int) (segments * position); + if (!sim.euroResistorCheckItem.getState()) { + // draw zigzag + for (i = 0; i != segments; i++) { + int nx = 0; + switch (i & 3) { + case 0: + nx = 1; + break; + case 2: + nx = -1; + break; + default: + nx = 0; + break; + } + double v = v1 + (v3 - v1) * i / divide; + if (i >= divide) + v = v3 + (v2 - v3) * (i - divide) / (segments - divide); + setVoltageColor(g, v); + interpPoint(lead1, lead2, ps1, i * segf, hs * ox); + interpPoint(lead1, lead2, ps2, (i + 1) * segf, hs * nx); + drawThickLine(g, ps1, ps2); + ox = nx; + } + } else { + // draw rectangle + setVoltageColor(g, v1); + interpPoint2(lead1, lead2, ps1, ps2, 0, hs); + drawThickLine(g, ps1, ps2); + for (i = 0; i != segments; i++) { + double v = v1 + (v3 - v1) * i / divide; + if (i >= divide) + v = v3 + (v2 - v3) * (i - divide) / (segments - divide); + setVoltageColor(g, v); + interpPoint2(lead1, lead2, ps1, ps2, i * segf, hs); + interpPoint2(lead1, lead2, ps3, ps4, (i + 1) * segf, hs); + drawThickLine(g, ps1, ps3); + drawThickLine(g, ps2, ps4); + } + interpPoint2(lead1, lead2, ps1, ps2, 1, hs); + drawThickLine(g, ps1, ps2); + } + setVoltageColor(g, v3); + drawThickLine(g, post3, corner2); + drawThickLine(g, corner2, arrowPoint); + drawThickLine(g, arrow1, arrowPoint); + drawThickLine(g, arrow2, arrowPoint); + curcount1 = updateDotCount(current1, curcount1); + curcount2 = updateDotCount(current2, curcount2); + curcount3 = updateDotCount(current3, curcount3); + if (sim.dragElm != this) { + drawDots(g, point1, midpoint, curcount1); + drawDots(g, point2, midpoint, curcount2); + drawDots(g, post3, corner2, curcount3); + drawDots(g, corner2, midpoint, addCurCount(curcount3, distance(post3, corner2))); + } + drawPosts(g); + + if (sim.showValuesCheckItem.getState() && resistance1 > 0 && (flags & FLAG_SHOW_VALUES) != 0) { + // check for vertical pot with 3rd terminal on left + boolean reverseY = (post3.x < lead1.x && lead1.x == lead2.x); + // check for horizontal pot with 3rd terminal on top + boolean reverseX = (post3.y < lead1.y && lead1.x != lead2.x); + // check if we need to swap texts (if leads are reversed, e.g. drawn right to left) + boolean rev = (lead1.x == lead2.x && lead1.y < lead2.y) || (lead1.y == lead2.y && lead1.x > lead2.x); + + // draw units + String s1 = getShortUnitText(rev ? resistance2 : resistance1, ""); + String s2 = getShortUnitText(rev ? resistance1 : resistance2, ""); + g.setFont(unitsFont); + g.setColor(whiteColor); + int ya = (int) g.currentFontSize / 2; + int w; + w = (int) g.context.measureText(s1).getWidth(); + + // vertical? + if (lead1.x == lead2.x) + g.drawString(s1, !reverseY ? arrowPoint.x + 2 : arrowPoint.x - 2 - w, Math.max(arrow1.y, arrow2.y) + 5 + ya); + else + g.drawString(s1, Math.min(arrow1.x, arrow2.x) - 2 - w, !reverseX ? arrowPoint.y + 4 + ya : arrowPoint.y - 4); + + w = (int) g.context.measureText(s2).getWidth(); + if (lead1.x == lead2.x) + g.drawString(s2, !reverseY ? arrowPoint.x + 2 : arrowPoint.x - 2 - w, Math.min(arrow1.y, arrow2.y) - 3); + else + g.drawString(s2, Math.max(arrow1.x, arrow2.x) + 2, !reverseX ? arrowPoint.y + 4 + ya : arrowPoint.y - 4); + } } - + // draw component values (number of resistor ohms, etc). hs = offset void drawValues(Graphics g, String s, Point pt, int hs) { - if (s == null) - return; - g.setFont(unitsFont); - //FontMetrics fm = g.getFontMetrics(); - int w = (int)g.context.measureText(s).getWidth(); - g.setColor(whiteColor); - int ya = (int)g.currentFontSize/2; - int xc = pt.x; - int yc = pt.y; - int dpx = hs; - int dpy = 0; - if (lead1.x != lead2.x) { - dpx = 0; - dpy = -hs; - } - CirSim.console("dv " + dpx + " " + w); - if (dpx == 0) - g.drawString(s, xc-w/2, yc-abs(dpy)-2); - else { - int xx = xc+abs(dpx)+2; - g.drawString(s, xx, yc+dpy+ya); - } + if (s == null) + return; + g.setFont(unitsFont); + //FontMetrics fm = g.getFontMetrics(); + int w = (int) g.context.measureText(s).getWidth(); + g.setColor(whiteColor); + int ya = (int) g.currentFontSize / 2; + int xc = pt.x; + int yc = pt.y; + int dpx = hs; + int dpy = 0; + if (lead1.x != lead2.x) { + dpx = 0; + dpy = -hs; + } + CirSim.console("dv " + dpx + " " + w); + if (dpx == 0) + g.drawString(s, xc - w / 2, yc - abs(dpy) - 2); + else { + int xx = xc + abs(dpx) + 2; + g.drawString(s, xx, yc + dpy + ya); + } } - + void reset() { - curcount1 = curcount2 = curcount3 = 0; - super.reset(); + curcount1 = curcount2 = curcount3 = 0; + super.reset(); } + void calculateCurrent() { - if (resistance1 == 0) - return; // avoid NaN - current1 = (volts[0]-volts[2])/resistance1; - current2 = (volts[1]-volts[2])/resistance2; - current3 = -current1-current2; + if (resistance1 == 0) + return; // avoid NaN + current1 = (volts[0] - volts[2]) / resistance1; + current2 = (volts[1] - volts[2]) / resistance2; + current3 = -current1 - current2; } - - @Override double getCurrentIntoNode(int n) { - if (n == 0) - return -current1; - if (n == 1) - return -current2; - return -current3; + + @Override + double getCurrentIntoNode(int n) { + if (n == 0) + return -current1; + if (n == 1) + return -current2; + return -current3; } - + void stamp() { - resistance1 = maxResistance*position; - resistance2 = maxResistance*(1-position); - sim.stampResistor(nodes[0], nodes[2], resistance1); - sim.stampResistor(nodes[2], nodes[1], resistance2); + resistance1 = maxResistance * position; + resistance2 = maxResistance * (1 - position); + sim.stampResistor(nodes[0], nodes[2], resistance1); + sim.stampResistor(nodes[2], nodes[1], resistance2); } + void getInfo(String arr[]) { - arr[0] = "potentiometer"; - arr[1] = "Vd = " + getVoltageDText(getVoltageDiff()); - arr[2] = "R1 = " + getUnitText(resistance1, Locale.ohmString); - arr[3] = "R2 = " + getUnitText(resistance2, Locale.ohmString); - arr[4] = "I1 = " + getCurrentDText(current1); - arr[5] = "I2 = " + getCurrentDText(current2); + arr[0] = "potentiometer"; + arr[1] = "Vd = " + getVoltageDText(getVoltageDiff()); + arr[2] = "R1 = " + getUnitText(resistance1, Locale.ohmString); + arr[3] = "R2 = " + getUnitText(resistance2, Locale.ohmString); + arr[4] = "I1 = " + getCurrentDText(current1); + arr[5] = "I2 = " + getCurrentDText(current2); } + public EditInfo getEditInfo(int n) { - // ohmString doesn't work here on linux - if (n == 0) - return new EditInfo("Resistance (ohms)", maxResistance, 0, 0); - if (n == 1) { - EditInfo ei = new EditInfo("Slider Text", 0, -1, -1); - ei.text = sliderText; - return ei; - } - if (n == 2) { + // ohmString doesn't work here on linux + if (n == 0) + return new EditInfo("Resistance (ohms)", maxResistance, 0, 0); + if (n == 1) { + EditInfo ei = new EditInfo("Slider Text", 0, -1, -1); + ei.text = sliderText; + return ei; + } + if (n == 2) { EditInfo ei = new EditInfo("", 0, -1, -1); ei.checkbox = new Checkbox("Show Values", (flags & FLAG_SHOW_VALUES) != 0); return ei; - } - return null; + } + return null; } + public void setEditValue(int n, EditInfo ei) { - if (n == 0) - maxResistance = ei.value; - if (n == 1) { - sliderText = ei.textf.getText(); - label.setText(sliderText); - sim.setSlidersPanelHeight(); - } - if (n == 2) - flags = ei.changeFlag(flags, FLAG_SHOW_VALUES); + if (n == 0) + maxResistance = ei.value; + if (n == 1) { + sliderText = ei.textf.getText(); + label.setText(sliderText); + sim.setSlidersPanelHeight(); + } + if (n == 2) + flags = ei.changeFlag(flags, FLAG_SHOW_VALUES); } + void setMouseElm(boolean v) { - super.setMouseElm(v); - if (slider!=null) - slider.draw(); + super.setMouseElm(v); + if (slider != null) + slider.draw(); } - + public void onMouseWheel(MouseWheelEvent e) { - if (slider!=null) - slider.onMouseWheel(e); + if (slider != null) + slider.onMouseWheel(e); } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/ProbeElm.java b/src/main/java/com/lushprojects/circuitjs1/client/ProbeElm.java index 974b151..0d1a863 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/ProbeElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/ProbeElm.java @@ -23,7 +23,7 @@ // much of this was adapted from Bill Collis's code in TestPointElm.java -class ProbeElm extends CircuitElm { +public class ProbeElm extends CircuitElm { static final int FLAG_SHOWVOLTAGE = 1; static final int FLAG_CIRCLE = 2; int meter; @@ -39,257 +39,270 @@ class ProbeElm extends CircuitElm { final int TP_PER = 7; final int TP_PWI = 8; final int TP_DUT = 9; //mark to space ratio - - public ProbeElm(int xx, int yy) { super(xx, yy); - meter = TP_VOL; - - // default for new elements - flags = FLAG_SHOWVOLTAGE | FLAG_CIRCLE; - scale = SCALE_AUTO; + + public ProbeElm(int xx, int yy) { + super(xx, yy); + meter = TP_VOL; + + // default for new elements + flags = FLAG_SHOWVOLTAGE | FLAG_CIRCLE; + scale = SCALE_AUTO; } + public ProbeElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f); - meter = TP_VOL; - scale = SCALE_AUTO; - try { - meter = Integer.parseInt(st.nextToken()); // get meter type from saved dump - scale = Integer.parseInt(st.nextToken()); - } catch (Exception e) {} + StringTokenizer st) { + super(xa, ya, xb, yb, f); + meter = TP_VOL; + scale = SCALE_AUTO; + try { + meter = Integer.parseInt(st.nextToken()); // get meter type from saved dump + scale = Integer.parseInt(st.nextToken()); + } catch (Exception e) { + } } - int getDumpType() { return 'p'; } + + int getDumpType() { + return 'p'; + } + String dump() { return super.dump() + " " + meter + " " + scale; } - String getMeter(){ + + String getMeter() { switch (meter) { - case TP_VOL: - return "V"; - case TP_RMS: - return "V(rms)"; - case TP_MAX: - return "Vmax"; - case TP_MIN: - return "Vmin"; - case TP_P2P: - return "Peak to peak"; - case TP_BIN: - return "Binary"; - case TP_FRQ: - return "Frequency"; - case TP_PER: - return "Period"; - case TP_PWI: - return "Pulse width"; - case TP_DUT: - return "Duty cycle"; + case TP_VOL: + return "V"; + case TP_RMS: + return "V(rms)"; + case TP_MAX: + return "Vmax"; + case TP_MIN: + return "Vmin"; + case TP_P2P: + return "Peak to peak"; + case TP_BIN: + return "Binary"; + case TP_FRQ: + return "Frequency"; + case TP_PER: + return "Period"; + case TP_PWI: + return "Pulse width"; + case TP_DUT: + return "Duty cycle"; } return ""; } - - double rmsV=0, total, count; - double binaryLevel=0;//0 or 1 - double because we only pass doubles back to the web page - int zerocount=0; - double maxV=0, lastMaxV; - double minV=0, lastMinV; - double frequency=0; - double period=0; - double pulseWidth=0; - double dutyCycle=0; - double selectedValue=0; - - boolean increasingV=true, decreasingV=true; + + double rmsV = 0, total, count; + double binaryLevel = 0;//0 or 1 - double because we only pass doubles back to the web page + int zerocount = 0; + double maxV = 0, lastMaxV; + double minV = 0, lastMinV; + double frequency = 0; + double period = 0; + double pulseWidth = 0; + double dutyCycle = 0; + double selectedValue = 0; + + boolean increasingV = true, decreasingV = true; long periodStart, periodLength, pulseStart;//time between consecutive max values Point center; - - void setPoints() { - super.setPoints(); - center = interpPoint(point1, point2, .5); - } - - + + void setPoints() { + super.setPoints(); + center = interpPoint(point1, point2, .5); + } + void draw(Graphics g) { - g.save(); - int hs = (drawAsCircle()) ? circleSize : 8; - setBbox(point1, point2, hs); - boolean selected = needsHighlight(); - double len = (selected || sim.dragElm == this || mustShowVoltage()) ? 16 : dn-32; - if (drawAsCircle()) - len = circleSize*2; - calcLeads((int) len); - setVoltageColor(g, volts[0]); - if (selected) - g.setColor(selectColor); - drawThickLine(g, point1, lead1); - setVoltageColor(g, volts[1]); - if (selected) - g.setColor(selectColor); - drawThickLine(g, lead2, point2); - Font f = new Font("SansSerif", Font.BOLD, 14); - g.setFont(f); - if (this == sim.plotXElm) - drawCenteredText(g, "X", center.x, center.y, true); - if (this == sim.plotYElm) - drawCenteredText(g, "Y", center.x, center.y, true); - if (mustShowVoltage()) { - String s = ""; - switch (meter) { - case TP_VOL: - s = getUnitTextWithScale(getVoltageDiff(),"V", scale); - break; - case TP_RMS: - s = getUnitTextWithScale(rmsV,"V(rms)", scale); - break; - case TP_MAX: - s = getUnitTextWithScale(lastMaxV,"Vpk", scale); - break; - case TP_MIN: - s = getUnitTextWithScale(lastMinV,"Vmin", scale); - break; - case TP_P2P: - s = getUnitTextWithScale(lastMaxV-lastMinV,"Vp2p", scale); - break; - case TP_BIN: - s= binaryLevel + ""; - break; - case TP_FRQ: - s = getUnitText(frequency, "Hz"); - break; - case TP_PER: + g.save(); + int hs = (drawAsCircle()) ? circleSize : 8; + setBbox(point1, point2, hs); + boolean selected = needsHighlight(); + double len = (selected || sim.dragElm == this || mustShowVoltage()) ? 16 : dn - 32; + if (drawAsCircle()) + len = circleSize * 2; + calcLeads((int) len); + setVoltageColor(g, volts[0]); + if (selected) + g.setColor(selectColor); + drawThickLine(g, point1, lead1); + setVoltageColor(g, volts[1]); + if (selected) + g.setColor(selectColor); + drawThickLine(g, lead2, point2); + Font f = new Font("SansSerif", Font.BOLD, 14); + g.setFont(f); + if (this == sim.plotXElm) + drawCenteredText(g, "X", center.x, center.y, true); + if (this == sim.plotYElm) + drawCenteredText(g, "Y", center.x, center.y, true); + if (mustShowVoltage()) { + String s = ""; + switch (meter) { + case TP_VOL: + s = getUnitTextWithScale(getVoltageDiff(), "V", scale); + break; + case TP_RMS: + s = getUnitTextWithScale(rmsV, "V(rms)", scale); + break; + case TP_MAX: + s = getUnitTextWithScale(lastMaxV, "Vpk", scale); + break; + case TP_MIN: + s = getUnitTextWithScale(lastMinV, "Vmin", scale); + break; + case TP_P2P: + s = getUnitTextWithScale(lastMaxV - lastMinV, "Vp2p", scale); + break; + case TP_BIN: + s = binaryLevel + ""; + break; + case TP_FRQ: + s = getUnitText(frequency, "Hz"); + break; + case TP_PER: // s = "percent:"+period + " " + sim.timeStep + " " + sim.simTime + " " + sim.getIterCount(); - break; - case TP_PWI: - s = getUnitText(pulseWidth, "S"); - break; - case TP_DUT: - s = showFormat.format(dutyCycle); - break; - } - drawValues(g, s, drawAsCircle() ? circleSize+3 : 4); - } - g.setColor(whiteColor); - g.setFont(unitsFont); - Point plusPoint = interpPoint(point1, point2, (dn/2-len/2-4)/dn, -10*dsign ); - if (y2 > y) - plusPoint.y += 4; - if (y > y2) - plusPoint.y += 3; - int w = (int)g.context.measureText("+").getWidth(); - g.drawString("+", plusPoint.x-w/2, plusPoint.y); - if (drawAsCircle()) { - g.setColor(lightGrayColor); - drawThickCircle(g, center.x, center.y, circleSize); - drawCenteredText(g, "V", center.x, center.y, true); - } - drawPosts(g); - g.restore(); + break; + case TP_PWI: + s = getUnitText(pulseWidth, "S"); + break; + case TP_DUT: + s = showFormat.format(dutyCycle); + break; + } + drawValues(g, s, drawAsCircle() ? circleSize + 3 : 4); + } + g.setColor(whiteColor); + g.setFont(unitsFont); + Point plusPoint = interpPoint(point1, point2, (dn / 2 - len / 2 - 4) / dn, -10 * dsign); + if (y2 > y) + plusPoint.y += 4; + if (y > y2) + plusPoint.y += 3; + int w = (int) g.context.measureText("+").getWidth(); + g.drawString("+", plusPoint.x - w / 2, plusPoint.y); + if (drawAsCircle()) { + g.setColor(lightGrayColor); + drawThickCircle(g, center.x, center.y, circleSize); + drawCenteredText(g, "V", center.x, center.y, true); + } + drawPosts(g); + g.restore(); } final int circleSize = 12; - + boolean mustShowVoltage() { - return (flags & FLAG_SHOWVOLTAGE) != 0; + return (flags & FLAG_SHOWVOLTAGE) != 0; } - boolean drawAsCircle() { return (flags & FLAG_CIRCLE) != 0; } - - void stepFinished(){ + + boolean drawAsCircle() { + return (flags & FLAG_CIRCLE) != 0; + } + + void stepFinished() { count++;//how many counts are in a cycle double v = getVoltageDiff(); - total += v*v; //sum of squares + total += v * v; //sum of squares - if (v<2.5) + if (v < 2.5) binaryLevel = 0; else binaryLevel = 1; - - + + //V going up, track maximum value with - if (v>maxV && increasingV){ + if (v > maxV && increasingV) { maxV = v; increasingV = true; decreasingV = false; } - if (vminV && decreasingV){ //change of direction V now going up - lastMinV=minV; //capture last minimum - pulseStart = System.currentTimeMillis(); + if (v > minV && decreasingV) { //change of direction V now going up + lastMinV = minV; //capture last minimum + pulseStart = System.currentTimeMillis(); maxV = v; increasingV = true; decreasingV = false; - + //rms data - total = total/count; + total = total / count; rmsV = Math.sqrt(total); if (Double.isNaN(rmsV)) - rmsV=0; - count=0; - total=0; + rmsV = 0; + count = 0; + total = 0; + - } //need to zero the rms value if it stays at 0 for a while - if (v==0){ + if (v == 0) { zerocount++; - if (zerocount > 5){ - total=0; - rmsV=0; - maxV=0; - minV=0; + if (zerocount > 5) { + total = 0; + rmsV = 0; + maxV = 0; + minV = 0; } - }else{ - zerocount=0; + } else { + zerocount = 0; } } void getInfo(String arr[]) { - arr[0] = "voltmeter"; - arr[1] = "Vd = " + getVoltageText(getVoltageDiff()); + arr[0] = "voltmeter"; + arr[1] = "Vd = " + getVoltageText(getVoltageDiff()); + } + + boolean getConnection(int n1, int n2) { + return false; } - boolean getConnection(int n1, int n2) { return false; } public EditInfo getEditInfo(int n) { - if (n == 0) { - EditInfo ei = new EditInfo("", 0, -1, -1); - ei.checkbox = new Checkbox("Show Value", mustShowVoltage()); - return ei; - } - if (n==1){ - EditInfo ei = new EditInfo("Value", selectedValue, -1, -1); + if (n == 0) { + EditInfo ei = new EditInfo("", 0, -1, -1); + ei.checkbox = new Checkbox("Show Value", mustShowVoltage()); + return ei; + } + if (n == 1) { + EditInfo ei = new EditInfo("Value", selectedValue, -1, -1); ei.choice = new Choice(); ei.choice.add("Voltage"); ei.choice.add("RMS Voltage"); ei.choice.add("Max Voltage"); ei.choice.add("Min Voltage"); ei.choice.add("P2P Voltage"); - ei.choice.add("Binary Value"); + ei.choice.add("Binary Value"); //ei.choice.add("Frequency"); //ei.choice.add("Period"); //ei.choice.add("Pulse Width"); @@ -298,7 +311,7 @@ public EditInfo getEditInfo(int n) { return ei; } if (n == 2) { - EditInfo ei = new EditInfo("Scale", 0); + EditInfo ei = new EditInfo("Scale", 0); ei.choice = new Choice(); ei.choice.add("Auto"); ei.choice.add("V"); @@ -316,13 +329,13 @@ public EditInfo getEditInfo(int n) { } public void setEditValue(int n, EditInfo ei) { - if (n == 0) { - if (ei.checkbox.getState()) - flags = FLAG_SHOWVOLTAGE; - else - flags &= ~FLAG_SHOWVOLTAGE; - } - if (n==1){ + if (n == 0) { + if (ei.checkbox.getState()) + flags = FLAG_SHOWVOLTAGE; + else + flags &= ~FLAG_SHOWVOLTAGE; + } + if (n == 1) { meter = ei.choice.getSelectedIndex(); } if (n == 2) diff --git a/src/main/java/com/lushprojects/circuitjs1/client/PushSwitchElm.java b/src/main/java/com/lushprojects/circuitjs1/client/PushSwitchElm.java index 39ce5ea..75aff33 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/PushSwitchElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/PushSwitchElm.java @@ -19,8 +19,16 @@ package com.lushprojects.circuitjs1.client; -class PushSwitchElm extends SwitchElm { - public PushSwitchElm(int xx, int yy) { super(xx, yy, true); } - Class getDumpClass() { return SwitchElm.class; } - int getShortcut() { return 0; } +public class PushSwitchElm extends SwitchElm { + public PushSwitchElm(int xx, int yy) { + super(xx, yy, true); } + + Class getDumpClass() { + return SwitchElm.class; + } + + int getShortcut() { + return 0; + } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/QueryParameters.java b/src/main/java/com/lushprojects/circuitjs1/client/QueryParameters.java index 6114df1..4cc1da8 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/QueryParameters.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/QueryParameters.java @@ -20,42 +20,37 @@ package com.lushprojects.circuitjs1.client; import java.util.HashMap; + import com.google.gwt.http.client.URL; -public class QueryParameters -{ +public class QueryParameters { private HashMap map = new HashMap(); - public QueryParameters() - { + public QueryParameters() { String search = getQueryString(); - if ((search != null) && (search.length() > 0)) - { + if ((search != null) && (search.length() > 0)) { String[] nameValues = search.substring(1).split("&"); - for (int i = 0; i < nameValues.length; i++) - { + for (int i = 0; i < nameValues.length; i++) { String[] pair = nameValues[i].split("="); map.put(pair[0], URL.decode(pair[1])); } } } - - public String getValue(String key) - { + + public String getValue(String key) { return (String) map.get(key); } - - - public boolean getBooleanValue(String key, boolean def){ - String val=getValue(key); - if (val==null) - return def; - else - return (val=="1" || val.equalsIgnoreCase("true")); + + public boolean getBooleanValue(String key, boolean def) { + String val = getValue(key); + if (val == null) + return def; + else + return (val == "1" || val.equalsIgnoreCase("true")); } - + private native String getQueryString() /*-{ return $wnd.location.search; diff --git a/src/main/java/com/lushprojects/circuitjs1/client/RailElm.java b/src/main/java/com/lushprojects/circuitjs1/client/RailElm.java index 6647036..9dbb502 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/RailElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/RailElm.java @@ -19,93 +19,110 @@ package com.lushprojects.circuitjs1.client; -class RailElm extends VoltageElm { - public RailElm(int xx, int yy) { - super(xx, yy, WF_DC); +public class RailElm extends VoltageElm { + public RailElm(int xx, int yy) { + super(xx, yy, WF_DC); - } - RailElm(int xx, int yy, int wf) { - super(xx, yy, wf); - } + } + + RailElm(int xx, int yy, int wf) { + super(xx, yy, wf); + } + + public RailElm(int xa, int ya, int xb, int yb, int f, + StringTokenizer st) { + super(xa, ya, xb, yb, f, st); + } - public RailElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f, st); - } - final int FLAG_CLOCK = 1; - int getDumpType() { return 'R'; } - int getPostCount() { return 1; } - + + int getDumpType() { + return 'R'; + } + + int getPostCount() { + return 1; + } + void setPoints() { - super.setPoints(); - lead1 = interpPoint(point1, point2, 1-circleSize/dn); + super.setPoints(); + lead1 = interpPoint(point1, point2, 1 - circleSize / dn); } - + String getRailText() { - return null; + return null; } - + void draw(Graphics g) { - String rt = getRailText(); - double w = rt == null ? circleSize : g.context.measureText(rt).getWidth()/2; - if (w > dn*.8) - w = dn*.8; - lead1 = interpPoint(point1, point2, 1-w/dn); - setBbox(point1, point2, circleSize); - setVoltageColor(g, volts[0]); - drawThickLine(g, point1, lead1); - drawRail(g); - drawPosts(g); - curcount = updateDotCount(-current, curcount); - if (sim.dragElm != this) - drawDots(g, point1, lead1, curcount); + String rt = getRailText(); + double w = rt == null ? circleSize : g.context.measureText(rt).getWidth() / 2; + if (w > dn * .8) + w = dn * .8; + lead1 = interpPoint(point1, point2, 1 - w / dn); + setBbox(point1, point2, circleSize); + setVoltageColor(g, volts[0]); + drawThickLine(g, point1, lead1); + drawRail(g); + drawPosts(g); + curcount = updateDotCount(-current, curcount); + if (sim.dragElm != this) + drawDots(g, point1, lead1, curcount); } void drawRail(Graphics g) { - if (waveform == WF_SQUARE && (flags & FLAG_CLOCK) != 0) - drawRailText(g, "CLK"); - else if (waveform == WF_DC || waveform == WF_VAR) { - g.setColor(needsHighlight() ? selectColor : whiteColor); - setPowerColor(g, false); - double v = getVoltage(); - String s; - if (Math.abs(v) < 1) - s = showFormat.format(v)+" V"; - else - s = getShortUnitText(v, "V"); - if (getVoltage() > 0) - s = "+" + s; - drawLabeledNode(g, s, point1, lead1); - } else { - drawWaveform(g, point2); - } + if (waveform == WF_SQUARE && (flags & FLAG_CLOCK) != 0) + drawRailText(g, "CLK"); + else if (waveform == WF_DC || waveform == WF_VAR) { + g.setColor(needsHighlight() ? selectColor : whiteColor); + setPowerColor(g, false); + double v = getVoltage(); + String s; + if (Math.abs(v) < 1) + s = showFormat.format(v) + " V"; + else + s = getShortUnitText(v, "V"); + if (getVoltage() > 0) + s = "+" + s; + drawLabeledNode(g, s, point1, lead1); + } else { + drawWaveform(g, point2); + } } - + void drawRailText(Graphics g, String s) { - g.setColor(needsHighlight() ? selectColor : whiteColor); - setPowerColor(g, false); - drawLabeledNode(g, s, point1, lead1); + g.setColor(needsHighlight() ? selectColor : whiteColor); + setPowerColor(g, false); + drawLabeledNode(g, s, point1, lead1); } - - double getVoltageDiff() { return volts[0]; } + + double getVoltageDiff() { + return volts[0]; + } + void stamp() { - if (waveform == WF_DC) - sim.stampVoltageSource(0, nodes[0], voltSource, getVoltage()); - else - sim.stampVoltageSource(0, nodes[0], voltSource); + if (waveform == WF_DC) + sim.stampVoltageSource(0, nodes[0], voltSource, getVoltage()); + else + sim.stampVoltageSource(0, nodes[0], voltSource); } + void doStep() { - if (waveform != WF_DC) - sim.updateVoltageSource(0, nodes[0], voltSource, getVoltage()); + if (waveform != WF_DC) + sim.updateVoltageSource(0, nodes[0], voltSource, getVoltage()); } - boolean hasGroundConnection(int n1) { return true; } - int getShortcut() { return 'V'; } - + + boolean hasGroundConnection(int n1) { + return true; + } + + int getShortcut() { + return 'V'; + } + // void drawHandles(Graphics g, Color c) { // g.setColor(c); // g.fillRect(x-3, y-3, 7, 7); // } - + } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/Rectangle.java b/src/main/java/com/lushprojects/circuitjs1/client/Rectangle.java index 94109d0..d3099fa 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/Rectangle.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/Rectangle.java @@ -19,48 +19,48 @@ // Via http://grepcode.com/file_/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/awt/Rectangle.java/?v=source public class Rectangle { - int x; - int y; - int width; - int height; - - public Rectangle(){ - x=0; - y=0; - width=0; - height=0; - } - + int x; + int y; + int width; + int height; + + public Rectangle() { + x = 0; + y = 0; + width = 0; + height = 0; + } + public Rectangle(int x, int y, int width, int height) { this.x = x; this.y = y; this.width = width; this.height = height; } - + public Rectangle(Point pt) { - this.x = pt.x; - this.y = pt.y; - this.width = 0; - this.height = 0; + this.x = pt.x; + this.y = pt.y; + this.width = 0; + this.height = 0; } - + public Rectangle(Rectangle r) { this(r.x, r.y, r.width, r.height); } - + public void setBounds(int x, int y, int width, int height) { this.x = x; this.y = y; this.width = width; this.height = height; } - + public void translate(int dx, int dy) { x += dx; y += dy; } - + public boolean contains(int X, int Y) { int w = this.width; int h = this.height; @@ -87,7 +87,7 @@ public void move(int x, int y) { this.y = y; } */ - + public boolean intersects(Rectangle r) { int tw = this.width; int th = this.height; @@ -110,7 +110,7 @@ public boolean intersects(Rectangle r) { (tw < tx || tw > rx) && (th < ty || th > ry)); } - + public Rectangle union(Rectangle r) { long tx2 = this.width; long ty2 = this.height; @@ -149,13 +149,15 @@ public Rectangle union(Rectangle r) { if (ty2 > Integer.MAX_VALUE) ty2 = Integer.MAX_VALUE; return new Rectangle(tx1, ty1, (int) tx2, (int) ty2); } - - public String toString() { return "Rect(" + x + "," + y + "," + width + "," + height + ")"; } - + public String toString() { + return "Rect(" + x + "," + y + "," + width + "," + height + ")"; + } + + public boolean equals(Object obj) { if (obj instanceof Rectangle) { - Rectangle r = (Rectangle)obj; + Rectangle r = (Rectangle) obj; return ((x == r.x) && (y == r.y) && (width == r.width) && diff --git a/src/main/java/com/lushprojects/circuitjs1/client/Relay2Elm.java b/src/main/java/com/lushprojects/circuitjs1/client/Relay2Elm.java index c6e2883..f2162d2 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/Relay2Elm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/Relay2Elm.java @@ -30,7 +30,7 @@ // 3n+1 = coil // 3n+2 = end of coil resistor -class Relay2Elm extends CircuitElm { +public class Relay2Elm extends CircuitElm { final int FLAG_SWAP_COIL = 1; final int FLAG_SHOW_BOX = 2; final int FLAG_BOTH_SIDES_COIL = 4; @@ -87,7 +87,7 @@ public Relay2Elm(int xx, int yy) { } public Relay2Elm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { + StringTokenizer st) { super(xa, ya, xb, yb, f); poleCount = new Integer(st.nextToken()).intValue(); inductance = new Double(st.nextToken()).doubleValue(); @@ -336,7 +336,7 @@ void reset() { int i; for (i = 0; i != poleCount; i++) switchCurrent[i] = switchCurCount[i] = 0; - f_position= d_position = i_position = 0; + f_position = d_position = i_position = 0; // preserve onState because if we don't, Relay Flip-Flop gets left in a weird state on reset. // onState = false; @@ -398,7 +398,7 @@ void startIteration() { } else { f_position -= sim.timeStep / switchingTime; if (f_position <= 0) - f_position= d_position = i_position = 0; + f_position = d_position = i_position = 0; } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/RelayElm.java b/src/main/java/com/lushprojects/circuitjs1/client/RelayElm.java index ba26ff2..edaca61 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/RelayElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/RelayElm.java @@ -30,11 +30,11 @@ // 3n+1 = coil // 3n+2 = end of coil resistor -class RelayElm extends CircuitElm { - final int FLAG_SWAP_COIL = 1; - final int FLAG_SHOW_BOX = 2; - final int FLAG_BOTH_SIDES_COIL = 4; - +public class RelayElm extends CircuitElm { + final int FLAG_SWAP_COIL = 1; + final int FLAG_SHOW_BOX = 2; + final int FLAG_BOTH_SIDES_COIL = 4; + double inductance; Inductor ind; double r_on, r_off, onCurrent, offCurrent; @@ -42,18 +42,18 @@ class RelayElm extends CircuitElm { Point lines[]; Point outline[] = newPointArray(4); double coilCurrent, switchCurrent[], coilCurCount, switchCurCount[]; - + // fractional position, between 0 and 1 inclusive double d_position; - + // integer position, can be 0 (off), 1 (on), 2 (in between) int i_position; - + double coilR; - + // time to switch in seconds, or 0 for old model where switching time was not constant double switchingTime; - + int poleCount; int openhs; boolean onState; @@ -62,443 +62,463 @@ class RelayElm extends CircuitElm { final int nSwitch2 = 2; int nCoil1, nCoil2, nCoil3; double currentOffset1, currentOffset2; - + public RelayElm(int xx, int yy) { - super(xx, yy); - ind = new Inductor(sim); - inductance = .2; - ind.setup(inductance, 0, Inductor.FLAG_BACK_EULER); - noDiagonal = true; - onCurrent = .02; - offCurrent = .015; - r_on = .05; - r_off = 1e6; - coilR = 20; - switchingTime = 5e-3; - coilCurrent = coilCurCount = 0; - poleCount = 1; - flags |= FLAG_SHOW_BOX | FLAG_BOTH_SIDES_COIL; - setupPoles(); + super(xx, yy); + ind = new Inductor(sim); + inductance = .2; + ind.setup(inductance, 0, Inductor.FLAG_BACK_EULER); + noDiagonal = true; + onCurrent = .02; + offCurrent = .015; + r_on = .05; + r_off = 1e6; + coilR = 20; + switchingTime = 5e-3; + coilCurrent = coilCurCount = 0; + poleCount = 1; + flags |= FLAG_SHOW_BOX | FLAG_BOTH_SIDES_COIL; + setupPoles(); } + public RelayElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f); - poleCount = new Integer(st.nextToken()).intValue(); - inductance = new Double(st.nextToken()).doubleValue(); - coilCurrent = new Double(st.nextToken()).doubleValue(); - r_on = new Double(st.nextToken()).doubleValue(); - r_off = new Double(st.nextToken()).doubleValue(); - onCurrent = new Double(st.nextToken()).doubleValue(); - coilR = new Double(st.nextToken()).doubleValue(); - try { - offCurrent = onCurrent; - switchingTime = 0; - offCurrent = new Double(st.nextToken()).doubleValue(); - switchingTime = Double.parseDouble(st.nextToken()); - d_position = i_position = Integer.parseInt(st.nextToken()); - } catch (Exception e) {} - if (i_position == 1) - onState = true; - // intermediate state? - if (i_position == 2) - d_position = .5; - noDiagonal = true; - ind = new Inductor(sim); - ind.setup(inductance, coilCurrent, Inductor.FLAG_BACK_EULER); - setupPoles(); + StringTokenizer st) { + super(xa, ya, xb, yb, f); + poleCount = new Integer(st.nextToken()).intValue(); + inductance = new Double(st.nextToken()).doubleValue(); + coilCurrent = new Double(st.nextToken()).doubleValue(); + r_on = new Double(st.nextToken()).doubleValue(); + r_off = new Double(st.nextToken()).doubleValue(); + onCurrent = new Double(st.nextToken()).doubleValue(); + coilR = new Double(st.nextToken()).doubleValue(); + try { + offCurrent = onCurrent; + switchingTime = 0; + offCurrent = new Double(st.nextToken()).doubleValue(); + switchingTime = Double.parseDouble(st.nextToken()); + d_position = i_position = Integer.parseInt(st.nextToken()); + } catch (Exception e) { + } + if (i_position == 1) + onState = true; + // intermediate state? + if (i_position == 2) + d_position = .5; + noDiagonal = true; + ind = new Inductor(sim); + ind.setup(inductance, coilCurrent, Inductor.FLAG_BACK_EULER); + setupPoles(); allocNodes(); } - + void setupPoles() { - nCoil1 = 3*poleCount; - nCoil2 = nCoil1+1; - nCoil3 = nCoil1+2; - if (switchCurrent == null || switchCurrent.length != poleCount) { - switchCurrent = new double[poleCount]; - switchCurCount = new double[poleCount]; - } + nCoil1 = 3 * poleCount; + nCoil2 = nCoil1 + 1; + nCoil3 = nCoil1 + 2; + if (switchCurrent == null || switchCurrent.length != poleCount) { + switchCurrent = new double[poleCount]; + switchCurCount = new double[poleCount]; + } } - - int getDumpType() { return 178; } - + + int getDumpType() { + return 178; + } + String dump() { - return super.dump() + " " + poleCount + " " + - inductance + " " + coilCurrent + " " + - r_on + " " + r_off + " " + onCurrent + " " + coilR + " " + offCurrent + " " + switchingTime + " " + i_position; + return super.dump() + " " + poleCount + " " + + inductance + " " + coilCurrent + " " + + r_on + " " + r_off + " " + onCurrent + " " + coilR + " " + offCurrent + " " + switchingTime + " " + i_position; } - + void draw(Graphics g) { - int i, p; - for (i = 0; i != 2; i++) { - setVoltageColor(g, volts[nCoil1+i]); - drawThickLine(g, coilLeads[i], coilPosts[i]); - } - int x = ((flags & FLAG_SWAP_COIL) != 0) ? 1 : 0; - setPowerColor(g, coilCurrent * (volts[nCoil1]-volts[nCoil2])); - drawCoil(g, dsign*6, coilLeads[x], coilLeads[1-x], - volts[nCoil1+x], volts[nCoil2-x]); - - // draw rectangle - if ((flags & FLAG_SHOW_BOX) != 0) { - g.setColor(needsHighlight() ? selectColor : lightGrayColor); - drawThickLine(g, outline[0], outline[1]); - drawThickLine(g, outline[1], outline[2]); - drawThickLine(g, outline[2], outline[3]); - drawThickLine(g, outline[3], outline[0]); - } - - // draw lines - g.setColor(Color.darkGray); - for (i = 0; i != poleCount; i++) { - if (i == 0) { - int off = ((flags & FLAG_BOTH_SIDES_COIL) == 0) ? 0 : 4; - interpPoint(point1, point2, lines[i*2 ], .5, - openhs*2+5*dsign-i*openhs*3+off); - } else - interpPoint(point1, point2, lines[i*2], .5, - (int) (openhs*(-i*3+3-.5+d_position))+5*dsign); - interpPoint(point1, point2, lines[i*2+1], .5, - (int) (openhs*(-i*3-.5+d_position))-5*dsign); - g.setLineDash(4, 4); - g.drawLine(lines[i*2].x, lines[i*2].y, lines[i*2+1].x, lines[i*2+1].y); - g.setLineDash(0, 0); - } - - for (p = 0; p != poleCount; p++) { - int po = p*3; - for (i = 0; i != 3; i++) { - // draw lead - setVoltageColor(g, volts[nSwitch0+po+i]); - drawThickLine(g, swposts[p][i], swpoles[p][i]); - } - - interpPoint(swpoles[p][1], swpoles[p][2], ptSwitch[p], d_position); - //setVoltageColor(g, volts[nSwitch0]); - g.setColor(Color.lightGray); - drawThickLine(g, swpoles[p][0], ptSwitch[p]); - switchCurCount[p] = updateDotCount(switchCurrent[p], - switchCurCount[p]); - drawDots(g, swposts[p][0], swpoles[p][0], switchCurCount[p]); - - if (i_position != 2) - drawDots(g, swpoles[p][i_position+1], swposts[p][i_position+1], - switchCurCount[p]); - } - - coilCurCount = updateDotCount(coilCurrent, coilCurCount); - - if (coilCurCount != 0) { - drawDots(g, coilPosts[0], coilLeads[0], coilCurCount); - drawDots(g, coilLeads[0], coilLeads[1], addCurCount(coilCurCount, currentOffset1)); - drawDots(g, coilLeads[1], coilPosts[1], addCurCount(coilCurCount, currentOffset2)); - } - - drawPosts(g); - setBbox(outline[0], outline[2], 0); - adjustBbox(coilPosts[0], coilPosts[1]); - adjustBbox(swposts[0][0], swposts[0][1]); + int i, p; + for (i = 0; i != 2; i++) { + setVoltageColor(g, volts[nCoil1 + i]); + drawThickLine(g, coilLeads[i], coilPosts[i]); + } + int x = ((flags & FLAG_SWAP_COIL) != 0) ? 1 : 0; + setPowerColor(g, coilCurrent * (volts[nCoil1] - volts[nCoil2])); + drawCoil(g, dsign * 6, coilLeads[x], coilLeads[1 - x], + volts[nCoil1 + x], volts[nCoil2 - x]); + + // draw rectangle + if ((flags & FLAG_SHOW_BOX) != 0) { + g.setColor(needsHighlight() ? selectColor : lightGrayColor); + drawThickLine(g, outline[0], outline[1]); + drawThickLine(g, outline[1], outline[2]); + drawThickLine(g, outline[2], outline[3]); + drawThickLine(g, outline[3], outline[0]); + } + + // draw lines + g.setColor(Color.darkGray); + for (i = 0; i != poleCount; i++) { + if (i == 0) { + int off = ((flags & FLAG_BOTH_SIDES_COIL) == 0) ? 0 : 4; + interpPoint(point1, point2, lines[i * 2], .5, + openhs * 2 + 5 * dsign - i * openhs * 3 + off); + } else + interpPoint(point1, point2, lines[i * 2], .5, + (int) (openhs * (-i * 3 + 3 - .5 + d_position)) + 5 * dsign); + interpPoint(point1, point2, lines[i * 2 + 1], .5, + (int) (openhs * (-i * 3 - .5 + d_position)) - 5 * dsign); + g.setLineDash(4, 4); + g.drawLine(lines[i * 2].x, lines[i * 2].y, lines[i * 2 + 1].x, lines[i * 2 + 1].y); + g.setLineDash(0, 0); + } + + for (p = 0; p != poleCount; p++) { + int po = p * 3; + for (i = 0; i != 3; i++) { + // draw lead + setVoltageColor(g, volts[nSwitch0 + po + i]); + drawThickLine(g, swposts[p][i], swpoles[p][i]); + } + + interpPoint(swpoles[p][1], swpoles[p][2], ptSwitch[p], d_position); + //setVoltageColor(g, volts[nSwitch0]); + g.setColor(Color.lightGray); + drawThickLine(g, swpoles[p][0], ptSwitch[p]); + switchCurCount[p] = updateDotCount(switchCurrent[p], + switchCurCount[p]); + drawDots(g, swposts[p][0], swpoles[p][0], switchCurCount[p]); + + if (i_position != 2) + drawDots(g, swpoles[p][i_position + 1], swposts[p][i_position + 1], + switchCurCount[p]); + } + + coilCurCount = updateDotCount(coilCurrent, coilCurCount); + + if (coilCurCount != 0) { + drawDots(g, coilPosts[0], coilLeads[0], coilCurCount); + drawDots(g, coilLeads[0], coilLeads[1], addCurCount(coilCurCount, currentOffset1)); + drawDots(g, coilLeads[1], coilPosts[1], addCurCount(coilCurCount, currentOffset2)); + } + + drawPosts(g); + setBbox(outline[0], outline[2], 0); + adjustBbox(coilPosts[0], coilPosts[1]); + adjustBbox(swposts[0][0], swposts[0][1]); } - + double getCurrentIntoNode(int n) { - if (n < 3*poleCount) { - int p = n/3; - int k = n%3; - if (k == 0) - return -switchCurrent[p]; - if (k == 1+i_position) - return switchCurrent[p]; - return 0; - } - if (n == 3*poleCount) - return -coilCurrent; - return coilCurrent; + if (n < 3 * poleCount) { + int p = n / 3; + int k = n % 3; + if (k == 0) + return -switchCurrent[p]; + if (k == 1 + i_position) + return switchCurrent[p]; + return 0; + } + if (n == 3 * poleCount) + return -coilCurrent; + return coilCurrent; } void setPoints() { - super.setPoints(); - setupPoles(); - allocNodes(); - openhs = -dsign*16; - - // switch - calcLeads(32); - swposts = new Point[poleCount][3]; - swpoles = new Point[poleCount][3]; - int i, j; - for (i = 0; i != poleCount; i++) { - for (j = 0; j != 3; j++) { - swposts[i][j] = new Point(); - swpoles[i][j] = new Point(); - } - interpPoint(lead1, lead2, swpoles[i][0], 0, -openhs*3*i); - interpPoint(lead1, lead2, swpoles[i][1], 1, -openhs*3*i-openhs); - interpPoint(lead1, lead2, swpoles[i][2], 1, -openhs*3*i+openhs); - interpPoint(point1, point2, swposts[i][0], 0, -openhs*3*i); - interpPoint(point1, point2, swposts[i][1], 1, -openhs*3*i-openhs); - interpPoint(point1, point2, swposts[i][2], 1, -openhs*3*i+openhs); - } - - // coil - coilPosts = newPointArray(2); - coilLeads = newPointArray(2); - ptSwitch = newPointArray(poleCount); - - int x = ((flags & FLAG_SWAP_COIL) != 0) ? 1 : 0; - int boxSize; - if ((flags & FLAG_BOTH_SIDES_COIL) == 0) { - interpPoint(point1, point2, coilPosts[0], x, openhs*2); - interpPoint(point1, point2, coilPosts[1], x, openhs*3); - interpPoint(point1, point2, coilLeads[0], .5, openhs*2); - interpPoint(point1, point2, coilLeads[1], .5, openhs*3); - boxSize = 56; - } else { - interpPoint(point1, point2, coilPosts[0], 0, openhs*2); - interpPoint(point1, point2, coilPosts[1], 1, openhs*2); - interpPoint(point1, point2, coilLeads[0], .5-16/dn, openhs*2); - interpPoint(point1, point2, coilLeads[1], .5+16/dn, openhs*2); - boxSize = 40; - } - - // lines - lines = newPointArray(poleCount*2); - - // outline - double boxWScale = Math.min(0.4, 25.0 / dn); - interpPoint(point1, point2, outline[0], 0.5 - boxWScale, -boxSize * dsign); - interpPoint(point1, point2, outline[1], 0.5 + boxWScale, -boxSize * dsign); - interpPoint(point1, point2, outline[2], 0.5 + boxWScale, -(openhs*3*poleCount) - (24.0 * dsign)); - interpPoint(point1, point2, outline[3], 0.5 - boxWScale, -(openhs*3*poleCount) - (24.0 * dsign)); - - currentOffset1 = distance(coilPosts[0], coilLeads[0]); - currentOffset2 = currentOffset1 + distance(coilLeads[0], coilLeads[1]); + super.setPoints(); + setupPoles(); + allocNodes(); + openhs = -dsign * 16; + + // switch + calcLeads(32); + swposts = new Point[poleCount][3]; + swpoles = new Point[poleCount][3]; + int i, j; + for (i = 0; i != poleCount; i++) { + for (j = 0; j != 3; j++) { + swposts[i][j] = new Point(); + swpoles[i][j] = new Point(); + } + interpPoint(lead1, lead2, swpoles[i][0], 0, -openhs * 3 * i); + interpPoint(lead1, lead2, swpoles[i][1], 1, -openhs * 3 * i - openhs); + interpPoint(lead1, lead2, swpoles[i][2], 1, -openhs * 3 * i + openhs); + interpPoint(point1, point2, swposts[i][0], 0, -openhs * 3 * i); + interpPoint(point1, point2, swposts[i][1], 1, -openhs * 3 * i - openhs); + interpPoint(point1, point2, swposts[i][2], 1, -openhs * 3 * i + openhs); + } + + // coil + coilPosts = newPointArray(2); + coilLeads = newPointArray(2); + ptSwitch = newPointArray(poleCount); + + int x = ((flags & FLAG_SWAP_COIL) != 0) ? 1 : 0; + int boxSize; + if ((flags & FLAG_BOTH_SIDES_COIL) == 0) { + interpPoint(point1, point2, coilPosts[0], x, openhs * 2); + interpPoint(point1, point2, coilPosts[1], x, openhs * 3); + interpPoint(point1, point2, coilLeads[0], .5, openhs * 2); + interpPoint(point1, point2, coilLeads[1], .5, openhs * 3); + boxSize = 56; + } else { + interpPoint(point1, point2, coilPosts[0], 0, openhs * 2); + interpPoint(point1, point2, coilPosts[1], 1, openhs * 2); + interpPoint(point1, point2, coilLeads[0], .5 - 16 / dn, openhs * 2); + interpPoint(point1, point2, coilLeads[1], .5 + 16 / dn, openhs * 2); + boxSize = 40; + } + + // lines + lines = newPointArray(poleCount * 2); + + // outline + double boxWScale = Math.min(0.4, 25.0 / dn); + interpPoint(point1, point2, outline[0], 0.5 - boxWScale, -boxSize * dsign); + interpPoint(point1, point2, outline[1], 0.5 + boxWScale, -boxSize * dsign); + interpPoint(point1, point2, outline[2], 0.5 + boxWScale, -(openhs * 3 * poleCount) - (24.0 * dsign)); + interpPoint(point1, point2, outline[3], 0.5 - boxWScale, -(openhs * 3 * poleCount) - (24.0 * dsign)); + + currentOffset1 = distance(coilPosts[0], coilLeads[0]); + currentOffset2 = currentOffset1 + distance(coilLeads[0], coilLeads[1]); } - + Point getPost(int n) { - if (n < 3*poleCount) - return swposts[n / 3][n % 3]; - return coilPosts[n-3*poleCount]; + if (n < 3 * poleCount) + return swposts[n / 3][n % 3]; + return coilPosts[n - 3 * poleCount]; + } + + int getPostCount() { + return 2 + poleCount * 3; + } + + int getInternalNodeCount() { + return 1; } - int getPostCount() { return 2+poleCount*3; } - int getInternalNodeCount() { return 1; } + void reset() { - super.reset(); - ind.reset(); - coilCurrent = coilCurCount = 0; - int i; - for (i = 0; i != poleCount; i++) - switchCurrent[i] = switchCurCount[i] = 0; - d_position = i_position = 0; - - // preserve onState because if we don't, Relay Flip-Flop gets left in a weird state on reset. - // onState = false; + super.reset(); + ind.reset(); + coilCurrent = coilCurCount = 0; + int i; + for (i = 0; i != poleCount; i++) + switchCurrent[i] = switchCurCount[i] = 0; + d_position = i_position = 0; + + // preserve onState because if we don't, Relay Flip-Flop gets left in a weird state on reset. + // onState = false; } + double a1, a2, a3, a4; + void stamp() { - // inductor from coil post 1 to internal node - ind.stamp(nodes[nCoil1], nodes[nCoil3]); - // resistor from internal node to coil post 2 - sim.stampResistor(nodes[nCoil3], nodes[nCoil2], coilR); - - int i; - for (i = 0; i != poleCount*3; i++) - sim.stampNonLinear(nodes[nSwitch0+i]); + // inductor from coil post 1 to internal node + ind.stamp(nodes[nCoil1], nodes[nCoil3]); + // resistor from internal node to coil post 2 + sim.stampResistor(nodes[nCoil3], nodes[nCoil2], coilR); + + int i; + for (i = 0; i != poleCount * 3; i++) + sim.stampNonLinear(nodes[nSwitch0 + i]); } - + void startIteration() { - // using old model? - if (switchingTime == 0) { - startIterationOld(); - return; - } - ind.startIteration(volts[nCoil1]-volts[nCoil3]); - double absCurrent = Math.abs(coilCurrent); - - if (onState) { - // on or turning on. check if we need to turn off - if (absCurrent < offCurrent) { - // turning off, set switch to intermediate position - onState = false; - i_position = 2; - } else { - d_position += sim.timeStep/switchingTime; - if (d_position >= 1) - d_position = i_position = 1; - } - } else { - // off or turning off. check if we need to turn on - if (absCurrent > onCurrent) { - // turning on, set switch to intermediate position - onState = true; - i_position = 2; - } else { - d_position -= sim.timeStep/switchingTime; - if (d_position <= 0) - d_position = i_position = 0; - } - - } + // using old model? + if (switchingTime == 0) { + startIterationOld(); + return; + } + ind.startIteration(volts[nCoil1] - volts[nCoil3]); + double absCurrent = Math.abs(coilCurrent); + + if (onState) { + // on or turning on. check if we need to turn off + if (absCurrent < offCurrent) { + // turning off, set switch to intermediate position + onState = false; + i_position = 2; + } else { + d_position += sim.timeStep / switchingTime; + if (d_position >= 1) + d_position = i_position = 1; + } + } else { + // off or turning off. check if we need to turn on + if (absCurrent > onCurrent) { + // turning on, set switch to intermediate position + onState = true; + i_position = 2; + } else { + d_position -= sim.timeStep / switchingTime; + if (d_position <= 0) + d_position = i_position = 0; + } + + } } - + void startIterationOld() { - ind.startIteration(volts[nCoil1]-volts[nCoil3]); - - // magic value to balance operate speed with reset speed not at all realistically - double magic = 1.3; - double pmult = Math.sqrt(magic+1); - double c = onCurrent; - double p = coilCurrent*pmult/c; - d_position = Math.abs(p*p) - 1.3; - if (d_position < 0) - d_position = 0; - if (d_position > 1) - d_position = 1; - if (d_position < .1) - i_position = 0; - else if (d_position > .9) - i_position = 1; - else - i_position = 2; - //System.out.println("ind " + this + " " + current + " " + voltdiff); + ind.startIteration(volts[nCoil1] - volts[nCoil3]); + + // magic value to balance operate speed with reset speed not at all realistically + double magic = 1.3; + double pmult = Math.sqrt(magic + 1); + double c = onCurrent; + double p = coilCurrent * pmult / c; + d_position = Math.abs(p * p) - 1.3; + if (d_position < 0) + d_position = 0; + if (d_position > 1) + d_position = 1; + if (d_position < .1) + i_position = 0; + else if (d_position > .9) + i_position = 1; + else + i_position = 2; + //System.out.println("ind " + this + " " + current + " " + voltdiff); } - + // we need this to be able to change the matrix for each step - boolean nonLinear() { return true; } + boolean nonLinear() { + return true; + } void doStep() { - double voltdiff = volts[nCoil1]-volts[nCoil3]; - ind.doStep(voltdiff); - int p; - for (p = 0; p != poleCount*3; p += 3) { - sim.stampResistor(nodes[nSwitch0+p], nodes[nSwitch1+p], - i_position == 0 ? r_on : r_off); - sim.stampResistor(nodes[nSwitch0+p], nodes[nSwitch2+p], - i_position == 1 ? r_on : r_off); - } + double voltdiff = volts[nCoil1] - volts[nCoil3]; + ind.doStep(voltdiff); + int p; + for (p = 0; p != poleCount * 3; p += 3) { + sim.stampResistor(nodes[nSwitch0 + p], nodes[nSwitch1 + p], + i_position == 0 ? r_on : r_off); + sim.stampResistor(nodes[nSwitch0 + p], nodes[nSwitch2 + p], + i_position == 1 ? r_on : r_off); + } } + void calculateCurrent() { - double voltdiff = volts[nCoil1]-volts[nCoil3]; - coilCurrent = ind.calculateCurrent(voltdiff); - - // actually this isn't correct, since there is a small amount - // of current through the switch when off - int p; - for (p = 0; p != poleCount; p++) { - if (i_position == 2) - switchCurrent[p] = 0; - else - switchCurrent[p] = - (volts[nSwitch0+p*3]-volts[nSwitch1+p*3+i_position])/r_on; - } + double voltdiff = volts[nCoil1] - volts[nCoil3]; + coilCurrent = ind.calculateCurrent(voltdiff); + + // actually this isn't correct, since there is a small amount + // of current through the switch when off + int p; + for (p = 0; p != poleCount; p++) { + if (i_position == 2) + switchCurrent[p] = 0; + else + switchCurrent[p] = + (volts[nSwitch0 + p * 3] - volts[nSwitch1 + p * 3 + i_position]) / r_on; + } } + void getInfo(String arr[]) { - arr[0] = Locale.LS("relay"); - if (i_position == 0) - arr[0] += " (" + Locale.LS("off") + ")"; - else if (i_position == 1) - arr[0] += " (" + Locale.LS("on") + ")"; - if (switchingTime == 0) - arr[0] += " (" + Locale.LS("old model") + ")"; - int i; - int ln = 1; - for (i = 0; i != poleCount; i++) - arr[ln++] = "I" + (i+1) + " = " + getCurrentDText(switchCurrent[i]); - arr[ln++] = Locale.LS("coil I") + " = " + getCurrentDText(coilCurrent); - arr[ln++] = Locale.LS("coil Vd") + " = " + - getVoltageDText(volts[nCoil1] - volts[nCoil2]); + arr[0] = Locale.LS("relay"); + if (i_position == 0) + arr[0] += " (" + Locale.LS("off") + ")"; + else if (i_position == 1) + arr[0] += " (" + Locale.LS("on") + ")"; + if (switchingTime == 0) + arr[0] += " (" + Locale.LS("old model") + ")"; + int i; + int ln = 1; + for (i = 0; i != poleCount; i++) + arr[ln++] = "I" + (i + 1) + " = " + getCurrentDText(switchCurrent[i]); + arr[ln++] = Locale.LS("coil I") + " = " + getCurrentDText(coilCurrent); + arr[ln++] = Locale.LS("coil Vd") + " = " + + getVoltageDText(volts[nCoil1] - volts[nCoil2]); } + public EditInfo getEditInfo(int n) { - if (n == 0) - return new EditInfo("Inductance (H)", inductance, 0, 0); - 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); - if (n == 3) - return new EditInfo("On Current (A)", onCurrent, 0, 0); - if (n == 4) { - if (switchingTime == 0) { - // still using old model, so hide off current which won't work. - // make button to switch to new model + if (n == 0) + return new EditInfo("Inductance (H)", inductance, 0, 0); + 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); + if (n == 3) + return new EditInfo("On Current (A)", onCurrent, 0, 0); + if (n == 4) { + if (switchingTime == 0) { + // still using old model, so hide off current which won't work. + // make button to switch to new model EditInfo ei = new EditInfo("", 0, -1, -1); ei.button = new Button(Locale.LS("Use New Model")); return ei; - } - return new EditInfo("Off Current (A)", offCurrent, 0, 0); - } - if (n == 5) - return new EditInfo("Number of Poles", poleCount, 1, 4). - setDimensionless(); - if (n == 6) - return new EditInfo("Coil Resistance (ohms)", coilR, 0, 0); - if (n == 7) { - int style = 1; - if ((flags & FLAG_SWAP_COIL) != 0) - style = 2; - else if ((flags & FLAG_BOTH_SIDES_COIL) != 0) - style = 0; - EditInfo ei = new EditInfo("Coil Style", style, -1, -1); - ei.choice = new Choice(); - ei.choice.add("Both Sides"); - ei.choice.add("Side 1"); - ei.choice.add("Side 2"); - ei.choice.select(style); - return ei; - } - if (n == 8) { - EditInfo ei = new EditInfo("", 0, -1, -1); - ei.checkbox = new Checkbox("Show Box", (flags & FLAG_SHOW_BOX) != 0); - return ei; - } - - // show switching time only for new model, since it is meaningless for old one - if (n == 9 && switchingTime > 0) - return new EditInfo("Switching Time (s)", switchingTime, 0, 0); - return null; + } + return new EditInfo("Off Current (A)", offCurrent, 0, 0); + } + if (n == 5) + return new EditInfo("Number of Poles", poleCount, 1, 4). + setDimensionless(); + if (n == 6) + return new EditInfo("Coil Resistance (ohms)", coilR, 0, 0); + if (n == 7) { + int style = 1; + if ((flags & FLAG_SWAP_COIL) != 0) + style = 2; + else if ((flags & FLAG_BOTH_SIDES_COIL) != 0) + style = 0; + EditInfo ei = new EditInfo("Coil Style", style, -1, -1); + ei.choice = new Choice(); + ei.choice.add("Both Sides"); + ei.choice.add("Side 1"); + ei.choice.add("Side 2"); + ei.choice.select(style); + return ei; + } + if (n == 8) { + EditInfo ei = new EditInfo("", 0, -1, -1); + ei.checkbox = new Checkbox("Show Box", (flags & FLAG_SHOW_BOX) != 0); + return ei; + } + + // show switching time only for new model, since it is meaningless for old one + if (n == 9 && switchingTime > 0) + return new EditInfo("Switching Time (s)", switchingTime, 0, 0); + return null; } - + public void setEditValue(int n, EditInfo ei) { - if (n == 0 && ei.value > 0) { - inductance = ei.value; - ind.setup(inductance, coilCurrent, Inductor.FLAG_BACK_EULER); - } - if (n == 1 && ei.value > 0) - r_on = ei.value; - if (n == 2 && ei.value > 0) - r_off = ei.value; - if (n == 3 && ei.value > 0) - onCurrent = ei.value; - if (n == 4) { - // this could be a button or a text box for off current - if (ei.button != null) { - // upgrading to new model - switchingTime = 5e-3; - ei.newDialog = true; - } else if (ei.value > 0) - offCurrent = ei.value; - } - if (n == 5 && ei.value >= 1) { - poleCount = (int) ei.value; - setPoints(); - } - if (n == 6 && ei.value > 0) - coilR = ei.value; - if (n == 7) { - int style = ei.choice.getSelectedIndex(); - final int styles[] = { FLAG_BOTH_SIDES_COIL, 0, FLAG_SWAP_COIL }; - flags &= ~(FLAG_SWAP_COIL|FLAG_BOTH_SIDES_COIL); - flags |= styles[style]; - setPoints(); - } - if (n == 8) - flags = ei.changeFlag(flags, FLAG_SHOW_BOX); - if (n == 9 && ei.value > 0) - switchingTime = ei.value; + if (n == 0 && ei.value > 0) { + inductance = ei.value; + ind.setup(inductance, coilCurrent, Inductor.FLAG_BACK_EULER); + } + if (n == 1 && ei.value > 0) + r_on = ei.value; + if (n == 2 && ei.value > 0) + r_off = ei.value; + if (n == 3 && ei.value > 0) + onCurrent = ei.value; + if (n == 4) { + // this could be a button or a text box for off current + if (ei.button != null) { + // upgrading to new model + switchingTime = 5e-3; + ei.newDialog = true; + } else if (ei.value > 0) + offCurrent = ei.value; + } + if (n == 5 && ei.value >= 1) { + poleCount = (int) ei.value; + setPoints(); + } + if (n == 6 && ei.value > 0) + coilR = ei.value; + if (n == 7) { + int style = ei.choice.getSelectedIndex(); + final int styles[] = {FLAG_BOTH_SIDES_COIL, 0, FLAG_SWAP_COIL}; + flags &= ~(FLAG_SWAP_COIL | FLAG_BOTH_SIDES_COIL); + flags |= styles[style]; + setPoints(); + } + if (n == 8) + flags = ei.changeFlag(flags, FLAG_SHOW_BOX); + if (n == 9 && ei.value > 0) + switchingTime = ei.value; } - + boolean getConnection(int n1, int n2) { - return (n1 / 3 == n2 / 3); + return (n1 / 3 == n2 / 3); + } + + int getShortcut() { + return 'R'; } - - int getShortcut() { return 'R'; } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/ResistorElm.java b/src/main/java/com/lushprojects/circuitjs1/client/ResistorElm.java index d86aad4..758cecc 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/ResistorElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/ResistorElm.java @@ -22,100 +22,125 @@ import com.google.gwt.canvas.dom.client.CanvasGradient; import com.lushprojects.circuitjs1.client.util.Locale; - class ResistorElm extends CircuitElm { - double resistance; - public ResistorElm(int xx, int yy) { super(xx, yy); resistance = 1000; } - public ResistorElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f); - resistance = new Double(st.nextToken()).doubleValue(); - } - int getDumpType() { return 'r'; } - String dump() { - return super.dump() + " " + resistance; - } - - Point ps3, ps4; - void setPoints() { - super.setPoints(); - calcLeads(32); - ps3 = new Point(); - ps4 = new Point(); - } - - void draw(Graphics g) { - int segments = 16; - int i; - int ox = 0; - //int hs = sim.euroResistorCheckItem.getState() ? 6 : 8; - int hs=6; - double v1 = volts[0]; - double v2 = volts[1]; - setBbox(point1, point2, hs); - draw2Leads(g); - - // double segf = 1./segments; - double len = distance(lead1, lead2); - g.context.save(); - g.context.setLineWidth(3.0); - g.context.transform(((double)(lead2.x-lead1.x))/len, ((double)(lead2.y-lead1.y))/len, -((double)(lead2.y-lead1.y))/len,((double)(lead2.x-lead1.x))/len,lead1.x,lead1.y); - if (sim.voltsCheckItem.getState() ) { - CanvasGradient grad = g.context.createLinearGradient(0,0,len,0); - grad.addColorStop(0, getVoltageColor(g,v1).getHexValue()); - grad.addColorStop(1.0, getVoltageColor(g,v2).getHexValue()); - g.context.setStrokeStyle(grad); - } else - setPowerColor(g, true); - if (dn < 30) - hs = 2; - if (!sim.euroResistorCheckItem.getState()) { - g.context.beginPath(); - g.context.moveTo(0,0); - for (i=0;i<4;i++){ - g.context.lineTo((1+4*i)*len/16, hs); - g.context.lineTo((3+4*i)*len/16, -hs); - } - g.context.lineTo(len, 0); - g.context.stroke(); - - } else { - g.context.strokeRect(0, -hs, len, 2.0*hs); - } - g.context.restore(); - if (sim.showValuesCheckItem.getState()) { - String s = getShortUnitText(resistance, ""); - drawValues(g, s, hs+2); - } - doDots(g); - drawPosts(g); - } - - void calculateCurrent() { - current = (volts[0]-volts[1])/resistance; - //System.out.print(this + " res current set to " + current + "\n"); - } - void stamp() { - sim.stampResistor(nodes[0], nodes[1], resistance); - } - void getInfo(String arr[]) { - arr[0] = "resistor"; - getBasicInfo(arr); - arr[3] = "R = " + getUnitText(resistance, Locale.ohmString); - arr[4] = "P = " + getUnitText(getPower(), "W"); - } - @Override String getScopeText(int v) { - return Locale.LS("resistor") + ", " + getUnitText(resistance, Locale.ohmString); - } - public EditInfo getEditInfo(int n) { - // ohmString doesn't work here on linux - if (n == 0) - return new EditInfo("Resistance (ohms)", resistance, 0, 0); - return null; - } - public void setEditValue(int n, EditInfo ei) { - resistance = (ei.value <= 0) ? 1e-9 : ei.value; - } - int getShortcut() { return 'r'; } - double getResistance() { return resistance; } - void setResistance(double r) { resistance = r; } +public class ResistorElm extends CircuitElm { + double resistance; + + public ResistorElm(int xx, int yy) { + super(xx, yy); + resistance = 1000; + } + + public ResistorElm(int xa, int ya, int xb, int yb, int f, + StringTokenizer st) { + super(xa, ya, xb, yb, f); + resistance = new Double(st.nextToken()).doubleValue(); + } + + int getDumpType() { + return 'r'; + } + + String dump() { + return super.dump() + " " + resistance; + } + + Point ps3, ps4; + + void setPoints() { + super.setPoints(); + calcLeads(32); + ps3 = new Point(); + ps4 = new Point(); + } + + void draw(Graphics g) { + int segments = 16; + int i; + int ox = 0; + //int hs = sim.euroResistorCheckItem.getState() ? 6 : 8; + int hs = 6; + double v1 = volts[0]; + double v2 = volts[1]; + setBbox(point1, point2, hs); + draw2Leads(g); + + // double segf = 1./segments; + double len = distance(lead1, lead2); + g.context.save(); + g.context.setLineWidth(3.0); + g.context.transform(((double) (lead2.x - lead1.x)) / len, ((double) (lead2.y - lead1.y)) / len, -((double) (lead2.y - lead1.y)) / len, ((double) (lead2.x - lead1.x)) / len, lead1.x, lead1.y); + if (sim.voltsCheckItem.getState()) { + CanvasGradient grad = g.context.createLinearGradient(0, 0, len, 0); + grad.addColorStop(0, getVoltageColor(g, v1).getHexValue()); + grad.addColorStop(1.0, getVoltageColor(g, v2).getHexValue()); + g.context.setStrokeStyle(grad); + } else + setPowerColor(g, true); + if (dn < 30) + hs = 2; + if (!sim.euroResistorCheckItem.getState()) { + g.context.beginPath(); + g.context.moveTo(0, 0); + for (i = 0; i < 4; i++) { + g.context.lineTo((1 + 4 * i) * len / 16, hs); + g.context.lineTo((3 + 4 * i) * len / 16, -hs); + } + g.context.lineTo(len, 0); + g.context.stroke(); + + } else { + g.context.strokeRect(0, -hs, len, 2.0 * hs); + } + g.context.restore(); + if (sim.showValuesCheckItem.getState()) { + String s = getShortUnitText(resistance, ""); + drawValues(g, s, hs + 2); + } + doDots(g); + drawPosts(g); + } + + void calculateCurrent() { + current = (volts[0] - volts[1]) / resistance; + //System.out.print(this + " res current set to " + current + "\n"); + } + + void stamp() { + sim.stampResistor(nodes[0], nodes[1], resistance); + } + + void getInfo(String arr[]) { + arr[0] = "resistor"; + getBasicInfo(arr); + arr[3] = "R = " + getUnitText(resistance, Locale.ohmString); + arr[4] = "P = " + getUnitText(getPower(), "W"); + } + + @Override + String getScopeText(int v) { + return Locale.LS("resistor") + ", " + getUnitText(resistance, Locale.ohmString); + } + + public EditInfo getEditInfo(int n) { + // ohmString doesn't work here on linux + if (n == 0) + return new EditInfo("Resistance (ohms)", resistance, 0, 0); + return null; + } + + public void setEditValue(int n, EditInfo ei) { + resistance = (ei.value <= 0) ? 1e-9 : ei.value; + } + + int getShortcut() { + return 'r'; + } + + double getResistance() { + return resistance; + } + + void setResistance(double r) { + resistance = r; } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/RingCounterElm.java b/src/main/java/com/lushprojects/circuitjs1/client/RingCounterElm.java index b8c895d..5bfa332 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/RingCounterElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/RingCounterElm.java @@ -19,112 +19,139 @@ package com.lushprojects.circuitjs1.client; - class RingCounterElm extends ChipElm { - boolean justLoaded; - final int FLAG_CLOCK_INHIBIT = 2; - final int FLAG_RESET_HIGH = 4; - - public RingCounterElm(int xx, int yy) { - super(xx, yy); - flags |= FLAG_CLOCK_INHIBIT; - setupPins(); - } - public RingCounterElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f, st); - justLoaded = true; - } - String getChipName() { return "ring counter"; } - boolean needsBits() { return true; } - int defaultBitCount() { return 10; } - boolean hasClockInhibit() { return (flags & FLAG_CLOCK_INHIBIT) != 0 && bits >= 3; } - boolean hasInvertReset() { return (flags & FLAG_RESET_HIGH) == 0; } - - int clockInhibit; - - void setupPins() { - sizeX = bits > 2 ? bits : 2; - sizeY = 2; - pins = new Pin[getPostCount()]; - pins[0] = new Pin(1, SIDE_W, ""); - pins[0].clock = true; - pins[1] = new Pin(sizeX-1, SIDE_S, "R"); - pins[1].lineOver = hasInvertReset(); - int i; - for (i = 0; i != bits; i++) { - int ii = i+2; - pins[ii] = new Pin(i, SIDE_N, "Q" + i); - pins[ii].output = pins[ii].state = true; - } - if (hasClockInhibit()) { - clockInhibit = pins.length-1; - pins[clockInhibit] = new Pin(1, SIDE_S, "CE"); - pins[clockInhibit].lineOver = true; - } else - clockInhibit = -1; - allocNodes(); - } - int getPostCount() { return hasClockInhibit() ? bits+3 : bits+2; } - int getVoltageSourceCount() { return bits; } - void execute() { - int i; - - // if we just loaded then the volts[] array is likely to be all zeroes, which might force us to do a reset, so defer execution until the next iteration - if (justLoaded) { - justLoaded = false; - return; - } - - boolean running = true; - if (hasClockInhibit() && pins[clockInhibit].value) - running = false; - - // find which output is high - for (i = 0; i != bits; i++) - if (pins[i+2].value) - break; - - if (pins[0].value && !lastClock && running) { - if (i < bits) - pins[i++ +2].value = false; - i %= bits; - pins[i+2].value = true; - } - - // reset if requested, or if all outputs are low - if (pins[1].value != hasInvertReset() || i == bits) { - for (i = 1; i != bits; i++) - pins[i+2].value = false; - pins[2].value = true; - } - lastClock = pins[0].value; - } - public EditInfo getChipEditInfo(int n) { - if (n == 0) { - EditInfo ei = new EditInfo("", 0, -1, -1); - ei.checkbox = new Checkbox("Invert reset pin", hasInvertReset()); - return ei; - } - if (n == 1) - return new EditInfo("# of Bits", bits, 1, 1).setDimensionless(); - return null; - } - public void setChipEditValue(int n, EditInfo ei) { - if (n == 0) { - if (ei.checkbox.getState()) - flags &= ~FLAG_RESET_HIGH; - else - flags |= FLAG_RESET_HIGH; - setupPins(); - setPoints(); - return; - } - if (n == 1 && ei.value >= 2) { - bits = (int)ei.value; - setupPins(); - setPoints(); - } - } - - int getDumpType() { return 163; } +public class RingCounterElm extends ChipElm { + boolean justLoaded; + final int FLAG_CLOCK_INHIBIT = 2; + final int FLAG_RESET_HIGH = 4; + + public RingCounterElm(int xx, int yy) { + super(xx, yy); + flags |= FLAG_CLOCK_INHIBIT; + setupPins(); + } + + public RingCounterElm(int xa, int ya, int xb, int yb, int f, + StringTokenizer st) { + super(xa, ya, xb, yb, f, st); + justLoaded = true; + } + + String getChipName() { + return "ring counter"; + } + + boolean needsBits() { + return true; + } + + int defaultBitCount() { + return 10; + } + + boolean hasClockInhibit() { + return (flags & FLAG_CLOCK_INHIBIT) != 0 && bits >= 3; + } + + boolean hasInvertReset() { + return (flags & FLAG_RESET_HIGH) == 0; + } + + int clockInhibit; + + void setupPins() { + sizeX = bits > 2 ? bits : 2; + sizeY = 2; + pins = new Pin[getPostCount()]; + pins[0] = new Pin(1, SIDE_W, ""); + pins[0].clock = true; + pins[1] = new Pin(sizeX - 1, SIDE_S, "R"); + pins[1].lineOver = hasInvertReset(); + int i; + for (i = 0; i != bits; i++) { + int ii = i + 2; + pins[ii] = new Pin(i, SIDE_N, "Q" + i); + pins[ii].output = pins[ii].state = true; + } + if (hasClockInhibit()) { + clockInhibit = pins.length - 1; + pins[clockInhibit] = new Pin(1, SIDE_S, "CE"); + pins[clockInhibit].lineOver = true; + } else + clockInhibit = -1; + allocNodes(); + } + + int getPostCount() { + return hasClockInhibit() ? bits + 3 : bits + 2; + } + + int getVoltageSourceCount() { + return bits; + } + + void execute() { + int i; + + // if we just loaded then the volts[] array is likely to be all zeroes, which might force us to do a reset, so defer execution until the next iteration + if (justLoaded) { + justLoaded = false; + return; + } + + boolean running = true; + if (hasClockInhibit() && pins[clockInhibit].value) + running = false; + + // find which output is high + for (i = 0; i != bits; i++) + if (pins[i + 2].value) + break; + + if (pins[0].value && !lastClock && running) { + if (i < bits) + pins[i++ + 2].value = false; + i %= bits; + pins[i + 2].value = true; + } + + // reset if requested, or if all outputs are low + if (pins[1].value != hasInvertReset() || i == bits) { + for (i = 1; i != bits; i++) + pins[i + 2].value = false; + pins[2].value = true; + } + lastClock = pins[0].value; + } + + public EditInfo getChipEditInfo(int n) { + if (n == 0) { + EditInfo ei = new EditInfo("", 0, -1, -1); + ei.checkbox = new Checkbox("Invert reset pin", hasInvertReset()); + return ei; + } + if (n == 1) + return new EditInfo("# of Bits", bits, 1, 1).setDimensionless(); + return null; + } + + public void setChipEditValue(int n, EditInfo ei) { + if (n == 0) { + if (ei.checkbox.getState()) + flags &= ~FLAG_RESET_HIGH; + else + flags |= FLAG_RESET_HIGH; + setupPins(); + setPoints(); + return; + } + if (n == 1 && ei.value >= 2) { + bits = (int) ei.value; + setupPins(); + setPoints(); + } + } + + int getDumpType() { + return 163; } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/RowInfo.java b/src/main/java/com/lushprojects/circuitjs1/client/RowInfo.java index d8b589e..9f5e42b 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/RowInfo.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/RowInfo.java @@ -20,13 +20,16 @@ package com.lushprojects.circuitjs1.client; // info about each row/column of the matrix for simplification purposes - class RowInfo { - static final int ROW_NORMAL = 0; // ordinary value - static final int ROW_CONST = 1; // value is constant - int type, mapCol, mapRow; - double value; - boolean rsChanges; // row's right side changes - boolean lsChanges; // row's left side changes - boolean dropRow; // row is not needed in matrix - RowInfo() { type = ROW_NORMAL; } +public class RowInfo { + static final int ROW_NORMAL = 0; // ordinary value + static final int ROW_CONST = 1; // value is constant + int type, mapCol, mapRow; + double value; + boolean rsChanges; // row's right side changes + boolean lsChanges; // row's left side changes + boolean dropRow; // row is not needed in matrix + + RowInfo() { + type = ROW_NORMAL; } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/SCRElm.java b/src/main/java/com/lushprojects/circuitjs1/client/SCRElm.java index b186aa3..1376f50 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/SCRElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/SCRElm.java @@ -26,59 +26,71 @@ // 3, 1 = diode // 2, 1 = 50 ohm resistor -class SCRElm extends CircuitElm { +public class SCRElm extends CircuitElm { final int anode = 0; final int cnode = 1; final int gnode = 2; final int inode = 3; final int FLAG_GATE_FIX = 1; Diode diode; - + public SCRElm(int xx, int yy) { - super(xx, yy); - setDefaults(); - flags |= FLAG_GATE_FIX; - setup(); + super(xx, yy); + setDefaults(); + flags |= FLAG_GATE_FIX; + setup(); } + public SCRElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f); - setDefaults(); - try { - lastvac = new Double(st.nextToken()).doubleValue(); - lastvag = new Double(st.nextToken()).doubleValue(); - volts[anode] = 0; - volts[cnode] = -lastvac; - volts[gnode] = -lastvag; - triggerI = new Double(st.nextToken()).doubleValue(); - holdingI = new Double(st.nextToken()).doubleValue(); - gresistance = new Double(st.nextToken()).doubleValue(); - } catch (Exception e) { - } - setup(); + StringTokenizer st) { + super(xa, ya, xb, yb, f); + setDefaults(); + try { + lastvac = new Double(st.nextToken()).doubleValue(); + lastvag = new Double(st.nextToken()).doubleValue(); + volts[anode] = 0; + volts[cnode] = -lastvac; + volts[gnode] = -lastvag; + triggerI = new Double(st.nextToken()).doubleValue(); + holdingI = new Double(st.nextToken()).doubleValue(); + gresistance = new Double(st.nextToken()).doubleValue(); + } catch (Exception e) { + } + setup(); } + void setDefaults() { - gresistance = 50; - holdingI = .0082; - triggerI = .01; + gresistance = 50; + holdingI = .0082; + triggerI = .01; } + void setup() { - diode = new Diode(sim); - diode.setupForDefaultModel(); - aresistance = 1; // to avoid divide by zero + diode = new Diode(sim); + diode.setupForDefaultModel(); + aresistance = 1; // to avoid divide by zero } - boolean nonLinear() { return true; } + + boolean nonLinear() { + return true; + } + void reset() { - volts[anode] = volts[cnode] = volts[gnode] = 0; - diode.reset(); - lastvag = lastvac = curcount_a = curcount_c = curcount_g = 0; + volts[anode] = volts[cnode] = volts[gnode] = 0; + diode.reset(); + lastvag = lastvac = curcount_a = curcount_c = curcount_g = 0; + } + + int getDumpType() { + return 177; } - int getDumpType() { return 177; } + String dump() { - return super.dump() + " " + (volts[anode]-volts[cnode]) + " " + - (volts[anode]-volts[gnode]) + " " + triggerI + " "+ holdingI + " " + - gresistance; + return super.dump() + " " + (volts[anode] - volts[cnode]) + " " + + (volts[anode] - volts[gnode]) + " " + triggerI + " " + holdingI + " " + + gresistance; } + double ia, ic, ig, curcount_a, curcount_c, curcount_g; double lastvac, lastvag; double gresistance, triggerI, holdingI; @@ -86,171 +98,185 @@ String dump() { final int hs = 8; Polygon poly; Point cathode[], gate[]; - - boolean applyGateFix() { return (flags & FLAG_GATE_FIX) != 0; } - + + boolean applyGateFix() { + return (flags & FLAG_GATE_FIX) != 0; + } + void setPoints() { - super.setPoints(); - int dir = 0; - if (abs(dx) > abs(dy)) { - dir = -sign(dx)*sign(dy); - - // correct dn (length) or else calcLeads() may get confused, and also gate may be drawn weirdly. Can't do this with old circuits or it may - // break them - if (applyGateFix()) - dn = abs(dx); - point2.y = point1.y; - } else { - dir = sign(dy)*sign(dx); - if (applyGateFix()) - dn = abs(dy); - point2.x = point1.x; - } - if (dir == 0) - dir = 1; - calcLeads(16); - cathode = newPointArray(2); - Point pa[] = newPointArray(2); - interpPoint2(lead1, lead2, pa[0], pa[1], 0, hs); - interpPoint2(lead1, lead2, cathode[0], cathode[1], 1, hs); - poly = createPolygon(pa[0], pa[1], lead2); - - gate = newPointArray(2); - double leadlen = (dn-16)/2; - int gatelen = sim.gridSize; - gatelen += leadlen % sim.gridSize; - if (leadlen < gatelen) { - x2 = x; y2 = y; - return; - } - interpPoint(lead2, point2, gate[0], gatelen/leadlen, gatelen*dir); - interpPoint(lead2, point2, gate[1], gatelen/leadlen, sim.gridSize*2*dir); - gate[1].x = sim.snapGrid(gate[1].x); - gate[1].y = sim.snapGrid(gate[1].y); + super.setPoints(); + int dir = 0; + if (abs(dx) > abs(dy)) { + dir = -sign(dx) * sign(dy); + + // correct dn (length) or else calcLeads() may get confused, and also gate may be drawn weirdly. Can't do this with old circuits or it may + // break them + if (applyGateFix()) + dn = abs(dx); + point2.y = point1.y; + } else { + dir = sign(dy) * sign(dx); + if (applyGateFix()) + dn = abs(dy); + point2.x = point1.x; + } + if (dir == 0) + dir = 1; + calcLeads(16); + cathode = newPointArray(2); + Point pa[] = newPointArray(2); + interpPoint2(lead1, lead2, pa[0], pa[1], 0, hs); + interpPoint2(lead1, lead2, cathode[0], cathode[1], 1, hs); + poly = createPolygon(pa[0], pa[1], lead2); + + gate = newPointArray(2); + double leadlen = (dn - 16) / 2; + int gatelen = sim.gridSize; + gatelen += leadlen % sim.gridSize; + if (leadlen < gatelen) { + x2 = x; + y2 = y; + return; + } + interpPoint(lead2, point2, gate[0], gatelen / leadlen, gatelen * dir); + interpPoint(lead2, point2, gate[1], gatelen / leadlen, sim.gridSize * 2 * dir); + gate[1].x = sim.snapGrid(gate[1].x); + gate[1].y = sim.snapGrid(gate[1].y); } - + void draw(Graphics g) { - setBbox(point1, point2, hs); - adjustBbox(gate[0], gate[1]); - - double v1 = volts[anode]; - double v2 = volts[cnode]; - - draw2Leads(g); - - // draw arrow thingy - setVoltageColor(g, v1); - setPowerColor(g, true); - g.fillPolygon(poly); - - setVoltageColor(g, volts[gnode]); - drawThickLine(g, lead2, gate[0]); - drawThickLine(g, gate[0], gate[1]); - - // draw thing arrow is pointing to - setVoltageColor(g, v2); - setPowerColor(g, true); - drawThickLine(g, cathode[0], cathode[1]); - - curcount_a = updateDotCount(ia, curcount_a); - curcount_c = updateDotCount(ic, curcount_c); - curcount_g = updateDotCount(ig, curcount_g); - if (sim.dragElm != this) { - drawDots(g, point1, lead2, curcount_a); - drawDots(g, point2, lead2, curcount_c); - drawDots(g, gate[1], gate[0], curcount_g); - drawDots(g, gate[0], lead2, curcount_g+distance(gate[1], gate[0])); - } - - if ((needsHighlight() || sim.dragElm == this) && point1.x == point2.x && point2.y > point1.y) { - g.setColor(whiteColor); - int ds = sign(dx); - g.drawString("C", lead2.x+((ds < 0) ? 5 : -15), lead2.y+12); - g.drawString("A", lead1.x+5, lead1.y-4); // x+6 if ds=1, -12 if -1 - g.drawString("G", gate[0].x, gate[0].y+12); - } - - drawPosts(g); + setBbox(point1, point2, hs); + adjustBbox(gate[0], gate[1]); + + double v1 = volts[anode]; + double v2 = volts[cnode]; + + draw2Leads(g); + + // draw arrow thingy + setVoltageColor(g, v1); + setPowerColor(g, true); + g.fillPolygon(poly); + + setVoltageColor(g, volts[gnode]); + drawThickLine(g, lead2, gate[0]); + drawThickLine(g, gate[0], gate[1]); + + // draw thing arrow is pointing to + setVoltageColor(g, v2); + setPowerColor(g, true); + drawThickLine(g, cathode[0], cathode[1]); + + curcount_a = updateDotCount(ia, curcount_a); + curcount_c = updateDotCount(ic, curcount_c); + curcount_g = updateDotCount(ig, curcount_g); + if (sim.dragElm != this) { + drawDots(g, point1, lead2, curcount_a); + drawDots(g, point2, lead2, curcount_c); + drawDots(g, gate[1], gate[0], curcount_g); + drawDots(g, gate[0], lead2, curcount_g + distance(gate[1], gate[0])); + } + + if ((needsHighlight() || sim.dragElm == this) && point1.x == point2.x && point2.y > point1.y) { + g.setColor(whiteColor); + int ds = sign(dx); + g.drawString("C", lead2.x + ((ds < 0) ? 5 : -15), lead2.y + 12); + g.drawString("A", lead1.x + 5, lead1.y - 4); // x+6 if ds=1, -12 if -1 + g.drawString("G", gate[0].x, gate[0].y + 12); + } + + drawPosts(g); } - + double getCurrentIntoNode(int n) { - if (n == anode) - return -ia; - if (n == cnode) - return -ic; - return -ig; + if (n == anode) + return -ia; + if (n == cnode) + return -ic; + return -ig; } - + Point getPost(int n) { - return (n == 0) ? point1 : (n == 1) ? point2 : gate[1]; + return (n == 0) ? point1 : (n == 1) ? point2 : gate[1]; } - - int getPostCount() { return 3; } - int getInternalNodeCount() { return 1; } + + int getPostCount() { + return 3; + } + + int getInternalNodeCount() { + return 1; + } + double getPower() { - return (volts[anode]-volts[gnode])*ia + (volts[cnode]-volts[gnode])*ic; + return (volts[anode] - volts[gnode]) * ia + (volts[cnode] - volts[gnode]) * ic; } double aresistance; + void stamp() { - sim.stampNonLinear(nodes[anode]); - sim.stampNonLinear(nodes[cnode]); - sim.stampNonLinear(nodes[gnode]); - sim.stampNonLinear(nodes[inode]); - sim.stampResistor(nodes[gnode], nodes[cnode], gresistance); - diode.stamp(nodes[inode], nodes[cnode]); + sim.stampNonLinear(nodes[anode]); + sim.stampNonLinear(nodes[cnode]); + sim.stampNonLinear(nodes[gnode]); + sim.stampNonLinear(nodes[inode]); + sim.stampResistor(nodes[gnode], nodes[cnode], gresistance); + diode.stamp(nodes[inode], nodes[cnode]); } void doStep() { - double vac = volts[anode]-volts[cnode]; // typically negative - double vag = volts[anode]-volts[gnode]; // typically positive - if (Math.abs(vac-lastvac) > .01 || - Math.abs(vag-lastvag) > .01) - sim.converged = false; - lastvac = vac; - lastvag = vag; - diode.doStep(volts[inode]-volts[cnode]); - double icmult = 1/triggerI; - double iamult = 1/holdingI - icmult; - //System.out.println(icmult + " " + iamult); - aresistance = (-icmult*ic + ia*iamult > 1) ? .0105 : 10e5; - //System.out.println(vac + " " + vag + " " + sim.converged + " " + ic + " " + ia + " " + aresistance + " " + volts[inode] + " " + volts[gnode] + " " + volts[anode]); - sim.stampResistor(nodes[anode], nodes[inode], aresistance); + double vac = volts[anode] - volts[cnode]; // typically negative + double vag = volts[anode] - volts[gnode]; // typically positive + if (Math.abs(vac - lastvac) > .01 || + Math.abs(vag - lastvag) > .01) + sim.converged = false; + lastvac = vac; + lastvag = vag; + diode.doStep(volts[inode] - volts[cnode]); + double icmult = 1 / triggerI; + double iamult = 1 / holdingI - icmult; + //System.out.println(icmult + " " + iamult); + aresistance = (-icmult * ic + ia * iamult > 1) ? .0105 : 10e5; + //System.out.println(vac + " " + vag + " " + sim.converged + " " + ic + " " + ia + " " + aresistance + " " + volts[inode] + " " + volts[gnode] + " " + volts[anode]); + sim.stampResistor(nodes[anode], nodes[inode], aresistance); } + void getInfo(String arr[]) { - arr[0] = "SCR"; - double vac = volts[anode]-volts[cnode]; - double vag = volts[anode]-volts[gnode]; - double vgc = volts[gnode]-volts[cnode]; - arr[1] = "Ia = " + getCurrentText(ia); - arr[2] = "Ig = " + getCurrentText(ig); - arr[3] = "Vac = " + getVoltageText(vac); - arr[4] = "Vag = " + getVoltageText(vag); - arr[5] = "Vgc = " + getVoltageText(vgc); + arr[0] = "SCR"; + double vac = volts[anode] - volts[cnode]; + double vag = volts[anode] - volts[gnode]; + double vgc = volts[gnode] - volts[cnode]; + arr[1] = "Ia = " + getCurrentText(ia); + arr[2] = "Ig = " + getCurrentText(ig); + arr[3] = "Vac = " + getVoltageText(vac); + arr[4] = "Vag = " + getVoltageText(vag); + arr[5] = "Vgc = " + getVoltageText(vgc); arr[6] = "P = " + getUnitText(getPower(), "W"); } + void calculateCurrent() { - ig = (volts[gnode]-volts[cnode])/gresistance; - ia = (volts[anode]-volts[inode])/aresistance; - ic = -ig-ia; + ig = (volts[gnode] - volts[cnode]) / gresistance; + ia = (volts[anode] - volts[inode]) / aresistance; + ic = -ig - ia; } + public EditInfo getEditInfo(int n) { - if (n == 0) - return new EditInfo("Trigger Current (A)", triggerI, 0, 0); - if (n == 1) - return new EditInfo("Holding Current (A)", holdingI, 0, 0); - if (n == 2) - return new EditInfo("Gate Resistance (ohms)", gresistance, 0, 0); - return null; + if (n == 0) + return new EditInfo("Trigger Current (A)", triggerI, 0, 0); + if (n == 1) + return new EditInfo("Holding Current (A)", holdingI, 0, 0); + if (n == 2) + return new EditInfo("Gate Resistance (ohms)", gresistance, 0, 0); + return null; } + public void setEditValue(int n, EditInfo ei) { - if (n == 0 && ei.value > 0) - triggerI = ei.value; - if (n == 1 && ei.value > 0) - holdingI = ei.value; - if (n == 2 && ei.value > 0) - gresistance = ei.value; + if (n == 0 && ei.value > 0) + triggerI = ei.value; + if (n == 1 && ei.value > 0) + holdingI = ei.value; + if (n == 2 && ei.value > 0) + gresistance = ei.value; } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/SRAMElm.java b/src/main/java/com/lushprojects/circuitjs1/client/SRAMElm.java index da6451c..fb7a730 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/SRAMElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/SRAMElm.java @@ -24,232 +24,251 @@ import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.TextArea; - class SRAMElm extends ChipElm { - int addressNodes, dataNodes, internalNodes; - int addressBits, dataBits; - HashMap map; - static String contentsOverride = null; - - public SRAMElm(int xx, int yy) { - super(xx, yy); - addressBits = dataBits = 4; - map = new HashMap(); - setupPins(); - } - - public SRAMElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f, st); - map = new HashMap(); - addressBits = Integer.parseInt(st.nextToken()); - dataBits = Integer.parseInt(st.nextToken()); - setupPins(); - try { - // load contents - // format: addr val(addr) val(addr+1) val(addr+2) ... -1 addr val val ... -1 ... -2 - while (true) { - int a = Integer.parseInt(st.nextToken()); - if (a < 0) - break; - int v = Integer.parseInt(st.nextToken()); - map.put(a, v); - while (true) { - v = Integer.parseInt(st.nextToken()); - if (v < 0) - break; - map.put(++a, v); - } - } - } catch (Exception e) {} - } - - String dump() { - String s = super.dump() + " " + addressBits + " " + dataBits; - - // dump contents - int maxI = 1< map; + static String contentsOverride = null; + + public SRAMElm(int xx, int yy) { + super(xx, yy); + addressBits = dataBits = 4; + map = new HashMap(); + setupPins(); + } + + public SRAMElm(int xa, int ya, int xb, int yb, int f, + StringTokenizer st) { + super(xa, ya, xb, yb, f, st); + map = new HashMap(); + addressBits = Integer.parseInt(st.nextToken()); + dataBits = Integer.parseInt(st.nextToken()); + setupPins(); + try { + // load contents + // format: addr val(addr) val(addr+1) val(addr+2) ... -1 addr val val ... -1 ... -2 + while (true) { + int a = Integer.parseInt(st.nextToken()); + if (a < 0) + break; + int v = Integer.parseInt(st.nextToken()); + map.put(a, v); + while (true) { + v = Integer.parseInt(st.nextToken()); + if (v < 0) + break; + map.put(++a, v); + } + } + } catch (Exception e) { + } + } + + String dump() { + String s = super.dump() + " " + addressBits + " " + dataBits; + + // dump contents + int maxI = 1 << addressBits; + int i; + for (i = 0; i < maxI; i++) { + Integer val = map.get(i); + if (val == null) + continue; + s += " " + i + " " + val; + while (true) { + val = map.get(++i); + if (val == null) + break; + s += " " + val; + } + s += " -1"; + } + s += " -2"; + return s; + } + + boolean nonLinear() { + return true; + } + + String getChipName() { + return "Static RAM"; + } + + void setupPins() { + sizeX = 2; + sizeY = max(addressBits, dataBits) + 1; + pins = new Pin[getPostCount()]; + pins[0] = new Pin(0, SIDE_W, "WE"); + pins[0].lineOver = true; + pins[1] = new Pin(0, SIDE_E, "OE"); + pins[1].lineOver = true; + int i; + addressNodes = 2; + dataNodes = 2 + addressBits; + internalNodes = 2 + addressBits + dataBits; + for (i = 0; i != addressBits; i++) { + int ii = i + addressNodes; + pins[ii] = new Pin(sizeY - addressBits + i, SIDE_W, "A" + (addressBits - i - 1)); + } + for (i = 0; i != dataBits; i++) { + int ii = i + dataNodes; + pins[ii] = new Pin(sizeY - dataBits + i, SIDE_E, "D" + (dataBits - i - 1)); + pins[ii].output = true; + } + allocNodes(); + } + + int getPostCount() { + return 2 + addressBits + dataBits; + } + + public EditInfo getChipEditInfo(int n) { + if (n == 0) + return new EditInfo("# of Address Bits", addressBits, 1, 1).setDimensionless(); + if (n == 1) + return new EditInfo("# of Data Bits", dataBits, 1, 1).setDimensionless(); + if (n == 2) { + EditInfo ei = new EditInfo("Contents", 0); + ei.textArea = new TextArea(); + ei.textArea.setVisibleLines(5); + String s = ""; + if (contentsOverride != null) { + s = contentsOverride; + contentsOverride = null; + } else { + int i; + int maxI = 1 << addressBits; + for (i = 0; i < maxI; i++) { + Integer val = map.get(i); + if (val == null) + continue; + s += i + ": " + val; + int ct = 1; + while (true) { + val = map.get(++i); + if (val == null) + break; + s += " " + val; + if (++ct == 8) + break; + } + s += "\n"; // sim.console("got " + i + " " + s); - } - } - ei.textArea.setText(s); - return ei; + } } - if (n == 3 && SRAMLoadFile.isSupported()) { - EditInfo ei = new EditInfo("", 0, -1, -1); - ei.loadFile = new SRAMLoadFile(); - ei.button = new Button("Load Contents From File"); - ei.newDialog = true; - return ei; + ei.textArea.setText(s); + return ei; + } + if (n == 3 && SRAMLoadFile.isSupported()) { + EditInfo ei = new EditInfo("", 0, -1, -1); + ei.loadFile = new SRAMLoadFile(); + ei.button = new Button("Load Contents From File"); + ei.newDialog = true; + return ei; + } + return super.getChipEditInfo(n); + } + + int parseNumber(String str) { + if (str.startsWith("0x")) + return Integer.parseInt(str.substring(2), 16); + if (str.startsWith("0b")) + return Integer.parseInt(str.substring(2), 2); + return Integer.parseInt(str); + } + + public void setChipEditValue(int n, EditInfo ei) { + if (n == 0 && ei.value >= 2 && ei.value <= 16) { + addressBits = (int) ei.value; + setupPins(); + setPoints(); + } + if (n == 1 && ei.value >= 2 && ei.value <= 16) { + dataBits = (int) ei.value; + setupPins(); + setPoints(); + } + if (n == 2) { + String s = ei.textArea.getText(); + String lines[] = s.split("\n"); + int i; + map.clear(); + for (i = 0; i != lines.length; i++) { + try { + String line = lines[i]; + String args[] = line.split(": *"); + int addr = parseNumber(args[0]); + String vals[] = args[1].split(" +"); + int j; + for (j = 0; j != vals.length; j++) { + int val = parseNumber(vals[j]); + map.put(addr++, val); + } + } catch (Exception e) { + } } - return super.getChipEditInfo(n); - } - - int parseNumber(String str) { - if (str.startsWith("0x")) - return Integer.parseInt(str.substring(2), 16); - if (str.startsWith("0b")) - return Integer.parseInt(str.substring(2), 2); - return Integer.parseInt(str); - } - - public void setChipEditValue(int n, EditInfo ei) { - if (n == 0 && ei.value >= 2 && ei.value <= 16) { - addressBits = (int)ei.value; - setupPins(); - setPoints(); - } - if (n == 1 && ei.value >= 2 && ei.value <= 16) { - dataBits = (int)ei.value; - setupPins(); - setPoints(); - } - if (n == 2) { - String s = ei.textArea.getText(); - String lines[] = s.split("\n"); - int i; - map.clear(); - for (i = 0; i != lines.length; i++) { - try { - String line = lines[i]; - String args[] = line.split(": *"); - int addr = parseNumber(args[0]); - String vals[] = args[1].split(" +"); - int j; - for (j = 0; j != vals.length; j++) { - int val = parseNumber(vals[j]); - map.put(addr++, val); - } - } catch (Exception e) {} - } - } - } - int getVoltageSourceCount() { return dataBits; } - int getInternalNodeCount() { return dataBits; } - - int address; - - void stamp() { - int i; - for (i = 0; i != dataBits; i++) { - Pin p = pins[i+dataNodes]; - sim.stampVoltageSource(0, nodes[internalNodes+i], p.voltSource); - sim.stampNonLinear(nodes[internalNodes+i]); - sim.stampNonLinear(nodes[dataNodes+i]); - } - } - - void doStep() { - int i; - boolean writeEnabled = volts[0] < getThreshold(); - boolean outputEnabled = (volts[1] < getThreshold()) && !writeEnabled; - - // get address - address = 0; - for (i = 0; i != addressBits; i++) { - address |= (volts[addressNodes+i] > getThreshold()) ? 1<<(addressBits-1-i) : 0; - } - - Integer dataObj = map.get(address); - int data = (dataObj == null) ? 0 : dataObj; - for (i = 0; i != dataBits; i++) { - Pin p = pins[i+dataNodes]; - sim.updateVoltageSource(0, nodes[internalNodes+i], p.voltSource, (data & (1<<(dataBits-1-i))) == 0 ? 0 : 5); - - // stamp resistor from internal voltage source to data pin. - // if output enabled, make it a small resistor. otherwise large. - sim.stampResistor(nodes[internalNodes+i], nodes[dataNodes+i], outputEnabled ? 1 : 1e8); - } - } - - void stepFinished() { - int i; - int data = 0; - boolean writeEnabled = volts[0] < getThreshold(); - if (!writeEnabled) - return; - - // store data in RAM - for (i = 0; i != dataBits; i++) { - data |= (volts[dataNodes+i] > getThreshold()) ? 1<<(dataBits-1-i) : 0; - } - map.put(address, data); - } - int getDumpType() { return 413; } + } + } + + int getVoltageSourceCount() { + return dataBits; + } + + int getInternalNodeCount() { + return dataBits; + } + + int address; + + void stamp() { + int i; + for (i = 0; i != dataBits; i++) { + Pin p = pins[i + dataNodes]; + sim.stampVoltageSource(0, nodes[internalNodes + i], p.voltSource); + sim.stampNonLinear(nodes[internalNodes + i]); + sim.stampNonLinear(nodes[dataNodes + i]); + } + } + + void doStep() { + int i; + boolean writeEnabled = volts[0] < getThreshold(); + boolean outputEnabled = (volts[1] < getThreshold()) && !writeEnabled; + + // get address + address = 0; + for (i = 0; i != addressBits; i++) { + address |= (volts[addressNodes + i] > getThreshold()) ? 1 << (addressBits - 1 - i) : 0; + } + + Integer dataObj = map.get(address); + int data = (dataObj == null) ? 0 : dataObj; + for (i = 0; i != dataBits; i++) { + Pin p = pins[i + dataNodes]; + sim.updateVoltageSource(0, nodes[internalNodes + i], p.voltSource, (data & (1 << (dataBits - 1 - i))) == 0 ? 0 : 5); + + // stamp resistor from internal voltage source to data pin. + // if output enabled, make it a small resistor. otherwise large. + sim.stampResistor(nodes[internalNodes + i], nodes[dataNodes + i], outputEnabled ? 1 : 1e8); + } + } + + void stepFinished() { + int i; + int data = 0; + boolean writeEnabled = volts[0] < getThreshold(); + if (!writeEnabled) + return; + + // store data in RAM + for (i = 0; i != dataBits; i++) { + data |= (volts[dataNodes + i] > getThreshold()) ? 1 << (dataBits - 1 - i) : 0; + } + map.put(address, data); + } + + int getDumpType() { + return 413; } +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/SRAMLoadFile.java b/src/main/java/com/lushprojects/circuitjs1/client/SRAMLoadFile.java index bdef2bf..56efb94 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/SRAMLoadFile.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/SRAMLoadFile.java @@ -20,8 +20,8 @@ package com.lushprojects.circuitjs1.client; public class SRAMLoadFile extends EditDialogLoadFile { - - public final native void handle() + + public final native void handle() /*-{ var oFiles = $doc.getElementById("EditDialogLoadFileElement").files, nFiles = oFiles.length; @@ -43,10 +43,10 @@ public final native void handle() reader.readAsArrayBuffer(oFiles[0]); } }-*/; - - static public void doLoadCallback(String data) { - SRAMElm.contentsOverride = data; - CirSim.editDialog.resetDialog(); - SRAMElm.contentsOverride = null; - } + + static public void doLoadCallback(String data) { + SRAMElm.contentsOverride = data; + CirSim.editDialog.resetDialog(); + SRAMElm.contentsOverride = null; + } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/SchmittElm.java b/src/main/java/com/lushprojects/circuitjs1/client/SchmittElm.java index 9b63951..0e1dd79 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/SchmittElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/SchmittElm.java @@ -21,81 +21,82 @@ // contributed by Edward Calver -class SchmittElm extends InvertingSchmittElm{ - public SchmittElm(int xx, int yy) { - super(xx,yy); - } - public SchmittElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa,ya,xb,yb,f,st); - } +public class SchmittElm extends InvertingSchmittElm { + public SchmittElm(int xx, int yy) { + super(xx, yy); + } + + public SchmittElm(int xa, int ya, int xb, int yb, int f, + StringTokenizer st) { + super(xa, ya, xb, yb, f, st); + } - int getDumpType() { return 182; } - void doStep() { - double v0 = volts[1]; - double out; - if(state) - {//Output is high - if(volts[0]>upperTrigger)//Input voltage high enough to set output high - { - state=false; - out=logicOnLevel; - } - else - { - out=logicOffLevel; - } - } - else - {//Output is low - if(volts[0] dn/2) - ww = (int) (dn/2); - lead1 = interpPoint(point1, point2, .5-ww/dn); - lead2 = interpPoint(point1, point2, .5+(ww-3)/dn); - Point triPoints[] = newPointArray(3); - interpPoint2(lead1, lead2, triPoints[0], triPoints[1], 0, hs); - triPoints[2] = interpPoint(point1, point2, .5+(ww-5)/dn); - gatePoly = createPolygon(triPoints); - } - void getInfo(String arr[]) { - arr[0] = "Schmitt Trigger~"; // ~ is for localization + void doStep() { + double v0 = volts[1]; + double out; + if (state) {//Output is high + if (volts[0] > upperTrigger)//Input voltage high enough to set output high + { + state = false; + out = logicOnLevel; + } else { + out = logicOffLevel; + } + } else {//Output is low + if (volts[0] < lowerTrigger)//Input voltage low enough to set output low + { + state = true; + out = logicOffLevel; + } else { + out = logicOnLevel; + } } - @Override double getCurrentIntoNode(int n) { - if (n == 1) - return current; - return 0; - } + double maxStep = slewRate * sim.timeStep * 1e9; + out = Math.max(Math.min(v0 + maxStep, out), v0 - maxStep); + sim.updateVoltageSource(0, nodes[1], voltSource, out); + } + + void draw(Graphics g) { + drawPosts(g); + draw2Leads(g); + g.setColor(needsHighlight() ? selectColor : lightGrayColor); + drawThickPolygon(g, gatePoly); + g.setLineWidth(2); + drawPolygon(g, symbolPoly); + g.setLineWidth(1); + ; + curcount = updateDotCount(current, curcount); + drawDots(g, lead2, point2, curcount); + } + + void setPoints() { + super.setPoints(); + int hs = 16; + int ww = 16; + if (ww > dn / 2) + ww = (int) (dn / 2); + lead1 = interpPoint(point1, point2, .5 - ww / dn); + lead2 = interpPoint(point1, point2, .5 + (ww - 3) / dn); + Point triPoints[] = newPointArray(3); + interpPoint2(lead1, lead2, triPoints[0], triPoints[1], 0, hs); + triPoints[2] = interpPoint(point1, point2, .5 + (ww - 5) / dn); + gatePoly = createPolygon(triPoints); + } + void getInfo(String arr[]) { + arr[0] = "Schmitt Trigger~"; // ~ is for localization } + + @Override + double getCurrentIntoNode(int n) { + if (n == 1) + return current; + return 0; + } + +} diff --git a/src/main/java/com/lushprojects/circuitjs1/client/Scope.java b/src/main/java/com/lushprojects/circuitjs1/client/Scope.java index 5fe712e..581e53f 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/Scope.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/Scope.java @@ -29,7 +29,7 @@ import com.google.gwt.canvas.dom.client.Context2d; // plot of single value on a scope -class ScopePlot { +public class ScopePlot { double minValues[], maxValues[]; int scopePointCount; int ptr; // ptr is pointer to the current sample @@ -40,12 +40,12 @@ class ScopePlot { double lastValue; String color; CircuitElm elm; - // Has a manual scale in "/div" format been put in by the user (as opposed to being - // inferred from a "MaxValue" format or from an automatically calculated scale)? - // Manual scales should be kept to sane values anyway, but this shows if this is a user - // intention we should respect, or if we should try and populate reasonable values from - // the data we have - boolean manScaleSet = false; + // Has a manual scale in "/div" format been put in by the user (as opposed to being + // inferred from a "MaxValue" format or from an automatically calculated scale)? + // Manual scales should be kept to sane values anyway, but this shows if this is a user + // intention we should respect, or if we should try and populate reasonable values from + // the data we have + boolean manScaleSet = false; double manScale = 1.0; // Units per division int manVPosition = 0; // 0 is center of screen. +V_POSITION_STEPS/2 is top of screen double gridMult; @@ -53,148 +53,147 @@ class ScopePlot { boolean acCoupled = false; double acAlpha = 0.9999; // Filter coefficient for AC coupling double acLastOut = 0; // Store y[i-1] term for AC coupling filter - - final static int FLAG_AC=1; - + + final static int FLAG_AC = 1; + ScopePlot(CircuitElm e, int u) { - elm = e; - units = u; + elm = e; + units = u; } - + ScopePlot(CircuitElm e, int u, int v, double manS) { - elm = e; - units = u; - value = v; - manScale = manS; - // ohms can only be positive, so move the v position to the bottom. - // power can be negative for caps and inductors, but still move to the bottom (for backward compatibility) - if (units == Scope.UNITS_OHMS || units == Scope.UNITS_W) - manVPosition = -Scope.V_POSITION_STEPS/2; + elm = e; + units = u; + value = v; + manScale = manS; + // ohms can only be positive, so move the v position to the bottom. + // power can be negative for caps and inductors, but still move to the bottom (for backward compatibility) + if (units == Scope.UNITS_OHMS || units == Scope.UNITS_W) + manVPosition = -Scope.V_POSITION_STEPS / 2; } int startIndex(int w) { - return ptr + scopePointCount - w; + return ptr + scopePointCount - w; } - + void reset(int spc, int sp, boolean full) { - int oldSpc = scopePointCount; - scopePointCount = spc; - if (scopePlotSpeed != sp) - oldSpc = 0; // throw away old data - scopePlotSpeed = sp; - // Adjust the time constant of the AC coupled filter in proportion to the number of samples - // we are seeing on the scope (if my maths is right). The constant is empirically determined - acAlpha = 1.0-1.0/(1.15*scopePlotSpeed*scopePointCount); - double oldMin[] = minValues; - double oldMax[] = maxValues; - minValues = new double[scopePointCount]; - maxValues = new double[scopePointCount]; - if (oldMin != null && !full) { - // preserve old data if possible - int i; - for (i = 0; i != scopePointCount && i != oldSpc; i++) { - int i1 = (-i) & (scopePointCount-1); - int i2 = (ptr-i) & (oldSpc-1); - minValues[i1] = oldMin[i2]; - maxValues[i1] = oldMax[i2]; - } - } else - lastUpdateTime = CirSim.theSim.t; - ptr = 0; + int oldSpc = scopePointCount; + scopePointCount = spc; + if (scopePlotSpeed != sp) + oldSpc = 0; // throw away old data + scopePlotSpeed = sp; + // Adjust the time constant of the AC coupled filter in proportion to the number of samples + // we are seeing on the scope (if my maths is right). The constant is empirically determined + acAlpha = 1.0 - 1.0 / (1.15 * scopePlotSpeed * scopePointCount); + double oldMin[] = minValues; + double oldMax[] = maxValues; + minValues = new double[scopePointCount]; + maxValues = new double[scopePointCount]; + if (oldMin != null && !full) { + // preserve old data if possible + int i; + for (i = 0; i != scopePointCount && i != oldSpc; i++) { + int i1 = (-i) & (scopePointCount - 1); + int i2 = (ptr - i) & (oldSpc - 1); + minValues[i1] = oldMin[i2]; + maxValues[i1] = oldMax[i2]; + } + } else + lastUpdateTime = CirSim.theSim.t; + ptr = 0; } void timeStep() { - if (elm == null) - return; - double v = elm.getScopeValue(value); - // AC coupling filter. 1st order IIR high pass - // y[i] = alpha x (y[i-1]+x[i]-x[i-1]) - // We calculate for all iterations (even DC coupled) to prime the data in case they switch to AC later - double newAcOut=acAlpha*(acLastOut+v-lastValue); - lastValue = v; - acLastOut = newAcOut; - if (isAcCoupled()) - v = newAcOut; - if (v < minValues[ptr]) - minValues[ptr] = v; - if (v > maxValues[ptr]) - maxValues[ptr] = v; - if (CirSim.theSim.t-lastUpdateTime >= CirSim.theSim.maxTimeStep * scopePlotSpeed) { - ptr = (ptr+1) & (scopePointCount-1); - minValues[ptr] = maxValues[ptr] = v; - lastUpdateTime += CirSim.theSim.maxTimeStep * scopePlotSpeed; - } + if (elm == null) + return; + double v = elm.getScopeValue(value); + // AC coupling filter. 1st order IIR high pass + // y[i] = alpha x (y[i-1]+x[i]-x[i-1]) + // We calculate for all iterations (even DC coupled) to prime the data in case they switch to AC later + double newAcOut = acAlpha * (acLastOut + v - lastValue); + lastValue = v; + acLastOut = newAcOut; + if (isAcCoupled()) + v = newAcOut; + if (v < minValues[ptr]) + minValues[ptr] = v; + if (v > maxValues[ptr]) + maxValues[ptr] = v; + if (CirSim.theSim.t - lastUpdateTime >= CirSim.theSim.maxTimeStep * scopePlotSpeed) { + ptr = (ptr + 1) & (scopePointCount - 1); + minValues[ptr] = maxValues[ptr] = v; + lastUpdateTime += CirSim.theSim.maxTimeStep * scopePlotSpeed; + } } - + String getUnitText(double v) { - switch (units) { - case Scope.UNITS_V: - return CircuitElm.getVoltageText(v); - case Scope.UNITS_A: - return CircuitElm.getCurrentText(v); - case Scope.UNITS_OHMS: - return CircuitElm.getUnitText(v, Locale.ohmString); - case Scope.UNITS_W: - return CircuitElm.getUnitText(v, "W"); - } - return null; + switch (units) { + case Scope.UNITS_V: + return CircuitElm.getVoltageText(v); + case Scope.UNITS_A: + return CircuitElm.getCurrentText(v); + case Scope.UNITS_OHMS: + return CircuitElm.getUnitText(v, Locale.ohmString); + case Scope.UNITS_W: + return CircuitElm.getUnitText(v, "W"); + } + return null; } static final String colors[] = { - "#FF0000", "#FF8000", "#FF00FF", "#7F00FF", - "#0000FF", "#0080FF", "#FFFF00", "#00FFFF", + "#FF0000", "#FF8000", "#FF00FF", "#7F00FF", + "#0000FF", "#0080FF", "#FFFF00", "#00FFFF", }; - + void assignColor(int count) { - if (count > 0) { - color = colors[(count-1) % 8]; - return; - } - switch (units) { - case Scope.UNITS_V: - color = CircuitElm.positiveColor.getHexValue(); - break; - case Scope.UNITS_A: - color = (CirSim.theSim.printableCheckItem.getState()) ? "#A0A000" : "#FFFF00"; - break; - default: - color = (CirSim.theSim.printableCheckItem.getState()) ? "#000000" : "#FFFFFF"; - break; - } + if (count > 0) { + color = colors[(count - 1) % 8]; + return; + } + switch (units) { + case Scope.UNITS_V: + color = CircuitElm.positiveColor.getHexValue(); + break; + case Scope.UNITS_A: + color = (CirSim.theSim.printableCheckItem.getState()) ? "#A0A000" : "#FFFF00"; + break; + default: + color = (CirSim.theSim.printableCheckItem.getState()) ? "#000000" : "#FFFFFF"; + break; + } } - + void setAcCoupled(boolean b) { - if (canAcCouple()) { - acCoupled = b; - } - else - acCoupled = false; + if (canAcCouple()) { + acCoupled = b; + } else + acCoupled = false; } - + boolean canAcCouple() { - return units == Scope.UNITS_V; // AC coupling is permitted if the plot is displaying volts + return units == Scope.UNITS_V; // AC coupling is permitted if the plot is displaying volts } - + boolean isAcCoupled() { - return acCoupled; + return acCoupled; } - + int getPlotFlags() { - return (acCoupled ? FLAG_AC : 0); + return (acCoupled ? FLAG_AC : 0); } } -class Scope { +public class Scope { final int FLAG_YELM = 32; - + // bunch of other flags go here, see getFlags() final int FLAG_IVALUE = 2048; // Flag to indicate if IVALUE is included in dump final int FLAG_PLOTS = 4096; // new-style dump with multiple plots - final int FLAG_PERPLOTFLAGS = 1<<18; // new-new style dump with plot flags - final int FLAG_PERPLOT_MAN_SCALE = 1<<19; // new-new style dump with manual included in each plot + final int FLAG_PERPLOTFLAGS = 1 << 18; // new-new style dump with plot flags + final int FLAG_PERPLOT_MAN_SCALE = 1 << 19; // new-new style dump with manual included in each plot final int FLAG_MAN_SCALE = 16; // other flags go here too, see getFlags() - + static final int VAL_POWER = 7; static final int VAL_POWER_OLD = 1; static final int VAL_VOLTAGE = 0; @@ -212,7 +211,7 @@ class Scope { static final int UNITS_OHMS = 3; static final int UNITS_COUNT = 4; static final double multa[] = {2.0, 2.5, 2.0}; - static final int V_POSITION_STEPS=200; + static final int V_POSITION_STEPS = 200; static final double MIN_MAN_SCALE = 1e-9; int scopePointCount = 128; FFT fft; @@ -235,7 +234,7 @@ class Scope { CirSim sim; Canvas imageCanvas; Context2d imageContext; - int alphaCounter =0; + int alphaCounter = 0; // scopeTimeStep to check if sim timestep has changed from previous value when redrawing double scopeTimeStep; double scale[]; // Max value to scale the display to show - indexed for each value of UNITS - e.g. UNITS_V, UNITS_A etc. @@ -250,279 +249,304 @@ class Scope { int manDivisions = 8; // Number of vertical divisions when in manual mode boolean drawGridLines; boolean somethingSelected; - + static double cursorTime; static int cursorUnits; static Scope cursorScope; - + Scope(CirSim s) { - sim = s; - scale = new double[UNITS_COUNT]; - reduceRange = new boolean[UNITS_COUNT]; - - rect = new Rectangle(0, 0, 1, 1); - imageCanvas=Canvas.createIfSupported(); - imageContext=imageCanvas.getContext2d(); - allocImage(); - initialize(); + sim = s; + scale = new double[UNITS_COUNT]; + reduceRange = new boolean[UNITS_COUNT]; + + rect = new Rectangle(0, 0, 1, 1); + imageCanvas = Canvas.createIfSupported(); + imageContext = imageCanvas.getContext2d(); + allocImage(); + initialize(); } - + void showCurrent(boolean b) { - showI = b; - if (b && !showingVoltageAndMaybeCurrent()) - setValue(0); - calcVisiblePlots(); + showI = b; + if (b && !showingVoltageAndMaybeCurrent()) + setValue(0); + calcVisiblePlots(); } + void showVoltage(boolean b) { - showV = b; - if (b && !showingVoltageAndMaybeCurrent()) - setValue(0); - calcVisiblePlots(); + showV = b; + if (b && !showingVoltageAndMaybeCurrent()) + setValue(0); + calcVisiblePlots(); + } + + void showMax(boolean b) { + showMax = b; + } + + void showScale(boolean b) { + showScale = b; + } + + void showMin(boolean b) { + showMin = b; + } + + void showFreq(boolean b) { + showFreq = b; } - void showMax (boolean b) { showMax = b; } - void showScale (boolean b) { showScale = b; } - void showMin (boolean b) { showMin = b; } - void showFreq (boolean b) { showFreq = b; } void showFFT(boolean b) { - showFFT = b; - if (!showFFT) - fft = null; + showFFT = b; + if (!showFFT) + fft = null; } - - void setManualScale(boolean value, boolean roundup) { - if (value!=manualScale) - clear2dView(); - manualScale = value; - for (ScopePlot p : plots) { - if (!p.manScaleSet) { - p.manScale=getManScaleFromMaxScale(p.units, roundup); - p.manVPosition=0; - p.manScaleSet = true; - } - } + + void setManualScale(boolean value, boolean roundup) { + if (value != manualScale) + clear2dView(); + manualScale = value; + for (ScopePlot p : plots) { + if (!p.manScaleSet) { + p.manScale = getManScaleFromMaxScale(p.units, roundup); + p.manVPosition = 0; + p.manScaleSet = true; + } + } } - - void resetGraph() { resetGraph(false); } - + + void resetGraph() { + resetGraph(false); + } + void resetGraph(boolean full) { - scopePointCount = 1; - while (scopePointCount <= rect.width) - scopePointCount *= 2; - if (plots == null) - plots = new Vector(); - showNegative = false; - int i; - for (i = 0; i != plots.size(); i++) - plots.get(i).reset(scopePointCount, speed, full); - calcVisiblePlots(); - scopeTimeStep = sim.maxTimeStep; - allocImage(); + scopePointCount = 1; + while (scopePointCount <= rect.width) + scopePointCount *= 2; + if (plots == null) + plots = new Vector(); + showNegative = false; + int i; + for (i = 0; i != plots.size(); i++) + plots.get(i).reset(scopePointCount, speed, full); + calcVisiblePlots(); + scopeTimeStep = sim.maxTimeStep; + allocImage(); } - + void setManualScaleValue(int plotId, double d) { - if (plotId >= visiblePlots.size() ) - return; // Shouldn't happen, but just in case... - clear2dView(); - visiblePlots.get(plotId).manScale=d; - visiblePlots.get(plotId).manScaleSet=true; + if (plotId >= visiblePlots.size()) + return; // Shouldn't happen, but just in case... + clear2dView(); + visiblePlots.get(plotId).manScale = d; + visiblePlots.get(plotId).manScaleSet = true; } - + double getScaleValue() { - if (visiblePlots.size() == 0) - return 0; - ScopePlot p = visiblePlots.get(0); - return scale[p.units]; + if (visiblePlots.size() == 0) + return 0; + ScopePlot p = visiblePlots.get(0); + return scale[p.units]; } - + String getScaleUnitsText() { - if (visiblePlots.size() == 0) - return "V"; - ScopePlot p = visiblePlots.get(0); - return getScaleUnitsText(p.units); + if (visiblePlots.size() == 0) + return "V"; + ScopePlot p = visiblePlots.get(0); + return getScaleUnitsText(p.units); } - + static String getScaleUnitsText(int units) { - switch (units) { - case UNITS_A: return "A"; - case UNITS_OHMS: return Locale.ohmString; - case UNITS_W: return "W"; - default: return "V"; - } + switch (units) { + case UNITS_A: + return "A"; + case UNITS_OHMS: + return Locale.ohmString; + case UNITS_W: + return "W"; + default: + return "V"; + } } - - boolean active() { return plots.size() > 0 && plots.get(0).elm != null; } - + + boolean active() { + return plots.size() > 0 && plots.get(0).elm != null; + } + void initialize() { - resetGraph(); - scale[UNITS_W] = scale[UNITS_OHMS] = scale[UNITS_V] = 5; - scale[UNITS_A] = .1; - scaleX = 5; - scaleY = .1; - speed = 64; - showMax = true; - showV = showI = false; - showScale = showFreq = manualScale = showMin = showElmInfo = false; - showFFT = false; - plot2d = false; - if (!loadDefaults()) { - // set showV and showI appropriately depending on what plots are present - int i; - for (i = 0; i != plots.size(); i++) { - ScopePlot plot = plots.get(i); - if (plot.units == UNITS_V) - showV = true; - if (plot.units == UNITS_A) - showI = true; - } - } + resetGraph(); + scale[UNITS_W] = scale[UNITS_OHMS] = scale[UNITS_V] = 5; + scale[UNITS_A] = .1; + scaleX = 5; + scaleY = .1; + speed = 64; + showMax = true; + showV = showI = false; + showScale = showFreq = manualScale = showMin = showElmInfo = false; + showFFT = false; + plot2d = false; + if (!loadDefaults()) { + // set showV and showI appropriately depending on what plots are present + int i; + for (i = 0; i != plots.size(); i++) { + ScopePlot plot = plots.get(i); + if (plot.units == UNITS_V) + showV = true; + if (plot.units == UNITS_A) + showI = true; + } + } } - + void calcVisiblePlots() { - visiblePlots = new Vector(); - int i; - int vc = 0, ac = 0, oc = 0; - if (!plot2d) { - for (i = 0; i != plots.size(); i++) { - ScopePlot plot = plots.get(i); - if (plot.units == UNITS_V) { - if (showV) { - visiblePlots.add(plot); - plot.assignColor(vc++); - } - } else if (plot.units == UNITS_A) { - if (showI) { - visiblePlots.add(plot); - plot.assignColor(ac++); - } - } else { - visiblePlots.add(plot); - plot.assignColor(oc++); - } - } - } else { // In 2D mode the visible plots are the first two plots - for(i =0; (i<2) && (i(); + int i; + int vc = 0, ac = 0, oc = 0; + if (!plot2d) { + for (i = 0; i != plots.size(); i++) { + ScopePlot plot = plots.get(i); + if (plot.units == UNITS_V) { + if (showV) { + visiblePlots.add(plot); + plot.assignColor(vc++); + } + } else if (plot.units == UNITS_A) { + if (showI) { + visiblePlots.add(plot); + plot.assignColor(ac++); + } + } else { + visiblePlots.add(plot); + plot.assignColor(oc++); + } + } + } else { // In 2D mode the visible plots are the first two plots + for (i = 0; (i < 2) && (i < plots.size()); i++) { + visiblePlots.add(plots.get(i)); + } + } } - + void setRect(Rectangle r) { - int w = this.rect.width; - this.rect = r; - if (this.rect.width != w) - resetGraph(); + int w = this.rect.width; + this.rect = r; + if (this.rect.width != w) + resetGraph(); } - - int getWidth() { return rect.width; } - - int rightEdge() { return rect.x+rect.width; } - + + int getWidth() { + return rect.width; + } + + int rightEdge() { + return rect.x + rect.width; + } + void setElm(CircuitElm ce) { - plots = new Vector(); - if (ce instanceof TransistorElm) - setValue(VAL_VCE, ce); - else - setValue(0, ce); - initialize(); + plots = new Vector(); + if (ce instanceof TransistorElm) + setValue(VAL_VCE, ce); + else + setValue(0, ce); + initialize(); } - + void addElm(CircuitElm ce) { - if (ce instanceof TransistorElm) - addValue(VAL_VCE, ce); - else - addValue(0, ce); + if (ce instanceof TransistorElm) + addValue(VAL_VCE, ce); + else + addValue(0, ce); } void setValue(int val) { - if (plots.size() > 2 || plots.size() == 0) - return; - CircuitElm ce = plots.firstElement().elm; - if (plots.size() == 2 && plots.get(1).elm != ce) - return; - plot2d = plotXY = false; - setValue(val, ce); + if (plots.size() > 2 || plots.size() == 0) + return; + CircuitElm ce = plots.firstElement().elm; + if (plots.size() == 2 && plots.get(1).elm != ce) + return; + plot2d = plotXY = false; + setValue(val, ce); } - + void addValue(int val, CircuitElm ce) { - if (val == 0) { - plots.add(new ScopePlot(ce, UNITS_V, VAL_VOLTAGE, getManScaleFromMaxScale(UNITS_V, false))); - - // create plot for current if applicable - if (ce != null && - sim.dotsCheckItem.getState() && - !(ce instanceof OutputElm || - ce instanceof LogicOutputElm || - ce instanceof AudioOutputElm || - ce instanceof ProbeElm)) - plots.add(new ScopePlot(ce, UNITS_A, VAL_CURRENT, getManScaleFromMaxScale(UNITS_A, false))); - } else { - int u = ce.getScopeUnits(val); - plots.add(new ScopePlot(ce, u, val, getManScaleFromMaxScale(u, false))); - if (u == UNITS_V) - showV = true; - if (u == UNITS_A) - showI = true; - } - calcVisiblePlots(); - resetGraph(); + if (val == 0) { + plots.add(new ScopePlot(ce, UNITS_V, VAL_VOLTAGE, getManScaleFromMaxScale(UNITS_V, false))); + + // create plot for current if applicable + if (ce != null && + sim.dotsCheckItem.getState() && + !(ce instanceof OutputElm || + ce instanceof LogicOutputElm || + ce instanceof AudioOutputElm || + ce instanceof ProbeElm)) + plots.add(new ScopePlot(ce, UNITS_A, VAL_CURRENT, getManScaleFromMaxScale(UNITS_A, false))); + } else { + int u = ce.getScopeUnits(val); + plots.add(new ScopePlot(ce, u, val, getManScaleFromMaxScale(u, false))); + if (u == UNITS_V) + showV = true; + if (u == UNITS_A) + showI = true; + } + calcVisiblePlots(); + resetGraph(); } - + void setValue(int val, CircuitElm ce) { - plots = new Vector(); - addValue(val, ce); + plots = new Vector(); + addValue(val, ce); // initialize(); } void setValues(int val, int ival, CircuitElm ce, CircuitElm yelm) { - if (ival > 0) { - plots = new Vector(); - plots.add(new ScopePlot(ce, ce.getScopeUnits( val), val, getManScaleFromMaxScale(ce.getScopeUnits( val), false))); - plots.add(new ScopePlot(ce, ce.getScopeUnits(ival), ival, getManScaleFromMaxScale(ce.getScopeUnits(ival), false))); - return; - } - if (yelm != null) { - plots = new Vector(); - plots.add(new ScopePlot(ce, ce.getScopeUnits( val), 0, getManScaleFromMaxScale(ce.getScopeUnits( val), false))); - plots.add(new ScopePlot(yelm, ce.getScopeUnits(ival), 0, getManScaleFromMaxScale(ce.getScopeUnits( val), false))); - return; - } - setValue(val); + if (ival > 0) { + plots = new Vector(); + plots.add(new ScopePlot(ce, ce.getScopeUnits(val), val, getManScaleFromMaxScale(ce.getScopeUnits(val), false))); + plots.add(new ScopePlot(ce, ce.getScopeUnits(ival), ival, getManScaleFromMaxScale(ce.getScopeUnits(ival), false))); + return; + } + if (yelm != null) { + plots = new Vector(); + plots.add(new ScopePlot(ce, ce.getScopeUnits(val), 0, getManScaleFromMaxScale(ce.getScopeUnits(val), false))); + plots.add(new ScopePlot(yelm, ce.getScopeUnits(ival), 0, getManScaleFromMaxScale(ce.getScopeUnits(val), false))); + return; + } + setValue(val); } - + void setText(String s) { - text = s; + text = s; } - + String getText() { - return text; + return text; } - + boolean showingValue(int v) { - int i; - for (i = 0; i != plots.size(); i++) { - ScopePlot sp = plots.get(i); - if (sp.value != v) - return false; - } - return true; + int i; + for (i = 0; i != plots.size(); i++) { + ScopePlot sp = plots.get(i); + if (sp.value != v) + return false; + } + return true; } // returns true if we have a plot of voltage and nothing else (except current). // The default case is a plot of voltage and current, so we're basically checking if that case is true. boolean showingVoltageAndMaybeCurrent() { - int i; - boolean gotv = false; - for (i = 0; i != plots.size(); i++) { - ScopePlot sp = plots.get(i); - if (sp.value == VAL_VOLTAGE) - gotv = true; - else if (sp.value != VAL_CURRENT) - return false; - } - return gotv; + int i; + boolean gotv = false; + for (i = 0; i != plots.size(); i++) { + ScopePlot sp = plots.get(i); + if (sp.value == VAL_VOLTAGE) + gotv = true; + else if (sp.value != VAL_CURRENT) + return false; + } + return gotv; } - + void combine(Scope s) { /* @@ -533,115 +557,115 @@ void combine(Scope s) { plots.add(s.plots.get(0)); else */ - plots = visiblePlots; - plots.addAll(s.visiblePlots); - s.plots.removeAllElements(); - calcVisiblePlots(); + plots = visiblePlots; + plots.addAll(s.visiblePlots); + s.plots.removeAllElements(); + calcVisiblePlots(); } // separate this scope's plots into separate scopes and return them in arr[pos], arr[pos+1], etc. return new length of array. int separate(Scope arr[], int pos) { - int i; - ScopePlot lastPlot = null; - for (i = 0; i != visiblePlots.size(); i++) { - if (pos >= arr.length) - return pos; - Scope s = new Scope(sim); - ScopePlot sp = visiblePlots.get(i); - if (lastPlot != null && lastPlot.elm == sp.elm && lastPlot.value == VAL_VOLTAGE && sp.value == VAL_CURRENT) - continue; - s.setValue(sp.value, sp.elm); - s.position = pos; - arr[pos++] = s; - lastPlot = sp; - s.setFlags(getFlags()); - s.setSpeed(speed); - } - return pos; + int i; + ScopePlot lastPlot = null; + for (i = 0; i != visiblePlots.size(); i++) { + if (pos >= arr.length) + return pos; + Scope s = new Scope(sim); + ScopePlot sp = visiblePlots.get(i); + if (lastPlot != null && lastPlot.elm == sp.elm && lastPlot.value == VAL_VOLTAGE && sp.value == VAL_CURRENT) + continue; + s.setValue(sp.value, sp.elm); + s.position = pos; + arr[pos++] = s; + lastPlot = sp; + s.setFlags(getFlags()); + s.setSpeed(speed); + } + return pos; } void removePlot(int plot) { - if (plot < visiblePlots.size()) { - ScopePlot p = visiblePlots.get(plot); - plots.remove(p); - calcVisiblePlots(); - } + if (plot < visiblePlots.size()) { + ScopePlot p = visiblePlots.get(plot); + plots.remove(p); + calcVisiblePlots(); + } } - + // called for each timestep void timeStep() { - int i; - for (i = 0; i != plots.size(); i++) - plots.get(i).timeStep(); + int i; + for (i = 0; i != plots.size(); i++) + plots.get(i).timeStep(); - int x=0; - int y=0; - - // For 2d plots we draw here rather than in the drawing routine - if (plot2d && imageContext!=null && plots.size()>=2) { - double v = plots.get(0).lastValue; - double yval = plots.get(1).lastValue; - if (!isManualScale()) { - boolean newscale = false; - while (v > scaleX || v < -scaleX) { - scaleX *= 2; - newscale = true; - } - while (yval > scaleY || yval < -scaleY) { - scaleY *= 2; - newscale = true; - } - if (newscale) - clear2dView(); - double xa = v /scaleX; - double ya = yval/scaleY; - x = (int) (rect.width *(1+xa)*.499); - y = (int) (rect.height*(1-ya)*.499); - } else { - double gridPx = calc2dGridPx(rect.width, rect.height); - x=(int)(rect.width*.499+(v/plots.get(0).manScale)*gridPx+gridPx*manDivisions*(double)(plots.get(0).manVPosition)/(double)(V_POSITION_STEPS)); - y=(int)(rect.height*.499-(yval/plots.get(1).manScale)*gridPx-gridPx*manDivisions*(double)(plots.get(1).manVPosition)/(double)(V_POSITION_STEPS)); - - } - drawTo(x, y); - } + int x = 0; + int y = 0; + + // For 2d plots we draw here rather than in the drawing routine + if (plot2d && imageContext != null && plots.size() >= 2) { + double v = plots.get(0).lastValue; + double yval = plots.get(1).lastValue; + if (!isManualScale()) { + boolean newscale = false; + while (v > scaleX || v < -scaleX) { + scaleX *= 2; + newscale = true; + } + while (yval > scaleY || yval < -scaleY) { + scaleY *= 2; + newscale = true; + } + if (newscale) + clear2dView(); + double xa = v / scaleX; + double ya = yval / scaleY; + x = (int) (rect.width * (1 + xa) * .499); + y = (int) (rect.height * (1 - ya) * .499); + } else { + double gridPx = calc2dGridPx(rect.width, rect.height); + x = (int) (rect.width * .499 + (v / plots.get(0).manScale) * gridPx + gridPx * manDivisions * (double) (plots.get(0).manVPosition) / (double) (V_POSITION_STEPS)); + y = (int) (rect.height * .499 - (yval / plots.get(1).manScale) * gridPx - gridPx * manDivisions * (double) (plots.get(1).manVPosition) / (double) (V_POSITION_STEPS)); + + } + drawTo(x, y); + } } double calc2dGridPx(int width, int height) { - int m = width 0) { - g.setColor("#880000"); - g.drawLine(x, 0, x, rect.height); + // Draw x-grid lines and label the frequencies in the FFT that they point to. + int prevEnd = 0; + int divs = 20; + double maxFrequency = 1 / (sim.maxTimeStep * speed * divs * 2); + for (int i = 0; i < divs; i++) { + int x = rect.width * i / divs; + if (x < prevEnd) continue; + String s = ((int) Math.round(i * maxFrequency)) + "Hz"; + int sWidth = (int) Math.ceil(g.context.measureText(s).getWidth()); + prevEnd = x + sWidth + 4; + if (i > 0) { + g.setColor("#880000"); + g.drawLine(x, 0, x, rect.height); + } + g.setColor("#FF0000"); + g.drawString(s, x + 2, rect.height); } - g.setColor("#FF0000"); - g.drawString(s, x + 2, rect.height); - } } void drawFFT(Graphics g) { - if (fft == null || fft.getSize() != scopePointCount) - fft = new FFT(scopePointCount); - double[] real = new double[scopePointCount]; - double[] imag = new double[scopePointCount]; - ScopePlot plot = (visiblePlots.size() == 0) ? plots.firstElement() : visiblePlots.firstElement(); - double maxV[] = plot.maxValues; - double minV[] = plot.minValues; - int ptr = plot.ptr; - for (int i = 0; i < scopePointCount; i++) { - int ii = (ptr - i + scopePointCount) & (scopePointCount - 1); - // need to average max and min or else it could cause average of function to be > 0, which - // produces spike at 0 Hz that hides rest of spectrum - real[i] = .5*(maxV[ii]+minV[ii]); - imag[i] = 0; - } - fft.fft(real, imag); - double maxM = 1e-8; - for (int i = 0; i < scopePointCount / 2; i++) { - double m = fft.magnitude(real[i], imag[i]); - if (m > maxM) - maxM = m; - } - int prevX = 0; - g.setColor("#FF0000"); - if (!logSpectrum) { - int prevHeight = 0; - int y = (rect.height - 1) - 12; - for (int i = 0; i < scopePointCount / 2; i++) { - int x = 2 * i * rect.width / scopePointCount; - // rect.width may be greater than or less than scopePointCount/2, - // so x may be greater than or equal to prevX. - double magnitude = fft.magnitude(real[i], imag[i]); - int height = (int) ((magnitude * y) / maxM); - if (x != prevX) - g.drawLine(prevX, y - prevHeight, x, y - height); - prevHeight = height; - prevX = x; - } - } else { - int y0 = 5; - int prevY = 0; - double ymult = rect.height/10; - double val0 = Math.log(scale[plot.units])*ymult; - for (int i = 0; i < scopePointCount / 2; i++) { - int x = 2 * i * rect.width / scopePointCount; - // rect.width may be greater than or less than scopePointCount/2, - // so x may be greater than or equal to prevX. - double val = Math.log(fft.magnitude(real[i], imag[i])); - int y = y0-(int) (val*ymult-val0); - if (x != prevX) - g.drawLine(prevX, prevY, x, y); - prevY = y; - prevX = x; - } - } + if (fft == null || fft.getSize() != scopePointCount) + fft = new FFT(scopePointCount); + double[] real = new double[scopePointCount]; + double[] imag = new double[scopePointCount]; + ScopePlot plot = (visiblePlots.size() == 0) ? plots.firstElement() : visiblePlots.firstElement(); + double maxV[] = plot.maxValues; + double minV[] = plot.minValues; + int ptr = plot.ptr; + for (int i = 0; i < scopePointCount; i++) { + int ii = (ptr - i + scopePointCount) & (scopePointCount - 1); + // need to average max and min or else it could cause average of function to be > 0, which + // produces spike at 0 Hz that hides rest of spectrum + real[i] = .5 * (maxV[ii] + minV[ii]); + imag[i] = 0; + } + fft.fft(real, imag); + double maxM = 1e-8; + for (int i = 0; i < scopePointCount / 2; i++) { + double m = fft.magnitude(real[i], imag[i]); + if (m > maxM) + maxM = m; + } + int prevX = 0; + g.setColor("#FF0000"); + if (!logSpectrum) { + int prevHeight = 0; + int y = (rect.height - 1) - 12; + for (int i = 0; i < scopePointCount / 2; i++) { + int x = 2 * i * rect.width / scopePointCount; + // rect.width may be greater than or less than scopePointCount/2, + // so x may be greater than or equal to prevX. + double magnitude = fft.magnitude(real[i], imag[i]); + int height = (int) ((magnitude * y) / maxM); + if (x != prevX) + g.drawLine(prevX, y - prevHeight, x, y - height); + prevHeight = height; + prevX = x; + } + } else { + int y0 = 5; + int prevY = 0; + double ymult = rect.height / 10; + double val0 = Math.log(scale[plot.units]) * ymult; + for (int i = 0; i < scopePointCount / 2; i++) { + int x = 2 * i * rect.width / scopePointCount; + // rect.width may be greater than or less than scopePointCount/2, + // so x may be greater than or equal to prevX. + double val = Math.log(fft.magnitude(real[i], imag[i])); + int y = y0 - (int) (val * ymult - val0); + if (x != prevX) + g.drawLine(prevX, prevY, x, y); + prevY = y; + prevX = x; + } + } } - + void drawSettingsWheel(Graphics g) { - final int outR = 8; - final int inR= 5; - final int inR45 = 4; - final int outR45 = 6; - if (showSettingsWheel()) { - g.context.save(); - if (cursorInSettingsWheel()) - g.setColor(CircuitElm.selectColor); - else - g.setColor(Color.dark_gray); - g.context.translate(rect.x+18, rect.y+rect.height-18); - CircuitElm.drawThickCircle(g,0, 0, inR); - CircuitElm.drawThickLine(g, -outR, 0, -inR, 0); - CircuitElm.drawThickLine(g, outR, 0, inR, 0); - CircuitElm.drawThickLine(g, 0, -outR, 0, -inR); - CircuitElm.drawThickLine(g, 0, outR, 0, inR); - CircuitElm.drawThickLine(g, -outR45, -outR45,-inR45,-inR45); - CircuitElm.drawThickLine(g, outR45, -outR45,inR45,-inR45); - CircuitElm.drawThickLine(g, -outR45, outR45,-inR45,inR45); - CircuitElm.drawThickLine(g, outR45, outR45,inR45,inR45); - g.context.restore(); - } + final int outR = 8; + final int inR = 5; + final int inR45 = 4; + final int outR45 = 6; + if (showSettingsWheel()) { + g.context.save(); + if (cursorInSettingsWheel()) + g.setColor(CircuitElm.selectColor); + else + g.setColor(Color.dark_gray); + g.context.translate(rect.x + 18, rect.y + rect.height - 18); + CircuitElm.drawThickCircle(g, 0, 0, inR); + CircuitElm.drawThickLine(g, -outR, 0, -inR, 0); + CircuitElm.drawThickLine(g, outR, 0, inR, 0); + CircuitElm.drawThickLine(g, 0, -outR, 0, -inR); + CircuitElm.drawThickLine(g, 0, outR, 0, inR); + CircuitElm.drawThickLine(g, -outR45, -outR45, -inR45, -inR45); + CircuitElm.drawThickLine(g, outR45, -outR45, inR45, -inR45); + CircuitElm.drawThickLine(g, -outR45, outR45, -inR45, inR45); + CircuitElm.drawThickLine(g, outR45, outR45, inR45, inR45); + g.context.restore(); + } } void draw2d(Graphics g) { - if (imageContext==null) - return; - g.context.save(); - g.context.translate(rect.x, rect.y); - g.clipRect(0, 0, rect.width, rect.height); - - alphaCounter++; - - if (alphaCounter>2) { - // fade out plot - alphaCounter=0; - imageContext.setGlobalAlpha(0.01); - if (sim.printableCheckItem.getState()) { - imageContext.setFillStyle("#ffffff"); - } else { - imageContext.setFillStyle("#000000"); - } - imageContext.fillRect(0,0,rect.width,rect.height); - imageContext.setGlobalAlpha(1.0); - } - - g.context.drawImage(imageContext.getCanvas(), 0.0, 0.0); + if (imageContext == null) + return; + g.context.save(); + g.context.translate(rect.x, rect.y); + g.clipRect(0, 0, rect.width, rect.height); + + alphaCounter++; + + if (alphaCounter > 2) { + // fade out plot + alphaCounter = 0; + imageContext.setGlobalAlpha(0.01); + if (sim.printableCheckItem.getState()) { + imageContext.setFillStyle("#ffffff"); + } else { + imageContext.setFillStyle("#000000"); + } + imageContext.fillRect(0, 0, rect.width, rect.height); + imageContext.setGlobalAlpha(1.0); + } + + g.context.drawImage(imageContext.getCanvas(), 0.0, 0.0); // g.drawImage(image, r.x, r.y, null); - g.setColor(CircuitElm.whiteColor); - g.fillOval(draw_ox-2, draw_oy-2, 5, 5); - // Axis - g.setColor(CircuitElm.positiveColor); - g.drawLine(0, rect.height/2, rect.width-1, rect.height/2); - if (!plotXY) - g.setColor(Color.yellow); - g.drawLine(rect.width/2, 0, rect.width/2, rect.height-1); - if (isManualScale()) { - double gridPx=calc2dGridPx(rect.width, rect.height); - g.setColor("#404040"); - for(int i=-manDivisions; i<=manDivisions; i++) { - if (i!=0) - g.drawLine((int)(gridPx*i)+rect.width/2, 0,(int)(gridPx*i)+rect.width/2, rect.height); - g.drawLine(0, (int)(gridPx*i)+rect.height/2,rect.width, (int)(gridPx*i)+rect.height/2); - } - } - textY=10; - g.setColor(CircuitElm.whiteColor); - if (text != null) { - drawInfoText(g, text); - } - if (showScale && plots.size()>=2 && isManualScale()) { - ScopePlot px = plots.get(0); - String sx=px.getUnitText(px.manScale); - ScopePlot py = plots.get(1); - String sy=py.getUnitText(py.manScale); - drawInfoText(g,"X="+sx+"/div, Y="+sy+"/div"); - } - g.context.restore(); - drawSettingsWheel(g); - if ( !sim.dialogIsShowing() && rect.contains(sim.mouseCursorX, sim.mouseCursorY) && plots.size()>=2) { - double gridPx=calc2dGridPx(rect.width, rect.height); - String info[] = new String [2]; - ScopePlot px = plots.get(0); - ScopePlot py = plots.get(1); - double xValue; - double yValue; - if (isManualScale()) { - xValue = px.manScale*((double)(sim.mouseCursorX-rect.x-rect.width/2)/gridPx-manDivisions*px.manVPosition/(double)(V_POSITION_STEPS)); - yValue = py.manScale*((double)(-sim.mouseCursorY+rect.y+rect.height/2)/gridPx-manDivisions*py.manVPosition/(double)(V_POSITION_STEPS)); - } else { - xValue = ((double)(sim.mouseCursorX-rect.x)/(0.499*(double)(rect.width))-1.0)*scaleX; - yValue = -((double)(sim.mouseCursorY-rect.y)/(0.499*(double)(rect.height))-1.0)*scaleY; - } - info[0]=px.getUnitText(xValue); - info[1]=py.getUnitText(yValue); - - drawCursorInfo(g, info, 2, sim.mouseCursorX, true); - - } + g.setColor(CircuitElm.whiteColor); + g.fillOval(draw_ox - 2, draw_oy - 2, 5, 5); + // Axis + g.setColor(CircuitElm.positiveColor); + g.drawLine(0, rect.height / 2, rect.width - 1, rect.height / 2); + if (!plotXY) + g.setColor(Color.yellow); + g.drawLine(rect.width / 2, 0, rect.width / 2, rect.height - 1); + if (isManualScale()) { + double gridPx = calc2dGridPx(rect.width, rect.height); + g.setColor("#404040"); + for (int i = -manDivisions; i <= manDivisions; i++) { + if (i != 0) + g.drawLine((int) (gridPx * i) + rect.width / 2, 0, (int) (gridPx * i) + rect.width / 2, rect.height); + g.drawLine(0, (int) (gridPx * i) + rect.height / 2, rect.width, (int) (gridPx * i) + rect.height / 2); + } + } + textY = 10; + g.setColor(CircuitElm.whiteColor); + if (text != null) { + drawInfoText(g, text); + } + if (showScale && plots.size() >= 2 && isManualScale()) { + ScopePlot px = plots.get(0); + String sx = px.getUnitText(px.manScale); + ScopePlot py = plots.get(1); + String sy = py.getUnitText(py.manScale); + drawInfoText(g, "X=" + sx + "/div, Y=" + sy + "/div"); + } + g.context.restore(); + drawSettingsWheel(g); + if (!sim.dialogIsShowing() && rect.contains(sim.mouseCursorX, sim.mouseCursorY) && plots.size() >= 2) { + double gridPx = calc2dGridPx(rect.width, rect.height); + String info[] = new String[2]; + ScopePlot px = plots.get(0); + ScopePlot py = plots.get(1); + double xValue; + double yValue; + if (isManualScale()) { + xValue = px.manScale * ((double) (sim.mouseCursorX - rect.x - rect.width / 2) / gridPx - manDivisions * px.manVPosition / (double) (V_POSITION_STEPS)); + yValue = py.manScale * ((double) (-sim.mouseCursorY + rect.y + rect.height / 2) / gridPx - manDivisions * py.manVPosition / (double) (V_POSITION_STEPS)); + } else { + xValue = ((double) (sim.mouseCursorX - rect.x) / (0.499 * (double) (rect.width)) - 1.0) * scaleX; + yValue = -((double) (sim.mouseCursorY - rect.y) / (0.499 * (double) (rect.height)) - 1.0) * scaleY; + } + info[0] = px.getUnitText(xValue); + info[1] = py.getUnitText(yValue); + + drawCursorInfo(g, info, 2, sim.mouseCursorX, true); + + } } - - - + + boolean showSettingsWheel() { - return rect.height > 100 && rect.width > 100; + return rect.height > 100 && rect.width > 100; } - + boolean cursorInSettingsWheel() { - return showSettingsWheel() && - sim.mouseCursorX >= rect.x && - sim.mouseCursorX <= rect.x + 36 && - sim.mouseCursorY >= rect.y + rect.height - 36 && - sim.mouseCursorY <= rect.y + rect.height; + return showSettingsWheel() && + sim.mouseCursorX >= rect.x && + sim.mouseCursorX <= rect.x + 36 && + sim.mouseCursorY >= rect.y + rect.height - 36 && + sim.mouseCursorY <= rect.y + rect.height; } - + // does another scope have something selected? void checkForSelectionElsewhere() { - // if mouse is here, then selection is already set by checkForSelection() - if (cursorScope == this) - return; - - if (cursorScope == null || visiblePlots.size() == 0) { - selectedPlot = -1; - return; - } - - // find a plot with same units as selected plot - int i; - for (i = 0; i != visiblePlots.size(); i++) { - ScopePlot p = visiblePlots.get(i); - if (p.units == cursorUnits) { - selectedPlot = i; - return; - } - } - - // default if we can't find anything with matching units - selectedPlot = 0; + // if mouse is here, then selection is already set by checkForSelection() + if (cursorScope == this) + return; + + if (cursorScope == null || visiblePlots.size() == 0) { + selectedPlot = -1; + return; + } + + // find a plot with same units as selected plot + int i; + for (i = 0; i != visiblePlots.size(); i++) { + ScopePlot p = visiblePlots.get(i); + if (p.units == cursorUnits) { + selectedPlot = i; + return; + } + } + + // default if we can't find anything with matching units + selectedPlot = 0; } - + void draw(Graphics g) { - if (plots.size() == 0) - return; - - // reset if timestep changed - if (scopeTimeStep != sim.maxTimeStep) { - scopeTimeStep = sim.maxTimeStep; - resetGraph(); - } - - - if (plot2d) { - draw2d(g); - return; - } - - drawSettingsWheel(g); - g.context.save(); - g.setColor(Color.red); - g.context.translate(rect.x, rect.y); - g.clipRect(0, 0, rect.width, rect.height); + if (plots.size() == 0) + return; + + // reset if timestep changed + if (scopeTimeStep != sim.maxTimeStep) { + scopeTimeStep = sim.maxTimeStep; + resetGraph(); + } + + + if (plot2d) { + draw2d(g); + return; + } + + drawSettingsWheel(g); + g.context.save(); + g.setColor(Color.red); + g.context.translate(rect.x, rect.y); + g.clipRect(0, 0, rect.width, rect.height); if (showFFT) { drawFFTVerticalGridLines(g); drawFFT(g); } - int i; - for (i = 0; i != UNITS_COUNT; i++) { - reduceRange[i] = false; - if (maxScale && !manualScale) - scale[i] = 1e-4; - } - - int si; - somethingSelected = false; // is one of our plots selected? - - for (si = 0; si != visiblePlots.size(); si++) { - ScopePlot plot = visiblePlots.get(si); - calcPlotScale(plot); - if (sim.scopeSelected == -1 && plot.elm !=null && plot.elm.isMouseElm()) - somethingSelected = true; - reduceRange[plot.units] = true; - } - - boolean sel = sim.scopeMenuIsSelected(this); - - checkForSelectionElsewhere(); - if (selectedPlot >= 0) - somethingSelected = true; - - drawGridLines = true; - boolean allPlotsSameUnits = true; - for (i = 1; i < visiblePlots.size(); i++) { - if (visiblePlots.get(i).units != visiblePlots.get(0).units) - allPlotsSameUnits = false; // Don't draw horizontal grid lines unless all plots are in same units - } - - if ((allPlotsSameUnits || showMax || showMin) && visiblePlots.size() > 0) - calcMaxAndMin(visiblePlots.firstElement().units); - - // draw volt plots on top (last), then current plots underneath, then everything else - for (i = 0; i != visiblePlots.size(); i++) { - if (visiblePlots.get(i).units > UNITS_A && i != selectedPlot) - drawPlot(g, visiblePlots.get(i), allPlotsSameUnits, false, sel); - } - for (i = 0; i != visiblePlots.size(); i++) { - if (visiblePlots.get(i).units == UNITS_A && i != selectedPlot) - drawPlot(g, visiblePlots.get(i), allPlotsSameUnits, false, sel); - } - for (i = 0; i != visiblePlots.size(); i++) { - if (visiblePlots.get(i).units == UNITS_V && i != selectedPlot) - drawPlot(g, visiblePlots.get(i), allPlotsSameUnits, false, sel); - } - // draw selection on top. only works if selection chosen from scope - if (selectedPlot >= 0 && selectedPlot < visiblePlots.size()) - drawPlot(g, visiblePlots.get(selectedPlot), allPlotsSameUnits, true, sel); - + int i; + for (i = 0; i != UNITS_COUNT; i++) { + reduceRange[i] = false; + if (maxScale && !manualScale) + scale[i] = 1e-4; + } + + int si; + somethingSelected = false; // is one of our plots selected? + + for (si = 0; si != visiblePlots.size(); si++) { + ScopePlot plot = visiblePlots.get(si); + calcPlotScale(plot); + if (sim.scopeSelected == -1 && plot.elm != null && plot.elm.isMouseElm()) + somethingSelected = true; + reduceRange[plot.units] = true; + } + + boolean sel = sim.scopeMenuIsSelected(this); + + checkForSelectionElsewhere(); + if (selectedPlot >= 0) + somethingSelected = true; + + drawGridLines = true; + boolean allPlotsSameUnits = true; + for (i = 1; i < visiblePlots.size(); i++) { + if (visiblePlots.get(i).units != visiblePlots.get(0).units) + allPlotsSameUnits = false; // Don't draw horizontal grid lines unless all plots are in same units + } + + if ((allPlotsSameUnits || showMax || showMin) && visiblePlots.size() > 0) + calcMaxAndMin(visiblePlots.firstElement().units); + + // draw volt plots on top (last), then current plots underneath, then everything else + for (i = 0; i != visiblePlots.size(); i++) { + if (visiblePlots.get(i).units > UNITS_A && i != selectedPlot) + drawPlot(g, visiblePlots.get(i), allPlotsSameUnits, false, sel); + } + for (i = 0; i != visiblePlots.size(); i++) { + if (visiblePlots.get(i).units == UNITS_A && i != selectedPlot) + drawPlot(g, visiblePlots.get(i), allPlotsSameUnits, false, sel); + } + for (i = 0; i != visiblePlots.size(); i++) { + if (visiblePlots.get(i).units == UNITS_V && i != selectedPlot) + drawPlot(g, visiblePlots.get(i), allPlotsSameUnits, false, sel); + } + // draw selection on top. only works if selection chosen from scope + if (selectedPlot >= 0 && selectedPlot < visiblePlots.size()) + drawPlot(g, visiblePlots.get(selectedPlot), allPlotsSameUnits, true, sel); + drawInfoTexts(g); - - g.restore(); - - drawCursor(g); - - if (plots.get(0).ptr > 5 && !manualScale) { - for (i = 0; i != UNITS_COUNT; i++) - if (scale[i] > 1e-4 && reduceRange[i]) - scale[i] /= 2; - } - - if ( (properties != null ) && properties.isShowing() ) - properties.refreshDraw(); + + g.restore(); + + drawCursor(g); + + if (plots.get(0).ptr > 5 && !manualScale) { + for (i = 0; i != UNITS_COUNT; i++) + if (scale[i] > 1e-4 && reduceRange[i]) + scale[i] /= 2; + } + + if ((properties != null) && properties.isShowing()) + properties.refreshDraw(); } - + // calculate maximum and minimum values for all plots of given units void calcMaxAndMin(int units) { - maxValue = -1e8; - minValue = 1e8; - int i; - int si; - for (si = 0; si != visiblePlots.size(); si++) { - ScopePlot plot = visiblePlots.get(si); - if (plot.units != units) - continue; - int ipa = plot.startIndex(rect.width); - double maxV[] = plot.maxValues; - double minV[] = plot.minValues; - for (i = 0; i != rect.width; i++) { - int ip = (i+ipa) & (scopePointCount-1); - if (maxV[ip] > maxValue) - maxValue = maxV[ip]; - if (minV[ip] < minValue) - minValue = minV[ip]; - } + maxValue = -1e8; + minValue = 1e8; + int i; + int si; + for (si = 0; si != visiblePlots.size(); si++) { + ScopePlot plot = visiblePlots.get(si); + if (plot.units != units) + continue; + int ipa = plot.startIndex(rect.width); + double maxV[] = plot.maxValues; + double minV[] = plot.minValues; + for (i = 0; i != rect.width; i++) { + int ip = (i + ipa) & (scopePointCount - 1); + if (maxV[ip] > maxValue) + maxValue = maxV[ip]; + if (minV[ip] < minValue) + minValue = minV[ip]; + } } } - + // adjust scale of a plot void calcPlotScale(ScopePlot plot) { - if (manualScale) - return; - int i; - int ipa = plot.startIndex(rect.width); - double maxV[] = plot.maxValues; - double minV[] = plot.minValues; - double max = 0; - double gridMax = scale[plot.units]; - for (i = 0; i != rect.width; i++) { - int ip = (i+ipa) & (scopePointCount-1); - if (maxV[ip] > max) - max = maxV[ip]; - if (minV[ip] < -max) - max = -minV[ip]; - } - // scale fixed at maximum? - if (maxScale) - gridMax = Math.max(max, gridMax); - else - // adjust in powers of two - while (max > gridMax) - gridMax *= 2; - scale[plot.units] = gridMax; + if (manualScale) + return; + int i; + int ipa = plot.startIndex(rect.width); + double maxV[] = plot.maxValues; + double minV[] = plot.minValues; + double max = 0; + double gridMax = scale[plot.units]; + for (i = 0; i != rect.width; i++) { + int ip = (i + ipa) & (scopePointCount - 1); + if (maxV[ip] > max) + max = maxV[ip]; + if (minV[ip] < -max) + max = -minV[ip]; + } + // scale fixed at maximum? + if (maxScale) + gridMax = Math.max(max, gridMax); + else + // adjust in powers of two + while (max > gridMax) + gridMax *= 2; + scale[plot.units] = gridMax; } - + double calcGridStepX() { - int multptr=0; - double gsx = 1e-15; + int multptr = 0; + double gsx = 1e-15; - double ts = sim.maxTimeStep*speed; - while (gsx < ts*20) { - gsx *=multa[(multptr++)%3]; - } - return gsx; + double ts = sim.maxTimeStep * speed; + while (gsx < ts * 20) { + gsx *= multa[(multptr++) % 3]; + } + return gsx; } double getGridMaxFromManScale(ScopePlot plot) { - return ((double)(manDivisions)/2+0.05)*plot.manScale; + return ((double) (manDivisions) / 2 + 0.05) * plot.manScale; } - + void drawPlot(Graphics g, ScopePlot plot, boolean allPlotsSameUnits, boolean selected, boolean allSelected) { - if (plot.elm == null) - return; - int i; - String col; - - double gridMid, positionOffset; - int multptr=0; - int x = 0; - final int maxy = (rect.height-1)/2; - - String color = (somethingSelected) ? "#A0A0A0" : plot.color; - if (allSelected || (sim.scopeSelected == -1 && plot.elm.isMouseElm())) - color = CircuitElm.selectColor.getHexValue(); - else if (selected) - color = plot.color; - int ipa = plot.startIndex(rect.width); - double maxV[] = plot.maxValues; - double minV[] = plot.minValues; - double gridMax; - - - // Calculate the max value (positive) to show and the value at the mid point of the grid - if (!isManualScale()) { - gridMax = scale[plot.units]; - gridMid = 0; - positionOffset = 0; - if (allPlotsSameUnits) { - // if we don't have overlapping scopes of different units, we can move zero around. - // Put it at the bottom if the scope is never negative. - double mx = gridMax; - double mn = 0; - if (maxScale) { - // scale is maxed out, so fix boundaries of scope at maximum and minimum. - mx = maxValue; - mn = minValue; - } else if (showNegative || minValue < (mx+mn)*.5 - (mx-mn)*.55) { - mn = -gridMax; - showNegative = true; - } - gridMid = (mx+mn)*.5; - gridMax = (mx-mn)*.55; // leave space at top and bottom - } - } else { - gridMid =0; - gridMax = getGridMaxFromManScale(plot); - positionOffset = gridMax*2.0*(double)(plot.manVPosition)/(double)(V_POSITION_STEPS); - } - plot.plotOffset = -gridMid+positionOffset; - - plot.gridMult = maxy/gridMax; - - int minRangeLo = -10-(int) (gridMid*plot.gridMult); - int minRangeHi = 10-(int) (gridMid*plot.gridMult); - if (!isManualScale()) { - gridStepY = 1e-8; - while (gridStepY < 20*gridMax/maxy) { - gridStepY *=multa[(multptr++)%3]; - } - } else { - gridStepY = plot.manScale; - } - - String minorDiv = "#404040"; - String majorDiv = "#A0A0A0"; - if (sim.printableCheckItem.getState()) { - minorDiv = "#D0D0D0"; - majorDiv = "#808080"; - curColor = "#A0A000"; - } - if (allSelected) - majorDiv = CircuitElm.selectColor.getHexValue(); - - // Vertical (T) gridlines - double ts = sim.maxTimeStep*speed; - gridStepX = calcGridStepX(); - - boolean highlightCenter = !isManualScale(); - - if (drawGridLines) { - // horizontal gridlines - - // don't show hgridlines if lines are too close together (except for center line) - boolean showHGridLines = (gridStepY != 0) && (isManualScale() || allPlotsSameUnits); // Will only show center line if we have mixed units - for (int ll = -100; ll <= 100; ll++) { - if (ll != 0 && !showHGridLines) - continue; - int yl = maxy-(int) ((ll*gridStepY-gridMid)*plot.gridMult); - if (yl < 0 || yl >= rect.height-1) - continue; - col = ll == 0 && highlightCenter ? majorDiv : minorDiv; - g.setColor(col); - g.drawLine(0,yl,rect.width-1,yl); - } - - // vertical gridlines - double tstart = sim.t-sim.maxTimeStep*speed*rect.width; - double tx = sim.t-(sim.t % gridStepX); - - for (int ll = 0; ; ll++) { - double tl = tx-gridStepX*ll; - int gx = (int) ((tl-tstart)/ts); - if (gx < 0) - break; - if (gx >= rect.width) - continue; - if (tl < 0) - continue; - col = minorDiv; - // first = 0; - if (((tl+gridStepX/4) % (gridStepX*10)) < gridStepX) { - col = majorDiv; - } - g.setColor(col); - g.drawLine(gx,0,gx,rect.height-1); - } - } - - // only need gridlines drawn once - drawGridLines = false; + if (plot.elm == null) + return; + int i; + String col; + + double gridMid, positionOffset; + int multptr = 0; + int x = 0; + final int maxy = (rect.height - 1) / 2; + + String color = (somethingSelected) ? "#A0A0A0" : plot.color; + if (allSelected || (sim.scopeSelected == -1 && plot.elm.isMouseElm())) + color = CircuitElm.selectColor.getHexValue(); + else if (selected) + color = plot.color; + int ipa = plot.startIndex(rect.width); + double maxV[] = plot.maxValues; + double minV[] = plot.minValues; + double gridMax; + + + // Calculate the max value (positive) to show and the value at the mid point of the grid + if (!isManualScale()) { + gridMax = scale[plot.units]; + gridMid = 0; + positionOffset = 0; + if (allPlotsSameUnits) { + // if we don't have overlapping scopes of different units, we can move zero around. + // Put it at the bottom if the scope is never negative. + double mx = gridMax; + double mn = 0; + if (maxScale) { + // scale is maxed out, so fix boundaries of scope at maximum and minimum. + mx = maxValue; + mn = minValue; + } else if (showNegative || minValue < (mx + mn) * .5 - (mx - mn) * .55) { + mn = -gridMax; + showNegative = true; + } + gridMid = (mx + mn) * .5; + gridMax = (mx - mn) * .55; // leave space at top and bottom + } + } else { + gridMid = 0; + gridMax = getGridMaxFromManScale(plot); + positionOffset = gridMax * 2.0 * (double) (plot.manVPosition) / (double) (V_POSITION_STEPS); + } + plot.plotOffset = -gridMid + positionOffset; + + plot.gridMult = maxy / gridMax; + + int minRangeLo = -10 - (int) (gridMid * plot.gridMult); + int minRangeHi = 10 - (int) (gridMid * plot.gridMult); + if (!isManualScale()) { + gridStepY = 1e-8; + while (gridStepY < 20 * gridMax / maxy) { + gridStepY *= multa[(multptr++) % 3]; + } + } else { + gridStepY = plot.manScale; + } + + String minorDiv = "#404040"; + String majorDiv = "#A0A0A0"; + if (sim.printableCheckItem.getState()) { + minorDiv = "#D0D0D0"; + majorDiv = "#808080"; + curColor = "#A0A000"; + } + if (allSelected) + majorDiv = CircuitElm.selectColor.getHexValue(); + + // Vertical (T) gridlines + double ts = sim.maxTimeStep * speed; + gridStepX = calcGridStepX(); + + boolean highlightCenter = !isManualScale(); + + if (drawGridLines) { + // horizontal gridlines + + // don't show hgridlines if lines are too close together (except for center line) + boolean showHGridLines = (gridStepY != 0) && (isManualScale() || allPlotsSameUnits); // Will only show center line if we have mixed units + for (int ll = -100; ll <= 100; ll++) { + if (ll != 0 && !showHGridLines) + continue; + int yl = maxy - (int) ((ll * gridStepY - gridMid) * plot.gridMult); + if (yl < 0 || yl >= rect.height - 1) + continue; + col = ll == 0 && highlightCenter ? majorDiv : minorDiv; + g.setColor(col); + g.drawLine(0, yl, rect.width - 1, yl); + } + + // vertical gridlines + double tstart = sim.t - sim.maxTimeStep * speed * rect.width; + double tx = sim.t - (sim.t % gridStepX); + + for (int ll = 0; ; ll++) { + double tl = tx - gridStepX * ll; + int gx = (int) ((tl - tstart) / ts); + if (gx < 0) + break; + if (gx >= rect.width) + continue; + if (tl < 0) + continue; + col = minorDiv; + // first = 0; + if (((tl + gridStepX / 4) % (gridStepX * 10)) < gridStepX) { + col = majorDiv; + } + g.setColor(col); + g.drawLine(gx, 0, gx, rect.height - 1); + } + } + + // only need gridlines drawn once + drawGridLines = false; g.setColor(color); - + if (isManualScale()) { // draw zero point - int y0= maxy-(int) (plot.gridMult*plot.plotOffset); + int y0 = maxy - (int) (plot.gridMult * plot.plotOffset); g.drawLine(0, y0, 8, y0); - g.drawString("0", 0, y0-2); + g.drawString("0", 0, y0 - 2); } - + int ox = -1, oy = -1; for (i = 0; i != rect.width; i++) { - int ip = (i+ipa) & (scopePointCount-1); - int minvy = (int) (plot.gridMult*(minV[ip]+plot.plotOffset)); - int maxvy = (int) (plot.gridMult*(maxV[ip]+plot.plotOffset)); + int ip = (i + ipa) & (scopePointCount - 1); + int minvy = (int) (plot.gridMult * (minV[ip] + plot.plotOffset)); + int maxvy = (int) (plot.gridMult * (maxV[ip] + plot.plotOffset)); if (minvy <= maxy) { - if (minvy < minRangeLo || maxvy > minRangeHi) { - // we got a value outside min range, so we don't need to rescale later - reduceRange[plot.units] = false; - minRangeLo = -1000; - minRangeHi = 1000; // avoid triggering this test again - } - if (ox != -1) { - if (minvy == oy && maxvy == oy) - continue; - g.drawLine(ox, maxy-oy, x+i, maxy-oy); - ox = oy = -1; - } - if (minvy == maxvy) { - ox = x+i; - oy = minvy; - continue; - } - g.drawLine(x+i, maxy-minvy, x+i, maxy-maxvy); + if (minvy < minRangeLo || maxvy > minRangeHi) { + // we got a value outside min range, so we don't need to rescale later + reduceRange[plot.units] = false; + minRangeLo = -1000; + minRangeHi = 1000; // avoid triggering this test again + } + if (ox != -1) { + if (minvy == oy && maxvy == oy) + continue; + g.drawLine(ox, maxy - oy, x + i, maxy - oy); + ox = oy = -1; + } + if (minvy == maxvy) { + ox = x + i; + oy = minvy; + continue; + } + g.drawLine(x + i, maxy - minvy, x + i, maxy - maxvy); } } // for (i=0...) if (ox != -1) - g.drawLine(ox, maxy-oy, x+i-1, maxy-oy); // Horizontal - + g.drawLine(ox, maxy - oy, x + i - 1, maxy - oy); // Horizontal + } static void clearCursorInfo() { - cursorScope = null; - cursorTime = -1; + cursorScope = null; + cursorTime = -1; } - + void selectScope(int mouseX, int mouseY) { - if (!rect.contains(mouseX, mouseY)) - return; - if (plot2d || visiblePlots.size() == 0) - cursorTime = -1; - else - cursorTime = sim.t-sim.maxTimeStep*speed*(rect.x+rect.width-mouseX); - checkForSelection(mouseX, mouseY); - cursorScope = this; + if (!rect.contains(mouseX, mouseY)) + return; + if (plot2d || visiblePlots.size() == 0) + cursorTime = -1; + else + cursorTime = sim.t - sim.maxTimeStep * speed * (rect.x + rect.width - mouseX); + checkForSelection(mouseX, mouseY); + cursorScope = this; } - + // find selected plot void checkForSelection(int mouseX, int mouseY) { - if (sim.dialogIsShowing()) - return; - if (!rect.contains(mouseX, mouseY)) { - selectedPlot = -1; - return; - } - if (plots.size() == 0) { - selectedPlot = -1; - return; - } - int ipa = plots.get(0).startIndex(rect.width); - int ip = (mouseX-rect.x+ipa) & (scopePointCount-1); - int maxy = (rect.height-1)/2; - int y = maxy; - int i; - int bestdist = 10000; - int best = -1; - for (i = 0; i != visiblePlots.size(); i++) { - ScopePlot plot = visiblePlots.get(i); - int maxvy = (int) (plot.gridMult*(plot.maxValues[ip]+plot.plotOffset)); - int dist = Math.abs(mouseY-(rect.y+y-maxvy)); - if (dist < bestdist) { - bestdist = dist; - best = i; - } - } - selectedPlot = best; - if (selectedPlot >= 0) - cursorUnits = visiblePlots.get(selectedPlot).units; + if (sim.dialogIsShowing()) + return; + if (!rect.contains(mouseX, mouseY)) { + selectedPlot = -1; + return; + } + if (plots.size() == 0) { + selectedPlot = -1; + return; + } + int ipa = plots.get(0).startIndex(rect.width); + int ip = (mouseX - rect.x + ipa) & (scopePointCount - 1); + int maxy = (rect.height - 1) / 2; + int y = maxy; + int i; + int bestdist = 10000; + int best = -1; + for (i = 0; i != visiblePlots.size(); i++) { + ScopePlot plot = visiblePlots.get(i); + int maxvy = (int) (plot.gridMult * (plot.maxValues[ip] + plot.plotOffset)); + int dist = Math.abs(mouseY - (rect.y + y - maxvy)); + if (dist < bestdist) { + bestdist = dist; + best = i; + } + } + selectedPlot = best; + if (selectedPlot >= 0) + cursorUnits = visiblePlots.get(selectedPlot).units; } - + void drawCursor(Graphics g) { - if (sim.dialogIsShowing()) - return; - if (cursorScope == null) - return; - String info[] = new String[4]; - int cursorX = -1; - int ct = 0; - if (cursorTime >= 0) { - cursorX = -(int) ((sim.t-cursorTime)/(sim.maxTimeStep*speed) - rect.x - rect.width); - if (cursorX >= rect.x) { - int ipa = plots.get(0).startIndex(rect.width); - int ip = (cursorX-rect.x+ipa) & (scopePointCount-1); - int maxy = (rect.height-1)/2; - int y = maxy; - if (visiblePlots.size() > 0) { - ScopePlot plot = visiblePlots.get(selectedPlot >= 0 ? selectedPlot : 0); - info[ct++] = plot.getUnitText(plot.maxValues[ip]); - int maxvy = (int) (plot.gridMult*(plot.maxValues[ip]+plot.plotOffset)); - g.setColor(plot.color); - g.fillOval(cursorX-2, rect.y+y-maxvy-2, 5, 5); - } - } - } - - // show FFT even if there's no plots (in which case cursorTime/cursorX will be invalid) + if (sim.dialogIsShowing()) + return; + if (cursorScope == null) + return; + String info[] = new String[4]; + int cursorX = -1; + int ct = 0; + if (cursorTime >= 0) { + cursorX = -(int) ((sim.t - cursorTime) / (sim.maxTimeStep * speed) - rect.x - rect.width); + if (cursorX >= rect.x) { + int ipa = plots.get(0).startIndex(rect.width); + int ip = (cursorX - rect.x + ipa) & (scopePointCount - 1); + int maxy = (rect.height - 1) / 2; + int y = maxy; + if (visiblePlots.size() > 0) { + ScopePlot plot = visiblePlots.get(selectedPlot >= 0 ? selectedPlot : 0); + info[ct++] = plot.getUnitText(plot.maxValues[ip]); + int maxvy = (int) (plot.gridMult * (plot.maxValues[ip] + plot.plotOffset)); + g.setColor(plot.color); + g.fillOval(cursorX - 2, rect.y + y - maxvy - 2, 5, 5); + } + } + } + + // show FFT even if there's no plots (in which case cursorTime/cursorX will be invalid) if (showFFT && cursorScope == this) { double maxFrequency = 1 / (sim.maxTimeStep * speed * 2); if (cursorX < 0) - cursorX = sim.mouseCursorX; - info[ct++] = CircuitElm.getUnitText(maxFrequency*(sim.mouseCursorX-rect.x)/rect.width, "Hz"); + cursorX = sim.mouseCursorX; + info[ct++] = CircuitElm.getUnitText(maxFrequency * (sim.mouseCursorX - rect.x) / rect.width, "Hz"); } else if (cursorX < rect.x) return; - - if (visiblePlots.size() > 0) - info[ct++] = CircuitElm.getTimeText(cursorTime); - - if (cursorScope != this) { - // don't show cursor info if not enough room, or stacked with selected one - // (position == -1 for embedded scopes) - if (rect.height < 40 || (position >= 0 && cursorScope.position == position)) { - drawCursorInfo(g, null, 0, cursorX, false); - return; - } - } - drawCursorInfo(g, info, ct, cursorX, false); + + if (visiblePlots.size() > 0) + info[ct++] = CircuitElm.getTimeText(cursorTime); + + if (cursorScope != this) { + // don't show cursor info if not enough room, or stacked with selected one + // (position == -1 for embedded scopes) + if (rect.height < 40 || (position >= 0 && cursorScope.position == position)) { + drawCursorInfo(g, null, 0, cursorX, false); + return; + } + } + drawCursorInfo(g, info, ct, cursorX, false); } - + void drawCursorInfo(Graphics g, String[] info, int ct, int x, Boolean drawY) { - int szw = 0, szh = 15*ct; - int i; - for (i = 0; i != ct; i++) { - int w=(int)g.context.measureText(info[i]).getWidth(); - if (w > szw) - szw = w; - } - - g.setColor(CircuitElm.whiteColor); - g.drawLine(x, rect.y, x, rect.y+rect.height); - if (drawY) - g.drawLine(rect.x, sim.mouseCursorY, rect.x+rect.width, sim.mouseCursorY); - g.setColor(sim.printableCheckItem.getState() ? Color.white : Color.black); - int bx = x; - if (bx < szw/2) - bx = szw/2; - g.fillRect(bx-szw/2, rect.y-szh, szw, szh); - g.setColor(CircuitElm.whiteColor); - for (i = 0; i != ct; i++) { - int w=(int)g.context.measureText(info[i]).getWidth(); - g.drawString(info[i], bx-w/2, rect.y-2-(ct-1-i)*15); - } - + int szw = 0, szh = 15 * ct; + int i; + for (i = 0; i != ct; i++) { + int w = (int) g.context.measureText(info[i]).getWidth(); + if (w > szw) + szw = w; + } + + g.setColor(CircuitElm.whiteColor); + g.drawLine(x, rect.y, x, rect.y + rect.height); + if (drawY) + g.drawLine(rect.x, sim.mouseCursorY, rect.x + rect.width, sim.mouseCursorY); + g.setColor(sim.printableCheckItem.getState() ? Color.white : Color.black); + int bx = x; + if (bx < szw / 2) + bx = szw / 2; + g.fillRect(bx - szw / 2, rect.y - szh, szw, szh); + g.setColor(CircuitElm.whiteColor); + for (i = 0; i != ct; i++) { + int w = (int) g.context.measureText(info[i]).getWidth(); + g.drawString(info[i], bx - w / 2, rect.y - 2 - (ct - 1 - i) * 15); + } + } boolean canShowRMS() { - if (visiblePlots.size() == 0) - return false; - ScopePlot plot = visiblePlots.firstElement(); - return (plot.units == Scope.UNITS_V || plot.units == Scope.UNITS_A); + if (visiblePlots.size() == 0) + return false; + ScopePlot plot = visiblePlots.firstElement(); + return (plot.units == Scope.UNITS_V || plot.units == Scope.UNITS_A); } - + // calc RMS and display it void drawRMS(Graphics g) { - if (!canShowRMS()) { - // needed for backward compatibility - showRMS = false; - showAverage = true; - drawAverage(g); - return; - } - ScopePlot plot = visiblePlots.firstElement(); - int i; - double avg = 0; - int ipa = plot.ptr+scopePointCount-rect.width; - double maxV[] = plot.maxValues; - double minV[] = plot.minValues; - double mid = (maxValue+minValue)/2; - int state = -1; - - // skip zeroes - for (i = 0; i != rect.width; i++) { - int ip = (i+ipa) & (scopePointCount-1); - if (maxV[ip] != 0) { - if (maxV[ip] > mid) - state = 1; - break; - } - } - int firstState = -state; - int start = i; - int end = 0; - int waveCount = 0; - double endAvg = 0; - for (; i != rect.width; i++) { - int ip = (i+ipa) & (scopePointCount-1); - boolean sw = false; - - // switching polarity? - if (state == 1) { - if (maxV[ip] < mid) - sw = true; - } else if (minV[ip] > mid) - sw = true; - - if (sw) { - state = -state; - - // completed a full cycle? - if (firstState == state) { - if (waveCount == 0) { - start = i; - firstState = state; - avg = 0; - } - waveCount++; - end = i; - endAvg = avg; - } - } - if (waveCount > 0) { - double m = (maxV[ip]+minV[ip])*.5; - avg += m*m; - } - } - double rms; - if (waveCount > 1) { - rms = Math.sqrt(endAvg/(end-start)); - drawInfoText(g, plot.getUnitText(rms) + "rms"); - } + if (!canShowRMS()) { + // needed for backward compatibility + showRMS = false; + showAverage = true; + drawAverage(g); + return; + } + ScopePlot plot = visiblePlots.firstElement(); + int i; + double avg = 0; + int ipa = plot.ptr + scopePointCount - rect.width; + double maxV[] = plot.maxValues; + double minV[] = plot.minValues; + double mid = (maxValue + minValue) / 2; + int state = -1; + + // skip zeroes + for (i = 0; i != rect.width; i++) { + int ip = (i + ipa) & (scopePointCount - 1); + if (maxV[ip] != 0) { + if (maxV[ip] > mid) + state = 1; + break; + } + } + int firstState = -state; + int start = i; + int end = 0; + int waveCount = 0; + double endAvg = 0; + for (; i != rect.width; i++) { + int ip = (i + ipa) & (scopePointCount - 1); + boolean sw = false; + + // switching polarity? + if (state == 1) { + if (maxV[ip] < mid) + sw = true; + } else if (minV[ip] > mid) + sw = true; + + if (sw) { + state = -state; + + // completed a full cycle? + if (firstState == state) { + if (waveCount == 0) { + start = i; + firstState = state; + avg = 0; + } + waveCount++; + end = i; + endAvg = avg; + } + } + if (waveCount > 0) { + double m = (maxV[ip] + minV[ip]) * .5; + avg += m * m; + } + } + double rms; + if (waveCount > 1) { + rms = Math.sqrt(endAvg / (end - start)); + drawInfoText(g, plot.getUnitText(rms) + "rms"); + } } - + void drawScale(ScopePlot plot, Graphics g) { - if (!isManualScale()) { - if ( gridStepY!=0 && (!(showV && showI))) { - String vScaleText=" V=" + plot.getUnitText(gridStepY)+"/div"; - drawInfoText(g, "H="+CircuitElm.getUnitText(gridStepX, "s")+"/div" + vScaleText); - } - } else { - if (rect.y + rect.height <= textY+5) - return; - double x = 0; - String hs = "H="+CircuitElm.getUnitText(gridStepX, "s")+"/div"; - g.drawString(hs, 0, textY); - x+=g.measureWidth(hs); - final double bulletWidth = 17; - for (int i=0; i rect.width) { - x=0; - textY += 15; - if (rect.y + rect.height <= textY+5) - return; - } - g.setColor(p.color); - g.fillOval((int)x+7, textY-9, 8, 8); - x+=bulletWidth; - g.setColor(CircuitElm.whiteColor); - g.drawString(vScaleText, (int)x, textY); - x+=vScaleWidth; - } - } - textY += 15; - } + if (!isManualScale()) { + if (gridStepY != 0 && (!(showV && showI))) { + String vScaleText = " V=" + plot.getUnitText(gridStepY) + "/div"; + drawInfoText(g, "H=" + CircuitElm.getUnitText(gridStepX, "s") + "/div" + vScaleText); + } + } else { + if (rect.y + rect.height <= textY + 5) + return; + double x = 0; + String hs = "H=" + CircuitElm.getUnitText(gridStepX, "s") + "/div"; + g.drawString(hs, 0, textY); + x += g.measureWidth(hs); + final double bulletWidth = 17; + for (int i = 0; i < visiblePlots.size(); i++) { + ScopePlot p = visiblePlots.get(i); + String s = p.getUnitText(p.manScale); + if (p != null) { + String vScaleText = "=" + s + "/div"; + double vScaleWidth = g.measureWidth(vScaleText); + if (x + bulletWidth + vScaleWidth > rect.width) { + x = 0; + textY += 15; + if (rect.y + rect.height <= textY + 5) + return; + } + g.setColor(p.color); + g.fillOval((int) x + 7, textY - 9, 8, 8); + x += bulletWidth; + g.setColor(CircuitElm.whiteColor); + g.drawString(vScaleText, (int) x, textY); + x += vScaleWidth; + } + } + textY += 15; + } + - } - + void drawAverage(Graphics g) { - ScopePlot plot = visiblePlots.firstElement(); - int i; - double avg = 0; - int ipa = plot.ptr+scopePointCount-rect.width; - double maxV[] = plot.maxValues; - double minV[] = plot.minValues; - double mid = (maxValue+minValue)/2; - int state = -1; - - // skip zeroes - for (i = 0; i != rect.width; i++) { - int ip = (i+ipa) & (scopePointCount-1); - if (maxV[ip] != 0) { - if (maxV[ip] > mid) - state = 1; - break; - } - } - int firstState = -state; - int start = i; - int end = 0; - int waveCount = 0; - double endAvg = 0; - for (; i != rect.width; i++) { - int ip = (i+ipa) & (scopePointCount-1); - boolean sw = false; - - // switching polarity? - if (state == 1) { - if (maxV[ip] < mid) - sw = true; - } else if (minV[ip] > mid) - sw = true; - - if (sw) { - state = -state; - - // completed a full cycle? - if (firstState == state) { - if (waveCount == 0) { - start = i; - firstState = state; - avg = 0; - } - waveCount++; - end = i; - endAvg = avg; - } - } - if (waveCount > 0) { - double m = (maxV[ip]+minV[ip])*.5; - avg += m; - } - } - if (waveCount > 1) { - avg = (endAvg/(end-start)); - drawInfoText(g, plot.getUnitText(avg) + Locale.LS(" average")); - } + ScopePlot plot = visiblePlots.firstElement(); + int i; + double avg = 0; + int ipa = plot.ptr + scopePointCount - rect.width; + double maxV[] = plot.maxValues; + double minV[] = plot.minValues; + double mid = (maxValue + minValue) / 2; + int state = -1; + + // skip zeroes + for (i = 0; i != rect.width; i++) { + int ip = (i + ipa) & (scopePointCount - 1); + if (maxV[ip] != 0) { + if (maxV[ip] > mid) + state = 1; + break; + } + } + int firstState = -state; + int start = i; + int end = 0; + int waveCount = 0; + double endAvg = 0; + for (; i != rect.width; i++) { + int ip = (i + ipa) & (scopePointCount - 1); + boolean sw = false; + + // switching polarity? + if (state == 1) { + if (maxV[ip] < mid) + sw = true; + } else if (minV[ip] > mid) + sw = true; + + if (sw) { + state = -state; + + // completed a full cycle? + if (firstState == state) { + if (waveCount == 0) { + start = i; + firstState = state; + avg = 0; + } + waveCount++; + end = i; + endAvg = avg; + } + } + if (waveCount > 0) { + double m = (maxV[ip] + minV[ip]) * .5; + avg += m; + } + } + if (waveCount > 1) { + avg = (endAvg / (end - start)); + drawInfoText(g, plot.getUnitText(avg) + Locale.LS(" average")); + } } void drawDutyCycle(Graphics g) { - ScopePlot plot = visiblePlots.firstElement(); - int i; - int ipa = plot.ptr+scopePointCount-rect.width; - double maxV[] = plot.maxValues; - double minV[] = plot.minValues; - double mid = (maxValue+minValue)/2; - int state = -1; - - // skip zeroes - for (i = 0; i != rect.width; i++) { - int ip = (i+ipa) & (scopePointCount-1); - if (maxV[ip] != 0) { - if (maxV[ip] > mid) - state = 1; - break; - } - } - int firstState = 1; - int start = i; - int end = 0; - int waveCount = 0; - int dutyLen = 0; - int middle = 0; - for (; i != rect.width; i++) { - int ip = (i+ipa) & (scopePointCount-1); - boolean sw = false; - - // switching polarity? - if (state == 1) { - if (maxV[ip] < mid) - sw = true; - } else if (minV[ip] > mid) - sw = true; - - if (sw) { - state = -state; - - // completed a full cycle? - if (firstState == state) { - if (waveCount == 0) { - start = end = i; - } else { - end = start; - start = i; - dutyLen = end-middle; - } - waveCount++; - } else - middle = i; - } - } - if (waveCount > 1) { - int duty = 100*dutyLen/(end-start); - drawInfoText(g, Locale.LS("Duty cycle ") + duty + "%"); - } + ScopePlot plot = visiblePlots.firstElement(); + int i; + int ipa = plot.ptr + scopePointCount - rect.width; + double maxV[] = plot.maxValues; + double minV[] = plot.minValues; + double mid = (maxValue + minValue) / 2; + int state = -1; + + // skip zeroes + for (i = 0; i != rect.width; i++) { + int ip = (i + ipa) & (scopePointCount - 1); + if (maxV[ip] != 0) { + if (maxV[ip] > mid) + state = 1; + break; + } + } + int firstState = 1; + int start = i; + int end = 0; + int waveCount = 0; + int dutyLen = 0; + int middle = 0; + for (; i != rect.width; i++) { + int ip = (i + ipa) & (scopePointCount - 1); + boolean sw = false; + + // switching polarity? + if (state == 1) { + if (maxV[ip] < mid) + sw = true; + } else if (minV[ip] > mid) + sw = true; + + if (sw) { + state = -state; + + // completed a full cycle? + if (firstState == state) { + if (waveCount == 0) { + start = end = i; + } else { + end = start; + start = i; + dutyLen = end - middle; + } + waveCount++; + } else + middle = i; + } + } + if (waveCount > 1) { + int duty = 100 * dutyLen / (end - start); + drawInfoText(g, Locale.LS("Duty cycle ") + duty + "%"); + } } // calc frequency if possible and display it void drawFrequency(Graphics g) { - // try to get frequency - // get average - double avg = 0; - int i; - ScopePlot plot = visiblePlots.firstElement(); - int ipa = plot.ptr+scopePointCount-rect.width; - double minV[] = plot.minValues; - double maxV[] = plot.maxValues; - for (i = 0; i != rect.width; i++) { - int ip = (i+ipa) & (scopePointCount-1); - avg += minV[ip]+maxV[ip]; - } - avg /= i*2; - int state = 0; - double thresh = avg*.05; - int oi = 0; - double avperiod = 0; - int periodct = -1; - double avperiod2 = 0; - // count period lengths - for (i = 0; i != rect.width; i++) { - int ip = (i+ipa) & (scopePointCount-1); - double q = maxV[ip]-avg; - int os = state; - if (q < thresh) - state = 1; - else if (q > -thresh) - state = 2; - if (state == 2 && os == 1) { - int pd = i-oi; - oi = i; - // short periods can't be counted properly - if (pd < 12) - continue; - // skip first period, it might be too short - if (periodct >= 0) { - avperiod += pd; - avperiod2 += pd*pd; - } - periodct++; - } - } - avperiod /= periodct; - avperiod2 /= periodct; - double periodstd = Math.sqrt(avperiod2-avperiod*avperiod); - double freq = 1/(avperiod*sim.maxTimeStep*speed); - // don't show freq if standard deviation is too great - if (periodct < 1 || periodstd > 2) - freq = 0; - // System.out.println(freq + " " + periodstd + " " + periodct); - if (freq != 0) - drawInfoText(g, CircuitElm.getUnitText(freq, "Hz")); + // try to get frequency + // get average + double avg = 0; + int i; + ScopePlot plot = visiblePlots.firstElement(); + int ipa = plot.ptr + scopePointCount - rect.width; + double minV[] = plot.minValues; + double maxV[] = plot.maxValues; + for (i = 0; i != rect.width; i++) { + int ip = (i + ipa) & (scopePointCount - 1); + avg += minV[ip] + maxV[ip]; + } + avg /= i * 2; + int state = 0; + double thresh = avg * .05; + int oi = 0; + double avperiod = 0; + int periodct = -1; + double avperiod2 = 0; + // count period lengths + for (i = 0; i != rect.width; i++) { + int ip = (i + ipa) & (scopePointCount - 1); + double q = maxV[ip] - avg; + int os = state; + if (q < thresh) + state = 1; + else if (q > -thresh) + state = 2; + if (state == 2 && os == 1) { + int pd = i - oi; + oi = i; + // short periods can't be counted properly + if (pd < 12) + continue; + // skip first period, it might be too short + if (periodct >= 0) { + avperiod += pd; + avperiod2 += pd * pd; + } + periodct++; + } + } + avperiod /= periodct; + avperiod2 /= periodct; + double periodstd = Math.sqrt(avperiod2 - avperiod * avperiod); + double freq = 1 / (avperiod * sim.maxTimeStep * speed); + // don't show freq if standard deviation is too great + if (periodct < 1 || periodstd > 2) + freq = 0; + // System.out.println(freq + " " + periodstd + " " + periodct); + if (freq != 0) + drawInfoText(g, CircuitElm.getUnitText(freq, "Hz")); } void drawElmInfo(Graphics g) { - String info[] = new String[1]; - getElm().getInfo(info); - int i; - for (i = 0; info[i] != null; i++) - drawInfoText(g, info[i]); + String info[] = new String[1]; + getElm().getInfo(info); + int i; + for (i = 0; info[i] != null; i++) + drawInfoText(g, info[i]); } - + int textY; - + void drawInfoText(Graphics g, String text) { - if (rect.y + rect.height <= textY+5) - return; - g.drawString(text, 0, textY); - textY += 15; + if (rect.y + rect.height <= textY + 5) + return; + g.drawString(text, 0, textY); + textY += 15; } - + void drawInfoTexts(Graphics g) { - g.setColor(CircuitElm.whiteColor); - textY = 10; - - if (visiblePlots.size() == 0) { - if (showElmInfo) - drawElmInfo(g); - return; - } - ScopePlot plot = visiblePlots.firstElement(); - if (showScale) - drawScale(plot, g); + g.setColor(CircuitElm.whiteColor); + textY = 10; + + if (visiblePlots.size() == 0) { + if (showElmInfo) + drawElmInfo(g); + return; + } + ScopePlot plot = visiblePlots.firstElement(); + if (showScale) + drawScale(plot, g); // if (showMax || showMin) // calcMaxAndMin(plot.units); - if (showMax) - drawInfoText(g, "Max="+plot.getUnitText(maxValue)); - if (showMin) { - int ym=rect.height-5; - g.drawString("Min="+plot.getUnitText(minValue), 0, ym); - } - if (showRMS) - drawRMS(g); - if (showAverage) - drawAverage(g); - if (showDutyCycle) - drawDutyCycle(g); - String t = getScopeLabelOrText(true); - if (t != null && t!= "") - drawInfoText(g, t); - if (showFreq) - drawFrequency(g); - if (showElmInfo) - drawElmInfo(g); + if (showMax) + drawInfoText(g, "Max=" + plot.getUnitText(maxValue)); + if (showMin) { + int ym = rect.height - 5; + g.drawString("Min=" + plot.getUnitText(minValue), 0, ym); + } + if (showRMS) + drawRMS(g); + if (showAverage) + drawAverage(g); + if (showDutyCycle) + drawDutyCycle(g); + String t = getScopeLabelOrText(true); + if (t != null && t != "") + drawInfoText(g, t); + if (showFreq) + drawFrequency(g); + if (showElmInfo) + drawElmInfo(g); } String getScopeText() { - // stacked scopes? don't show text - if (stackCount != 1) - return null; - - // multiple elms? don't show text (unless one is selected) - if (selectedPlot < 0 && getSingleElm() == null) - return null; - - // no visible plots? - if (visiblePlots.size() == 0) - return null; - - ScopePlot plot = visiblePlots.firstElement(); - if (selectedPlot >= 0 && visiblePlots.size() > selectedPlot) - plot = visiblePlots.get(selectedPlot); - if (plot.elm == null) - return ""; - else - return plot.elm.getScopeText(plot.value); + // stacked scopes? don't show text + if (stackCount != 1) + return null; + + // multiple elms? don't show text (unless one is selected) + if (selectedPlot < 0 && getSingleElm() == null) + return null; + + // no visible plots? + if (visiblePlots.size() == 0) + return null; + + ScopePlot plot = visiblePlots.firstElement(); + if (selectedPlot >= 0 && visiblePlots.size() > selectedPlot) + plot = visiblePlots.get(selectedPlot); + if (plot.elm == null) + return ""; + else + return plot.elm.getScopeText(plot.value); } String getScopeLabelOrText() { - return getScopeLabelOrText(false); + return getScopeLabelOrText(false); } String getScopeLabelOrText(boolean forInfo) { - String t = text; - if (t == null) { - // if we're drawing the info and showElmInfo is true, return null so we don't print redundant info. - // But don't do that if we're getting the scope label to generate "Add to Existing Scope" menu. - if (forInfo && showElmInfo) - return null; - t = getScopeText(); - if (t==null) - return ""; - return Locale.LS(t); - } - else - return t; + String t = text; + if (t == null) { + // if we're drawing the info and showElmInfo is true, return null so we don't print redundant info. + // But don't do that if we're getting the scope label to generate "Add to Existing Scope" menu. + if (forInfo && showElmInfo) + return null; + t = getScopeText(); + if (t == null) + return ""; + return Locale.LS(t); + } else + return t; } - + void setSpeed(int sp) { - if (sp < 1) - sp = 1; - if (sp > 1024) - sp = 1024; - speed = sp; - resetGraph(); + if (sp < 1) + sp = 1; + if (sp > 1024) + sp = 1024; + speed = sp; + resetGraph(); } - + void properties() { - properties = new ScopePropertiesDialog(sim, this); - CirSim.dialogShowing = properties; + properties = new ScopePropertiesDialog(sim, this); + CirSim.dialogShowing = properties; } - + void speedUp() { - if (speed > 1) { - speed /= 2; - resetGraph(); - } + if (speed > 1) { + speed /= 2; + resetGraph(); + } } void slowDown() { - if (speed < 1024) - speed *= 2; - resetGraph(); + if (speed < 1024) + speed *= 2; + resetGraph(); } - + void setPlotPosition(int plot, int v) { - visiblePlots.get(plot).manVPosition = v; + visiblePlots.get(plot).manVPosition = v; } - + // get scope element, returning null if there's more than one CircuitElm getSingleElm() { - CircuitElm elm = plots.get(0).elm; - int i; - for (i = 1; i < plots.size(); i++) { - if (plots.get(i).elm != elm) - return null; - } - return elm; + CircuitElm elm = plots.get(0).elm; + int i; + for (i = 1; i < plots.size(); i++) { + if (plots.get(i).elm != elm) + return null; + } + return elm; } - + boolean canMenu() { - return (plots.get(0).elm != null); + return (plots.get(0).elm != null); } - + boolean canShowResistance() { - CircuitElm elm = getSingleElm(); - return elm != null && elm.canShowValueInScope(VAL_R); + CircuitElm elm = getSingleElm(); + return elm != null && elm.canShowValueInScope(VAL_R); } - + boolean isShowingVceAndIc() { - return plot2d && plots.size() == 2 && plots.get(0).value == VAL_VCE && plots.get(1).value == VAL_IC; + return plot2d && plots.size() == 2 && plots.get(0).value == VAL_VCE && plots.get(1).value == VAL_IC; } int getFlags() { - int flags = (showI ? 1 : 0) | (showV ? 2 : 0) | - (showMax ? 0 : 4) | // showMax used to be always on - (showFreq ? 8 : 0) | - // In this version we always dump manual settings using the PERPLOT format - (isManualScale() ? (FLAG_MAN_SCALE | FLAG_PERPLOT_MAN_SCALE): 0) | - (plot2d ? 64 : 0) | - (plotXY ? 128 : 0) | (showMin ? 256 : 0) | (showScale? 512:0) | - (showFFT ? 1024 : 0) | (maxScale ? 8192 : 0) | (showRMS ? 16384 : 0) | - (showDutyCycle ? 32768 : 0) | (logSpectrum ? 65536 : 0) | - (showAverage ? (1<<17) : 0) | (showElmInfo ? (1<<20) : 0); - flags |= FLAG_PLOTS; // 4096 - int allPlotFlags = 0; - for (ScopePlot p : plots) { - allPlotFlags |= p.getPlotFlags(); - - } - // If none of our plots has a flag set we will use the old format with no plot flags, or - // else we will set FLAG_PLOTFLAGS and include flags in all plots - flags |= (allPlotFlags !=0) ? FLAG_PERPLOTFLAGS :0; // (1<<18) - return flags; + int flags = (showI ? 1 : 0) | (showV ? 2 : 0) | + (showMax ? 0 : 4) | // showMax used to be always on + (showFreq ? 8 : 0) | + // In this version we always dump manual settings using the PERPLOT format + (isManualScale() ? (FLAG_MAN_SCALE | FLAG_PERPLOT_MAN_SCALE) : 0) | + (plot2d ? 64 : 0) | + (plotXY ? 128 : 0) | (showMin ? 256 : 0) | (showScale ? 512 : 0) | + (showFFT ? 1024 : 0) | (maxScale ? 8192 : 0) | (showRMS ? 16384 : 0) | + (showDutyCycle ? 32768 : 0) | (logSpectrum ? 65536 : 0) | + (showAverage ? (1 << 17) : 0) | (showElmInfo ? (1 << 20) : 0); + flags |= FLAG_PLOTS; // 4096 + int allPlotFlags = 0; + for (ScopePlot p : plots) { + allPlotFlags |= p.getPlotFlags(); + + } + // If none of our plots has a flag set we will use the old format with no plot flags, or + // else we will set FLAG_PLOTFLAGS and include flags in all plots + flags |= (allPlotFlags != 0) ? FLAG_PERPLOTFLAGS : 0; // (1<<18) + return flags; } - - + String dump() { - ScopePlot vPlot = plots.get(0); - - CircuitElm elm = vPlot.elm; - if (elm == null) - return null; - int flags = getFlags(); - int eno = sim.locateElm(elm); - if (eno < 0) - return null; - String x = "o " + eno + " " + - vPlot.scopePlotSpeed + " " + vPlot.value + " " - + exportAsDecOrHex(flags, FLAG_PERPLOTFLAGS) + " " + - scale[UNITS_V] + " " + scale[UNITS_A] + " " + position + " " + - plots.size(); - int i; - for (i = 0; i < plots.size(); i++) { - ScopePlot p = plots.get(i); - if ((flags & FLAG_PERPLOTFLAGS) !=0) - x += " " + Integer.toHexString(p.getPlotFlags()); // NB always export in Hex (no prefix) - if (i > 0) - x += " " + sim.locateElm(p.elm) + " " + p.value; - // dump scale if units are not V or A - if (p.units > UNITS_A) - x += " " + scale[p.units]; - if (isManualScale()) {// In this version we always dump manual settings using the PERPLOT format - x += " " + p.manScale + " " - + p.manVPosition; - } - } - if (text != null) - x += " " + CustomLogicModel.escape(text); - return x; + ScopePlot vPlot = plots.get(0); + + CircuitElm elm = vPlot.elm; + if (elm == null) + return null; + int flags = getFlags(); + int eno = sim.locateElm(elm); + if (eno < 0) + return null; + String x = "o " + eno + " " + + vPlot.scopePlotSpeed + " " + vPlot.value + " " + + exportAsDecOrHex(flags, FLAG_PERPLOTFLAGS) + " " + + scale[UNITS_V] + " " + scale[UNITS_A] + " " + position + " " + + plots.size(); + int i; + for (i = 0; i < plots.size(); i++) { + ScopePlot p = plots.get(i); + if ((flags & FLAG_PERPLOTFLAGS) != 0) + x += " " + Integer.toHexString(p.getPlotFlags()); // NB always export in Hex (no prefix) + if (i > 0) + x += " " + sim.locateElm(p.elm) + " " + p.value; + // dump scale if units are not V or A + if (p.units > UNITS_A) + x += " " + scale[p.units]; + if (isManualScale()) {// In this version we always dump manual settings using the PERPLOT format + x += " " + p.manScale + " " + + p.manVPosition; + } + } + if (text != null) + x += " " + CustomLogicModel.escape(text); + return x; } - + void undump(StringTokenizer st) { - initialize(); - int e = new Integer(st.nextToken()).intValue(); - if (e == -1) - return; - CircuitElm ce = sim.getElm(e); - setElm(ce); - speed = new Integer(st.nextToken()).intValue(); - int value = new Integer(st.nextToken()).intValue(); - - // fix old value for VAL_POWER which doesn't work for transistors (because it's the same as VAL_IB) - if (!(ce instanceof TransistorElm) && value == VAL_POWER_OLD) - value = VAL_POWER; - - int flags = importDecOrHex(st.nextToken()); - scale[UNITS_V] = new Double(st.nextToken()).doubleValue(); - scale[UNITS_A] = new Double(st.nextToken()).doubleValue(); - if (scale[UNITS_V] == 0) - scale[UNITS_V] = .5; - if (scale[UNITS_A] == 0) - scale[UNITS_A] = 1; - scaleX = scale[UNITS_V]; - scaleY = scale[UNITS_A]; - scale[UNITS_OHMS] = scale[UNITS_W] = scale[UNITS_V]; - text = null; - boolean plot2dFlag = (flags & 64) != 0; - boolean hasPlotFlags = (flags & FLAG_PERPLOTFLAGS) != 0; - if ((flags & FLAG_PLOTS) != 0) { - // new-style dump - try { - position = Integer.parseInt(st.nextToken()); - int sz = Integer.parseInt(st.nextToken()); - int i; - int u = ce.getScopeUnits(value); - if (u > UNITS_A) - scale[u] = Double.parseDouble(st.nextToken()); - setValue(value); - // setValue(0) creates an extra plot for current, so remove that - while (plots.size() > 1) - plots.removeElementAt(1); - - - int plotFlags = 0; - for (i = 0; i != sz; i++) { - if (hasPlotFlags) - plotFlags=Integer.parseInt(st.nextToken(), 16); // Import in hex (no prefix) - if (i!=0) { - int ne = Integer.parseInt(st.nextToken()); - int val = Integer.parseInt(st.nextToken()); - CircuitElm elm = sim.getElm(ne); - u = elm.getScopeUnits(val); - if (u > UNITS_A) - scale[u] = Double.parseDouble(st.nextToken()); - plots.add(new ScopePlot(elm, u, val, getManScaleFromMaxScale(u, false))); - } - ScopePlot p = plots.get(i); - p.acCoupled = (plotFlags & ScopePlot.FLAG_AC) != 0; - if ( (flags & FLAG_PERPLOT_MAN_SCALE) != 0) { - p.manScaleSet = true; - p.manScale=Double.parseDouble(st.nextToken()); - p.manVPosition=Integer.parseInt(st.nextToken()); - } - } - while (st.hasMoreTokens()) { - if (text == null) - text = st.nextToken(); - else - text += " " + st.nextToken(); - } - } catch (Exception ee) { - } - } else { - // old-style dump - CircuitElm yElm = null; - int ivalue = 0; - try { - position = new Integer(st.nextToken()).intValue(); - int ye = -1; - if ((flags & FLAG_YELM) != 0) { - ye = new Integer(st.nextToken()).intValue(); - if (ye != -1) - yElm = sim.getElm(ye); - // sinediode.txt has yElm set to something even though there's no xy plot...? - if (!plot2dFlag) - yElm = null; - } - if ((flags & FLAG_IVALUE) !=0) { - ivalue = new Integer(st.nextToken()).intValue(); - } - while (st.hasMoreTokens()) { - if (text == null) - text = st.nextToken(); - else - text += " " + st.nextToken(); - } - } catch (Exception ee) { - } - setValues(value, ivalue, sim.getElm(e), yElm); - } - if (text != null) - text = CustomLogicModel.unescape(text); - plot2d = plot2dFlag; - setFlags(flags); + initialize(); + int e = new Integer(st.nextToken()).intValue(); + if (e == -1) + return; + CircuitElm ce = sim.getElm(e); + setElm(ce); + speed = new Integer(st.nextToken()).intValue(); + int value = new Integer(st.nextToken()).intValue(); + + // fix old value for VAL_POWER which doesn't work for transistors (because it's the same as VAL_IB) + if (!(ce instanceof TransistorElm) && value == VAL_POWER_OLD) + value = VAL_POWER; + + int flags = importDecOrHex(st.nextToken()); + scale[UNITS_V] = new Double(st.nextToken()).doubleValue(); + scale[UNITS_A] = new Double(st.nextToken()).doubleValue(); + if (scale[UNITS_V] == 0) + scale[UNITS_V] = .5; + if (scale[UNITS_A] == 0) + scale[UNITS_A] = 1; + scaleX = scale[UNITS_V]; + scaleY = scale[UNITS_A]; + scale[UNITS_OHMS] = scale[UNITS_W] = scale[UNITS_V]; + text = null; + boolean plot2dFlag = (flags & 64) != 0; + boolean hasPlotFlags = (flags & FLAG_PERPLOTFLAGS) != 0; + if ((flags & FLAG_PLOTS) != 0) { + // new-style dump + try { + position = Integer.parseInt(st.nextToken()); + int sz = Integer.parseInt(st.nextToken()); + int i; + int u = ce.getScopeUnits(value); + if (u > UNITS_A) + scale[u] = Double.parseDouble(st.nextToken()); + setValue(value); + // setValue(0) creates an extra plot for current, so remove that + while (plots.size() > 1) + plots.removeElementAt(1); + + + int plotFlags = 0; + for (i = 0; i != sz; i++) { + if (hasPlotFlags) + plotFlags = Integer.parseInt(st.nextToken(), 16); // Import in hex (no prefix) + if (i != 0) { + int ne = Integer.parseInt(st.nextToken()); + int val = Integer.parseInt(st.nextToken()); + CircuitElm elm = sim.getElm(ne); + u = elm.getScopeUnits(val); + if (u > UNITS_A) + scale[u] = Double.parseDouble(st.nextToken()); + plots.add(new ScopePlot(elm, u, val, getManScaleFromMaxScale(u, false))); + } + ScopePlot p = plots.get(i); + p.acCoupled = (plotFlags & ScopePlot.FLAG_AC) != 0; + if ((flags & FLAG_PERPLOT_MAN_SCALE) != 0) { + p.manScaleSet = true; + p.manScale = Double.parseDouble(st.nextToken()); + p.manVPosition = Integer.parseInt(st.nextToken()); + } + } + while (st.hasMoreTokens()) { + if (text == null) + text = st.nextToken(); + else + text += " " + st.nextToken(); + } + } catch (Exception ee) { + } + } else { + // old-style dump + CircuitElm yElm = null; + int ivalue = 0; + try { + position = new Integer(st.nextToken()).intValue(); + int ye = -1; + if ((flags & FLAG_YELM) != 0) { + ye = new Integer(st.nextToken()).intValue(); + if (ye != -1) + yElm = sim.getElm(ye); + // sinediode.txt has yElm set to something even though there's no xy plot...? + if (!plot2dFlag) + yElm = null; + } + if ((flags & FLAG_IVALUE) != 0) { + ivalue = new Integer(st.nextToken()).intValue(); + } + while (st.hasMoreTokens()) { + if (text == null) + text = st.nextToken(); + else + text += " " + st.nextToken(); + } + } catch (Exception ee) { + } + setValues(value, ivalue, sim.getElm(e), yElm); + } + if (text != null) + text = CustomLogicModel.unescape(text); + plot2d = plot2dFlag; + setFlags(flags); } - + void setFlags(int flags) { - showI = (flags & 1) != 0; - showV = (flags & 2) != 0; - showMax = (flags & 4) == 0; - showFreq = (flags & 8) != 0; - manualScale = (flags & FLAG_MAN_SCALE) != 0; - plotXY = (flags & 128) != 0; - showMin = (flags & 256) != 0; - showScale = (flags & 512) !=0; - showFFT((flags & 1024) != 0); - maxScale = (flags & 8192) != 0; - showRMS = (flags & 16384) != 0; - showDutyCycle = (flags & 32768) != 0; - logSpectrum = (flags & 65536) != 0; - showAverage = (flags & (1<<17)) != 0; - showElmInfo = (flags & (1<<20)) != 0; + showI = (flags & 1) != 0; + showV = (flags & 2) != 0; + showMax = (flags & 4) == 0; + showFreq = (flags & 8) != 0; + manualScale = (flags & FLAG_MAN_SCALE) != 0; + plotXY = (flags & 128) != 0; + showMin = (flags & 256) != 0; + showScale = (flags & 512) != 0; + showFFT((flags & 1024) != 0); + maxScale = (flags & 8192) != 0; + showRMS = (flags & 16384) != 0; + showDutyCycle = (flags & 32768) != 0; + logSpectrum = (flags & 65536) != 0; + showAverage = (flags & (1 << 17)) != 0; + showElmInfo = (flags & (1 << 20)) != 0; } - + void saveAsDefault() { Storage stor = Storage.getLocalStorageIfSupported(); if (stor == null) return; - ScopePlot vPlot = plots.get(0); - int flags = getFlags(); - - // store current scope settings as default. 1 is a version code - stor.setItem("scopeDefaults", "1 " + flags + " " + vPlot.scopePlotSpeed); - CirSim.console("saved defaults " + flags); + ScopePlot vPlot = plots.get(0); + int flags = getFlags(); + + // store current scope settings as default. 1 is a version code + stor.setItem("scopeDefaults", "1 " + flags + " " + vPlot.scopePlotSpeed); + CirSim.console("saved defaults " + flags); } boolean loadDefaults() { @@ -1996,82 +2017,82 @@ boolean loadDefaults() { speed = Integer.parseInt(arr[2]); return true; } - + void allocImage() { - if (imageCanvas != null) { - imageCanvas.setWidth(rect.width + "PX"); - imageCanvas.setHeight(rect.height + "PX"); - imageCanvas.setCoordinateSpaceWidth(rect.width); - imageCanvas.setCoordinateSpaceHeight(rect.height); - clear2dView(); - } + if (imageCanvas != null) { + imageCanvas.setWidth(rect.width + "PX"); + imageCanvas.setHeight(rect.height + "PX"); + imageCanvas.setCoordinateSpaceWidth(rect.width); + imageCanvas.setCoordinateSpaceHeight(rect.height); + clear2dView(); + } } - + void handleMenu(String mi, boolean state) { - if (mi == "maxscale") - maxScale(); - if (mi == "showvoltage") - showVoltage(state); - if (mi == "showcurrent") - showCurrent(state); - if (mi=="showscale") - showScale(state); - if (mi == "showpeak") - showMax(state); - if (mi == "shownegpeak") - showMin(state); - if (mi == "showfreq") - showFreq(state); - if (mi == "showfft") - showFFT(state); - if (mi == "logspectrum") - logSpectrum = state; - if (mi == "showrms") - showRMS = state; - if (mi == "showaverage") - showAverage = state; - if (mi == "showduty") - showDutyCycle = state; - if (mi == "showelminfo") - showElmInfo = state; - if (mi == "showpower") - setValue(VAL_POWER); - if (mi == "showib") - setValue(VAL_IB); - if (mi == "showic") - setValue(VAL_IC); - if (mi == "showie") - setValue(VAL_IE); - if (mi == "showvbe") - setValue(VAL_VBE); - if (mi == "showvbc") - setValue(VAL_VBC); - if (mi == "showvce") - setValue(VAL_VCE); - if (mi == "showvcevsic") { - plot2d = true; - plotXY = false; - setValues(VAL_VCE, VAL_IC, getElm(), null); - resetGraph(); - } - - if (mi == "showvvsi") { - plot2d = state; - plotXY = false; - resetGraph(); - } - if (mi == "manualscale") - setManualScale(state, true); - if (mi == "plotxy") { - plotXY = plot2d = state; - if (plot2d) - plots = visiblePlots; - if (plot2d && plots.size() == 1) - selectY(); - resetGraph(); - } - if (mi == "showresistance") - setValue(VAL_R); + if (mi == "maxscale") + maxScale(); + if (mi == "showvoltage") + showVoltage(state); + if (mi == "showcurrent") + showCurrent(state); + if (mi == "showscale") + showScale(state); + if (mi == "showpeak") + showMax(state); + if (mi == "shownegpeak") + showMin(state); + if (mi == "showfreq") + showFreq(state); + if (mi == "showfft") + showFFT(state); + if (mi == "logspectrum") + logSpectrum = state; + if (mi == "showrms") + showRMS = state; + if (mi == "showaverage") + showAverage = state; + if (mi == "showduty") + showDutyCycle = state; + if (mi == "showelminfo") + showElmInfo = state; + if (mi == "showpower") + setValue(VAL_POWER); + if (mi == "showib") + setValue(VAL_IB); + if (mi == "showic") + setValue(VAL_IC); + if (mi == "showie") + setValue(VAL_IE); + if (mi == "showvbe") + setValue(VAL_VBE); + if (mi == "showvbc") + setValue(VAL_VBC); + if (mi == "showvce") + setValue(VAL_VCE); + if (mi == "showvcevsic") { + plot2d = true; + plotXY = false; + setValues(VAL_VCE, VAL_IC, getElm(), null); + resetGraph(); + } + + if (mi == "showvvsi") { + plot2d = state; + plotXY = false; + resetGraph(); + } + if (mi == "manualscale") + setManualScale(state, true); + if (mi == "plotxy") { + plotXY = plot2d = state; + if (plot2d) + plots = visiblePlots; + if (plot2d && plots.size() == 1) + selectY(); + resetGraph(); + } + if (mi == "showresistance") + setValue(VAL_R); } // void select() { @@ -2083,31 +2104,31 @@ void handleMenu(String mi, boolean state) { // } void selectY() { - CircuitElm yElm = (plots.size() == 2) ? plots.get(1).elm : null; - int e = (yElm == null) ? -1 : sim.locateElm(yElm); - int firstE = e; - while (true) { - for (e++; e < sim.elmList.size(); e++) { - CircuitElm ce = sim.getElm(e); - if ((ce instanceof OutputElm || ce instanceof ProbeElm) && - ce != plots.get(0).elm) { - yElm = ce; - if (plots.size() == 1) - plots.add(new ScopePlot(yElm, UNITS_V)); - else { - plots.get(1).elm = yElm; - plots.get(1).units = UNITS_V; - } - return; - } - } - if (firstE == -1) - return; - e = firstE = -1; - } - // not reached + CircuitElm yElm = (plots.size() == 2) ? plots.get(1).elm : null; + int e = (yElm == null) ? -1 : sim.locateElm(yElm); + int firstE = e; + while (true) { + for (e++; e < sim.elmList.size(); e++) { + CircuitElm ce = sim.getElm(e); + if ((ce instanceof OutputElm || ce instanceof ProbeElm) && + ce != plots.get(0).elm) { + yElm = ce; + if (plots.size() == 1) + plots.add(new ScopePlot(yElm, UNITS_V)); + else { + plots.get(1).elm = yElm; + plots.get(1).units = UNITS_V; + } + return; + } + } + if (firstE == -1) + return; + e = firstE = -1; + } + // not reached } - + void onMouseWheel(MouseWheelEvent e) { wheelDeltaY += e.getDeltaY(); if (wheelDeltaY > 5) { @@ -2116,80 +2137,81 @@ void onMouseWheel(MouseWheelEvent e) { } if (wheelDeltaY < -5) { speedUp(); - wheelDeltaY = 0; - } + wheelDeltaY = 0; + } } - + CircuitElm getElm() { - if (selectedPlot >= 0 && visiblePlots.size() > selectedPlot) - return visiblePlots.get(selectedPlot).elm; - return visiblePlots.size() > 0 ? visiblePlots.get(0).elm : plots.get(0).elm; + if (selectedPlot >= 0 && visiblePlots.size() > selectedPlot) + return visiblePlots.get(selectedPlot).elm; + return visiblePlots.size() > 0 ? visiblePlots.get(0).elm : plots.get(0).elm; } boolean viewingWire() { - int i; - for (i = 0; i != plots.size(); i++) - if (plots.get(i).elm instanceof WireElm) - return true; - return false; + int i; + for (i = 0; i != plots.size(); i++) + if (plots.get(i).elm instanceof WireElm) + return true; + return false; } - + CircuitElm getXElm() { - return getElm(); + return getElm(); } + CircuitElm getYElm() { - if (plots.size() == 2) - return plots.get(1).elm; - return null; + if (plots.size() == 2) + return plots.get(1).elm; + return null; } - + boolean needToRemove() { - boolean ret = true; - boolean removed = false; - int i; - for (i = 0; i != plots.size(); i++) { - ScopePlot plot = plots.get(i); - if (sim.locateElm(plot.elm) < 0) { - plots.remove(i--); - removed = true; - } else - ret = false; - } - if (removed) - calcVisiblePlots(); - return ret; + boolean ret = true; + boolean removed = false; + int i; + for (i = 0; i != plots.size(); i++) { + ScopePlot plot = plots.get(i); + if (sim.locateElm(plot.elm) < 0) { + plots.remove(i--); + removed = true; + } else + ret = false; + } + if (removed) + calcVisiblePlots(); + return ret; } public boolean isManualScale() { - return manualScale; + return manualScale; } - + public double getManScaleFromMaxScale(int units, boolean roundUp) { - // When the user manually switches to manual scale (and we don't already have a setting) then - // call with "roundUp=true" to get a "sensible" suggestion for the scale. When importing from - // a legacy file then call with "roundUp=false" to stay as close as possible to the old presentation - double s =scale[units]; - if ( units > UNITS_A) - s = 0.5*s; - if (roundUp) - return ScopePropertiesDialog.nextHighestScale((2*s)/(double)(manDivisions)); - else - return (2*s)/(double)(manDivisions); + // When the user manually switches to manual scale (and we don't already have a setting) then + // call with "roundUp=true" to get a "sensible" suggestion for the scale. When importing from + // a legacy file then call with "roundUp=false" to stay as close as possible to the old presentation + double s = scale[units]; + if (units > UNITS_A) + s = 0.5 * s; + if (roundUp) + return ScopePropertiesDialog.nextHighestScale((2 * s) / (double) (manDivisions)); + else + return (2 * s) / (double) (manDivisions); } - + static String exportAsDecOrHex(int v, int thresh) { - // If v>=thresh then export as hex value prefixed by "x", else export as decimal - // Allows flags to be exported as dec if in an old value (for compatibility) or in hex if new value - if (v>=thresh) - return "x"+Integer.toHexString(v); - else - return Integer.toString(v); + // If v>=thresh then export as hex value prefixed by "x", else export as decimal + // Allows flags to be exported as dec if in an old value (for compatibility) or in hex if new value + if (v >= thresh) + return "x" + Integer.toHexString(v); + else + return Integer.toString(v); } - + static int importDecOrHex(String s) { - if (s.charAt(0) == 'x') - return Integer.parseInt(s.substring(1), 16); - else - return Integer.parseInt(s); + if (s.charAt(0) == 'x') + return Integer.parseInt(s.substring(1), 16); + else + return Integer.parseInt(s); } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/ScopeElm.java b/src/main/java/com/lushprojects/circuitjs1/client/ScopeElm.java index 98d218f..43bc2af 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/ScopeElm.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/ScopeElm.java @@ -19,102 +19,113 @@ package com.lushprojects.circuitjs1.client; -class ScopeElm extends CircuitElm { - +public class ScopeElm extends CircuitElm { + Scope elmScope; public ScopeElm(int xx, int yy) { - super(xx, yy); - noDiagonal=false; - x2=x+128; - y2=y+64; - elmScope = new Scope(sim); - setPoints(); + super(xx, yy); + noDiagonal = false; + x2 = x + 128; + y2 = y + 64; + elmScope = new Scope(sim); + setPoints(); } - + public void setScopeElm(CircuitElm e) { - elmScope.setElm(e); - elmScope.resetGraph(); + elmScope.setElm(e); + elmScope.resetGraph(); } - + public ScopeElm(int xa, int ya, int xb, int yb, int f, - StringTokenizer st) { - super(xa, ya, xb, yb, f); - noDiagonal=false; - String sStr = st.nextToken(); - StringTokenizer sst = new StringTokenizer(sStr, "_"); - elmScope = new Scope(sim); - elmScope.undump(sst); - setPoints(); - elmScope.resetGraph(); + StringTokenizer st) { + super(xa, ya, xb, yb, f); + noDiagonal = false; + String sStr = st.nextToken(); + StringTokenizer sst = new StringTokenizer(sStr, "_"); + elmScope = new Scope(sim); + elmScope.undump(sst); + setPoints(); + elmScope.resetGraph(); } - + public void setScopeRect() { - int i1 = sim.transformX(min(x,x2)); - int i2 = sim.transformX(max(x,x2)); - int j1 = sim.transformY(min(y,y2)); - int j2 = sim.transformY(max(y,y2)); - Rectangle r = new Rectangle(i1,j1,i2-i1, j2-j1); - if (!r.equals(elmScope.rect)) - elmScope.setRect(r); + int i1 = sim.transformX(min(x, x2)); + int i2 = sim.transformX(max(x, x2)); + int j1 = sim.transformY(min(y, y2)); + int j2 = sim.transformY(max(y, y2)); + Rectangle r = new Rectangle(i1, j1, i2 - i1, j2 - j1); + if (!r.equals(elmScope.rect)) + elmScope.setRect(r); } - + public void setPoints() { - super.setPoints(); - setScopeRect(); + super.setPoints(); + setScopeRect(); } - - public void setElmScope( Scope s) { - elmScope=s; + + public void setElmScope(Scope s) { + elmScope = s; } - - + + public void stepScope() { - elmScope.timeStep(); + elmScope.timeStep(); } - + public void reset() { - super.reset(); - elmScope.resetGraph(true); + super.reset(); + elmScope.resetGraph(true); } - + public void clearElmScope() { - elmScope = null; + elmScope = null; + } + + boolean canViewInScope() { + return false; + } + + int getDumpType() { + return 403; } - - boolean canViewInScope() { return false; } - - int getDumpType() { return 403; } public String dump() { - String dumpStr = super.dump(); - String elmDump = elmScope.dump(); - if (elmDump == null) - return null; - String sStr = elmDump.replace(' ', '_'); - sStr = sStr.replaceFirst("o_", ""); // remove unused prefix for embedded Scope - return dumpStr + " " + sStr; + String dumpStr = super.dump(); + String elmDump = elmScope.dump(); + if (elmDump == null) + return null; + String sStr = elmDump.replace(' ', '_'); + sStr = sStr.replaceFirst("o_", ""); // remove unused prefix for embedded Scope + return dumpStr + " " + sStr; } - + void draw(Graphics g) { - g.setColor(needsHighlight() ? selectColor : whiteColor); - g.context.save(); - // setTransform() doesn't work in version of canvas2svg we are using - g.context.scale(1/sim.transform[0], 1/sim.transform[3]); - g.context.translate(-sim.transform[4], -sim.transform[5]); - //g.context.scale(CirSim.devicePixelRatio(), CirSim.devicePixelRatio()); + g.setColor(needsHighlight() ? selectColor : whiteColor); + g.context.save(); + // setTransform() doesn't work in version of canvas2svg we are using + g.context.scale(1 / sim.transform[0], 1 / sim.transform[3]); + g.context.translate(-sim.transform[4], -sim.transform[5]); + //g.context.scale(CirSim.devicePixelRatio(), CirSim.devicePixelRatio()); - setScopeRect(); - elmScope.position = -1; - elmScope.draw(g); - g.context.restore(); - setBbox(point1, point2, 0); - drawPosts(g); + setScopeRect(); + elmScope.position = -1; + elmScope.draw(g); + g.context.restore(); + setBbox(point1, point2, 0); + drawPosts(g); } - - int getPostCount() { return 0; } - int getNumHandles() { return 2; } - - void selectScope(int mx, int my) { elmScope.selectScope(mx, my); } + + int getPostCount() { + return 0; + } + + int getNumHandles() { + return 2; + } + + void selectScope(int mx, int my) { + elmScope.selectScope(mx, my); + } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/ScopePopupMenu.java b/src/main/java/com/lushprojects/circuitjs1/client/ScopePopupMenu.java index 1d7a72b..cf46296 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/ScopePopupMenu.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/ScopePopupMenu.java @@ -24,7 +24,7 @@ import com.lushprojects.circuitjs1.client.util.Locale; public class ScopePopupMenu { - + private MenuBar m; private MenuItem removeScopeItem; private CheckboxMenuItem maxScaleItem; @@ -36,35 +36,35 @@ public class ScopePopupMenu { private MenuItem propertiesItem; private MenuItem dockItem; private MenuItem undockItem; - + ScopePopupMenu() { - m = new MenuBar(true); - m.addItem(removeScopeItem = new CheckboxAlignedMenuItem(Locale.LS("Remove Scope"),new MyCommand("scopepop", "remove"))); - m.addItem(dockItem = new CheckboxAlignedMenuItem(Locale.LS("Dock Scope"),new MyCommand("scopepop", "dock"))); - m.addItem(undockItem = new CheckboxAlignedMenuItem(Locale.LS("Undock Scope"),new MyCommand("scopepop", "undock"))); - m.addItem(maxScaleItem = new CheckboxMenuItem(Locale.LS("Max Scale"), new MyCommand("scopepop", "maxscale"))); - m.addItem(stackItem = new CheckboxAlignedMenuItem(Locale.LS("Stack"), new MyCommand("scopepop", "stack"))); - m.addItem(unstackItem = new CheckboxAlignedMenuItem(Locale.LS("Unstack"), new MyCommand("scopepop", "unstack"))); - m.addItem(combineItem = new CheckboxAlignedMenuItem(Locale.LS("Combine"), new MyCommand("scopepop", "combine"))); - m.addItem(removePlotItem = new CheckboxAlignedMenuItem(Locale.LS("Remove Plot"),new MyCommand("scopepop", "removeplot"))); - m.addItem(resetItem = new CheckboxAlignedMenuItem(Locale.LS("Reset"), new MyCommand("scopepop", "reset"))); - m.addItem(propertiesItem = new CheckboxAlignedMenuItem(Locale.LS("Properties..."), new MyCommand("scopepop", "properties"))); + m = new MenuBar(true); + m.addItem(removeScopeItem = new CheckboxAlignedMenuItem(Locale.LS("Remove Scope"), new MyCommand("scopepop", "remove"))); + m.addItem(dockItem = new CheckboxAlignedMenuItem(Locale.LS("Dock Scope"), new MyCommand("scopepop", "dock"))); + m.addItem(undockItem = new CheckboxAlignedMenuItem(Locale.LS("Undock Scope"), new MyCommand("scopepop", "undock"))); + m.addItem(maxScaleItem = new CheckboxMenuItem(Locale.LS("Max Scale"), new MyCommand("scopepop", "maxscale"))); + m.addItem(stackItem = new CheckboxAlignedMenuItem(Locale.LS("Stack"), new MyCommand("scopepop", "stack"))); + m.addItem(unstackItem = new CheckboxAlignedMenuItem(Locale.LS("Unstack"), new MyCommand("scopepop", "unstack"))); + m.addItem(combineItem = new CheckboxAlignedMenuItem(Locale.LS("Combine"), new MyCommand("scopepop", "combine"))); + m.addItem(removePlotItem = new CheckboxAlignedMenuItem(Locale.LS("Remove Plot"), new MyCommand("scopepop", "removeplot"))); + m.addItem(resetItem = new CheckboxAlignedMenuItem(Locale.LS("Reset"), new MyCommand("scopepop", "reset"))); + m.addItem(propertiesItem = new CheckboxAlignedMenuItem(Locale.LS("Properties..."), new MyCommand("scopepop", "properties"))); } - - void doScopePopupChecks( boolean floating, boolean canstack, boolean cancombine, boolean canunstack, Scope s) { - maxScaleItem.setState(s.maxScale); - stackItem.setVisible(!floating); - stackItem.setEnabled(canstack); - unstackItem.setVisible(!floating); - unstackItem.setEnabled(canunstack); - combineItem.setVisible(!floating); - combineItem.setEnabled(cancombine); - dockItem.setVisible(floating); - undockItem.setVisible(!floating); + + void doScopePopupChecks(boolean floating, boolean canstack, boolean cancombine, boolean canunstack, Scope s) { + maxScaleItem.setState(s.maxScale); + stackItem.setVisible(!floating); + stackItem.setEnabled(canstack); + unstackItem.setVisible(!floating); + unstackItem.setEnabled(canunstack); + combineItem.setVisible(!floating); + combineItem.setEnabled(cancombine); + dockItem.setVisible(floating); + undockItem.setVisible(!floating); } - - + + public MenuBar getMenuBar() { - return m; + return m; } } diff --git a/src/main/java/com/lushprojects/circuitjs1/client/ScopePropertiesDialog.java b/src/main/java/com/lushprojects/circuitjs1/client/ScopePropertiesDialog.java index a06c9b5..ef75910 100644 --- a/src/main/java/com/lushprojects/circuitjs1/client/ScopePropertiesDialog.java +++ b/src/main/java/com/lushprojects/circuitjs1/client/ScopePropertiesDialog.java @@ -25,663 +25,658 @@ import java.util.Vector; -class ScopeCheckBox extends CheckBox { +public class ScopeCheckBox extends CheckBox { String menuCmd; - + ScopeCheckBox(String text, String menu) { - super(text); - menuCmd = menu; + super(text); + menuCmd = menu; } - + void setValue(boolean x) { - if (getValue() == x) - return; - super.setValue(x); + if (getValue() == x) + return; + super.setValue(x); } } - public class ScopePropertiesDialog extends Dialog implements ValueChangeHandler { - -Panel fp, channelButtonsp, channelSettingsp; -HorizontalPanel hp; -HorizontalPanel vModep; -CirSim sim; -//RichTextArea textBox; -TextArea textArea; -RadioButton autoButton, maxButton, manualButton; -RadioButton acButton, dcButton; -CheckBox scaleBox, voltageBox, currentBox, powerBox, peakBox, negPeakBox, freqBox, spectrumBox, manualScaleBox; -CheckBox rmsBox, dutyBox, viBox, xyBox, resistanceBox, ibBox, icBox, ieBox, vbeBox, vbcBox, vceBox, vceIcBox, logSpectrumBox, averageBox; -CheckBox elmInfoBox; -TextBox labelTextBox, manualScaleTextBox; -Button applyButton, scaleUpButton, scaleDownButton; -Scrollbar speedBar,positionBar; -Scope scope; -Grid grid, vScaleGrid, hScaleGrid; -int nx, ny; -Label scopeSpeedLabel, manualScaleLabel,vScaleList, manualScaleId, positionLabel; -expandingLabel vScaleLabel, hScaleLabel; -Vector