Circuit Simulator

Aaron H. Graber, Computer Engineering

Project Advisor: Dr. Anthony Richardson

March 27, 2017 Evansville, Indiana Table of Contents

I. Introduction II. Problem Statement III. Project Design A. Design – GUI B. Design – Controller Class . Design – Circuit Class . Design – FileUtility E. Design – Constraints F. Design – Costs G. Design – Standards IV. Results V. Conclusion

List of Figures

1. GUI example 2. Starting the program 3. FXML Functional Methods 4. TreeItem example 5. Class Structure 6. Drawing example 7. Dialog example 8. Dialog Result Conversion 9. Class Differentiation 10. Parsing through the input file 11. Checking for intersections 12. Circuit with SPICE translation 13. Setting the Current Working Directory (CWD) 14. FileChooser Dialog 15. FileChooser Opening the Dialog 16. Text file format 17. LTSpice example circuit 18. LTSpice example circuit output 19. Program circuit output

I. Introduction

In the area of circuits and electronics, not every hardware design is going to work as first

thought. Mistakes are possible and other unforeseeable results may arise, especially for those

still learning the profession. For this reason, circuit simulators where created to get theoretical

results and see how the circuit behaves. For the design of this project it was split into several

user defined classes to create a working product. First off, the graphical allows the

user to create custom defined circuit, and also implements an open/save feature allowing the user

to save their work. Another feature is that the program executes a SPICE interpreter to allow the

user to view the theoretical results of the circuit they created. Overall, this project allows the

user to simulate any circuit that is created with this program.

II. Problem Statement

Overall this project is meant to be a free, open source, cross platform, graphically

focused, EDA tool suite. Basically, it is going to be a circuit simulator that lets you build the

circuits with visual components. There are many quality circuit simulators available and the one

that is favored by most of the professors at the University of Evansville is LTSpice [1].

Hopefully this project will produce a program that can compare to how smooth LTSpice operates

and how well it performs circuit simulation. However, LTSpice isn’t completely user friendly

and it is necessary to know the ins and outs of the program to preform even transient analysis,

and there are many features that are hidden underneath the surface. This is one of the main

reasons why there is a need to create an easy to use circuit simulator that also spans several

operating systems. Listed below were some features that had been planned for the program.

However, due to some time constraints some of them remain to be unimplemented. • “Workspace” or area where circuit components can be placed for the user

• Toolbox, list of all the components that are currently implemented in the program

• Interpreter for the SPICE language[4] so that circuit analysis can be completed and

values can be calculated

• Graphical charts for the data

• Ability to port in spice netlists for the SPICE language[4] to add in more components

• Port the program to Linux and Mac OS systems

• Self-explanatory GUI

III. Project Design

The design of this project is split into a multitude of pieces to simplify the process of

programming everything. The main pieces are split into several main groups; the GUI or

graphical user interface, the circuit workspace, file input and output, the creation of SPICE files,

and the execution of and results from the SPICE file. The project was created in Intellij Idea by

Jet Brains, which is a Java IDE. Intellij allows JavaFX projects to be created which is software

platform for creating desktop applications and is basically the standard GUI library for Java SE

[2].

A. Design - GUI

The GUI was created as a .fxml file in an extension to Intellij called JavaFX Scene

Builder. This application allows GUI components to be dragged and dropped so the user can

create their own custom GUI easily. Figure 1 shows what the GUI looks like in the Scene Builder designer. In Scene Builder, the

different components of the GUI can

be named, properties can be changed,

events can be created, and the overall

layout can be easily changed. So as

Figure 1 GUI example figure 1 shows, the current GUI has a menu at the top, a toolbar beneath that, a tabbed pane under the toolbar, and on the right, is a

blank rectangle that will later hold all the components. All of this occurs in the .fxml file and

none of it must be programmed; however, all GUI events, like a button press, must be entered later as a function. These event functions are linked to the .fxml file. The GUI portion is broken down into two main classes; the main class and a controller class. The main class starts the program, initializes the controller and then the program begins.

To get the program to run the .fxml file that was setup, a couple of steps must be taken first. From figure 2 several obvious things can be pointed out. First, the main function gets overridden by the JavaFX function called start. Then from the start function, the .fxml file can be loaded and imprinted onto the stage so that the correct scene can be shown. The controller is where all the .fxml objects are initialized and where all the methods are. The majority of the code in this project lies in the controller portion of the project.

B. Design – Controller Class

Next, the controller holds all the

components and events that were created Figure 2 Starting the program in the scene builder application. So any button, label, text field, or any other GUI peice are all

initialized here. As per the JavaFX standard, the controller class must have an overridden

function called initialize, which gets launched once main is finished. It is in this initialize

function where the current working directory is set, where

the circuit grid is created, and where the component

listening on the right side of the GUI is filled with current

components. Also, all global data structures are initialized.

The methods or events that are created in the scene builder

must have special methods as figure 3 shows. These

functions act as listening events and run for a certain case.

Figure 3 FXML Function Methods For example, if the delete button was pressed on the GUI, then the program would interrupt and jump into the btnDelete_Pressed function shown in figure

3 and given a button click event as a parameter. The @FXML acts just like the @Override shown in figure 2 and is a standard for JavaFX to show that this function connects to the .fxml file. There are similar functions to handle all button clicks, any dragging and dropping, or other misc. user interface options that would normally be available.

Next the list of components that would be available to the user would have to be created.

To create that list a Java type called TreeItem was used, which acts a lot like a kind of tree data structure as an example is shown in figure 4. A root must be created which then creates subfolders like “Basic Figure 4 TreeItem example

Components” or “Sources” where in these folders are other TreeItems that contain circuit components that can be selected and dragged by the mouse and dropped onto the circuit pane.

To allow the dragging and dropping functionality five functions were created. Four of them are for detecting a mouse event and differentiating between a click event and a drag mouse event.

The last function is for a mouse drag release over the circuit pane, which gets the x and y coordinates of the mouse, makes sure that the point gets translated to a certain point, in this case its every 10th pixel in both the x and y directions, and then adds the selected component to the circuit. The circuit is a whole class system and will be discussed in detail later.

Moving on, there are a total of four button functions in the controller class. The first is the event listener for the analysis button. Pressing the analysis button brings up a sub controller class which loads another .fxml file containing the GUI for the analysis window. This window allows the user to select what type of analysis they want the circuit to have along with certain parameters, like start and stop time. Once the run button is pressed then it gets a series of commands from the file utility, creates a separate process, and then calls a system command called exec and runs the list of commands that it is given. After the system call ngspice is opened with a loaded user created circuit, this will be discussed in much more detail later. The next function concerns the delete button, which upon being pressed it listens until the primary mouse button is pressed, which at default is the right mouse button. Once the mouse is clicked it gets the target, differentiates it between a wire and an actual circuit component then attempts to remove the part from the pane and remove it from any data structure that it may also be in. Even after a component is clicked it will continue to delete parts that the mouse clicks on until the secondary mouse button is clicked. The last button event deals with the wire button. When the wire button is pressed the dragging, and dropping methods mentioned previously are recreated for the circuit pane so that wire can be custom made to how the user wants them. For example, once the button is clicked the user can then click on a spot in the pane, drag the mouse around

and a wire will appear and become fixed when the user releases the mouse button. One of the

last pieces the controller listens for is a mouse scroll event. Once the mouse wheel scrolls the

pane will either enlarge or shrink the circuit pane zoom. This is currently everything that goes

into the GUI for this project.

C. Design – Circuit Class

The Circuit class is another major class in this project. It keeps track of all circuit pieces

on the pane except the wires. Figure 5 shows the relationships between the components and the circuit class, and these classes themselves are contained inside of some of these classes. The

BasicComponent class

handles everything for

resistors, capacitors, Figure 5 Class Structure and inductors. The diodes class handles all varieties of diodes, including regular diodes, LED’s, and zener diodes. There are two different types of circuit source classes; regular sources and dependent sources, and each one of the source classes contains a voltage source and a current source. Transistors are also split into different classes; MOSFET and BJT, which contain the capabilities to create NMOS,

CMOS, NPN, and PNP type transistors. The last needed component class is the ground component, and its purpose is to set a node that is connected to it by a wire to 0. This is because the SPICE language must have a 0-value node to know where ground is located and it will throw errors or cause a lot of unforeseen problems if this is not taken care of. All these classes inherit from the Object type and can all be stored in one custom data structure called an Observable List.

However, before the circuit class can be understood a basic understanding of a component class is needed. For this only the BasicComponent class will be used as all the component classes are quite similar. Starting off there are only two different constructors for creating a component of this type. The most basic one takes its type (Resistor, Capacitor, or Inductor), and and x and y values corresponding to its location on the circuit pane. Depending on the type an appropriate name is given, the starting x and y values are set to the ones passed in and its value is initialized to 0. The other constructor takes a type, a name, the value, the starting x and y values, the ending x and y values, and the nodal values. All the variables get set to their respective class values. Get and Set methods were created for all variables so that all the variables in class could be private. The next major piece of the component class is the Draw method, which draws the component to the circuit pane. Figure 6 shows how easy it is to Figure 6 Drawing Example draw using the Java language. The only thing that needs to be done to the resistor path is to then add it to a Java class called a Group. The next step is adding a transparent rectangle over most of the component to make custom user events easier, otherwise the user would have to try to click on a line the size of one pixel to try to change its value. On that topic, listener events were

created so that the component could

be dragged around in the pane,

rotated, and a popup dialog that

could set certain values. On a side

Figure 7 Dialog Example note, the invisible rectangle is added to the same Group class as the resistor path and then added to the circuit pane. For a resistor, the only values that are important are its name and the desired resistivity in ohms. Figure 7 shows what the dialog of the resistor looks like once the method has been run. The tricky part is getting the results back from the dialog and setting any values that had been changed by the user.

An example of how it was

Figure 8 Dialog Result Conversion implemented in this project is shown in figure 8. For later use, each component class has their own format function, which returns a string of all the values in a specific format that will be discussed in the file input/output section. The important part to know now is that it passes all of the important values from the class to the Circuit class.

Overall there are several major operations that the Circuit class handles. The first major operation is the function called formatCircuitList, this method is responsible for looping through each component currently in the circuits list and producing a list of strings that are later used in the file input and output. However, it takes a special type of conditional statement to check

whether a component is of a certain

class as shown in figure 9. This were

the previously mentioned format

Figure 9 Class Differentiation function works its way into the Circuit class, and it returns the string containing all the values in the class at the current moment.

These aren’t all the values for all components since each component can be extremely different, this is one of the reasons why components are separated into their respective classes. Going on a similar trend it is also necessary to parse this same list of strings for the inverse reason, like reading back in the data from a file. This happens in a function called parseCircuitList and uses several helper functions so that it is much easier to understand. One method tokenizes an input string by separating any “;” found in them. After that it loops through each one of those tokens,

creates another set of tokens separated by a

space or “ “. This function then searches for

keywords and returns the entire sub token

which would contain something like “name=

Resistor1”. Figure 10 shows all the

Figure 10 Parsing through the file input interactions for the parsing function and how they interact with each other. The second parameter in the function getInfo called type, doesn’t mean the type of the component, but rather means the type of data that needs to be read. Another major operation is finding and deleting components out of the circuit list and off the pane. The method is called by a function in the main controller class and sent with an x and y location and the circuit pane. Each component is then searched and if a matching x and y value are found then a component level function named remove is called. This function simply removes the drawn component group from the circuit pane. The last major Figure 11 Checking for an intersection operation of the circuit class is to test whether a wire intersects with any components currently in the circuit list. Figure 11 shows how the program is able to detect any wires that are intersecting.

However, for the wire to count the intersection as valid the wires have to either start or end on another wire. This allows the user to cross wires and not have to worry about a wires node value or needing a jumper wire. The whole purpose of this operation is to get the correct nodal values in the component so that the SPICE language correctly interprets the user created circuit. Figure

12 shows how a circuit can be represented into the SPICE language. The different portions of a line are extremely important. For example, take the line “v1 1 0”, this line is interpreted as v then the name is appended to the v, the positive node of the voltage source is connected to the node 1 and the negative end is connected to ground or node 0. It would appear that the value of the source is not set, however that gets added with the line “.dc v1 24 24 1.” This line sets the dc voltage value of the first voltage source to 24 volts. Translating the GUI circuit is extremely important for all aspects of theoretical circuit analysis if the correct results are desired.

The circuit class and its operations are critical to the entirety of the project. All the component classes are derived from here and thus all the listening events from the components are because the Circuit class is vastly important.

Its pivotal for the controller class and especially later for the file input/output class, which in this case is name FileUtility.

D. Design – FileUtility Figure 12 Circuit with SPICE netlist

The FileUtility class has to keep track of several important key files for set purposes.

The first one is the current working directory, CWD, which controls where a program can save a file. Setting the CWD is relatively easy and can be done as shown in figure 13, where all the

FileUtility needs

is a name. The

class then checks

to see if a

directory with

Figure 13 Setting the Current Working Directory the same name currently exists, if it does no action is taken, otherwise the system creates a directory of that name that stores all later circuit input and output. To create a circuit that can be run the user has to create a new folder in the CWD so that all files associated with the user created circuit all reside in the same place. To create a new file directory all that needs to be done is to run the program, click on the File menu and then select the new menu item option. This opens a window that can be used to open/save files much like windows file explorer. This window is part of the standard JavaFX 8 package and is called FileChooser. FileChooser provides support for any platform file dialogs, like Windows, Linux, or Mac operating systems. Figure 14 shows the

Windows operating system version

of the FileChooser dialog. Along

with that, figure 15 shows how to

implement this dialog that is capable

of opening both a text file and a

circuit file, or .cir file. Using that

format it is then easy to save or open

Figure 14 FileChooser Dialog any file the user wants. However, before we can save or open a file of a specific type a file format needs to be Figure 15 FileChooser Open Dialog created so that it can be easily parsed through to get the values. The format was mentioned briefly but figure 16 shows the basic form, transistors has additional values for the x, y, and node values for the gate, source, drain, base, emitter, or collector connections. So then while saving, a text file is created to match this format. The components can then be easily recreated if the user reopens the file.

Along with that a .cir file is created, and

this file is responsible for being run by a

SPICE program. From that .cir the user

can then get the theoretical results back

Figure 16 Text File Format from the circuit, as long as it has been translated and read correctly. However, a separated class was created to handle creating the

SPICE netlist, named NetlistSetup. The netlist constructor takes only the circuit list of objects.

Then the program then loops through each component and creates a string based on the standard component netlist. Each component creates a string formatted in a way that SPICE can use and is then added to a list of strings. This list is returned to the main controller to be used to create and save a file in the .cir format. To save this file a standard Java class called FileWriter is used.

With the Java programming language, it is incredibly simple to write to a file using this standard class. On the opposite side of that, another class called FileReader is then used to read in a file.

With another standard class, bufferedReader allows the program to read in entire strings and add them to a list of strings that will be parsed by the Circuit and then used to recreate the circuit that

was initially saved.

E. Design – Constraints

As an engineer, many factors should be considered when trying to design a project and

how they can affect various parts of our society. For this project, economically it will have no

effect since it doesn’t pertain to any business or organization. Environmentally the project must

be run on a computer, which could possibly end up harming the environment unintentionally, but

through no fault of this project. Various health and safety factors should be taken into account

when dealing with electronics, especially with computers. For example, stay away from open

wires, don’t lick batteries, and other hazardous ways electricity interact with humans. The last

factor to be considered is political, which this project will obviously have no effect. This

software project will follow the standards of the Java programming language and the standard

SPICE format.

F. Design – Costs

There were no direct costs in the production of this project. The IntelliJ Idea IDE,

JavaFX, and ngspice are all free pieces of software that can be found online. Although a

computer would be needed before any of this could be started. Even though there were no direct

costs there was an indirect cost in time and how long it took to figure out the syntax and

semantics of Java programming.

G. Design – Standards

There were several standards that had to be taken into consideration in the

completion of this project. First the standard programming implementations of the standard Java language have to be used. Next the 1320.2-1998 IEEE, which is an IEEE standard for the syntax and semantics for a conceptual modeling language where taken into consideration. Currently at this moment neither the IntelliJ Idea, JavaFX scene builder, or ngspice have any IEEE rated standards.

IV. Results

Figure 17 LTSpice Example Circuit Most of this project went fairly smoothly, there were some very difficult points in it but for the most part the system works. The GUI portion of the project is capable of properly analyzing the circuit and transforming into a language that can be understood by SPICE, which allows us to get results from the circuit in figure 17, created in the

program LTSpice. Using

LTSpice the figure outputs the

results shown in figure 18. So,

the results from the LTSpice

Figure 19 LTSpice Example Circuit Output version show a very clear and precise picture of what the circuit should output when compared to what this program outputs.

And as expected, figure 19 shows the results from what the program outputs after the run command is given. When put under close scrutiny the two programs output the same logarithmic curve, which shows that the project can completely create and implement a circuit using the SPICE language.

Figure 18 Program Circuit Output V. Conclusion

Overall this program performs what it is expected to do, but there are some features and safety nets that should be added before it can be categorized as a good circuit simulator. The program can correctly translate the circuit into the SPICE format which was the main focus of this program, other than the GUI. The program can output the correct theoretical results when compared to the results of LTSpice.

References

[1] Linear Technology. (2016 November). Circuit Simulating software. LTSpice. [Online]. Available site: linear.com/designtools/software [2] Oracle, "Java software," 2014. [Online]. Available: https://www.oracle.com/java/index.html. Accessed: Dec. 4, 2016. [3] Jet Brains. (2016 November). Java Compiler software. Intellij IDEA. [Online]. Available site: jetbrains.com/idea/ [4] Hewlett Packard, “SPICE Language,” [Online]. Available: http://www.hpl.hp.com/techreports/2002/HPL-2002-229.pdf. Accessed: Dec. 4, 2016.

Appendices:

////////////////////////////////////////////////////////////////////////////////////////////// /** * Main.java file: Starts up the GUI */ ////////////////////////////////////////////////////////////////////////////////////////////// package CircuitSimulator; import .application.Application; import javafx.fxml.FXMLLoader; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.stage.Stage; import java.io.IOException; public class Main extends Application{ private Stage primaryStage;

public static void main(String[] args){ launch(args); }

@Override public void start(Stage primaryStage) throws Exception{ this.primaryStage = primaryStage; startSimulatorLayout(); }

private void startSimulatorLayout(){ try{ FXMLLoader loader = new FXMLLoader(getClass().getResource("circuitSimulator.fxml")); Parent root = loader.load(); primaryStage.setTitle("Circuit Simulator"); Scene scene = new Scene(root, 1000, 600); MainController mainController = loader.getController(); mainController.setStage(primaryStage); primaryStage.setScene(scene); primaryStage.show(); } catch (IOException e){ e.printStackTrace(); } } }

////////////////////////////////////////////////////////////////////////////////////////////// /** * MainController.java: Controls all UI actions */ ////////////////////////////////////////////////////////////////////////////////////////////// package CircuitSimulator; import Circuit.Circuit; import CircuitSimulator.AnalysisDialog.AnalysisController; import Components.Component; import Components.Misc.Wire; import Components.Transistor; import Netlist.NetlistSetup; import javafx.event.ActionEvent; import javafx.event.EventType; import javafx.fxml.FXMLLoader; import javafx.fxml.Initializable; import java.io.*; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.ResourceBundle; import javafx.fxml.FXML; import javafx.scene.Cursor; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.control.*; import javafx.scene.input.MouseButton; import javafx.scene.input.MouseEvent; import javafx.scene.input.ScrollEvent; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import javafx.scene.paint.Color; import javafx.scene.shape.Line; import javafx.stage.Modality; import javafx.stage.Stage; public class MainController implements Initializable{ // FXML Elements // Circuit Tabs @FXML private Tab circuitTab; @FXML private Tab graphTab; @FXML private AnchorPane circuitPane; @FXML private AnchorPane graphPane; // Buttons @FXML private ToolBar simulatorToolBar; @FXML private Button btnWire; @FXML private Button btnDelete; @FXML private Button btnAnalysis; @FXML private Button btnRun; // Panes @FXML private TabPane simulatorPane; @FXML private BorderPane simulatorBorderPane; // Menu Items @FXML private MenuBar simulatorMenuBar; @FXML private MenuItem menuItem_New; @FXML private MenuItem menuItem_Save; @FXML private MenuItem menuItem_Open; // Misc @FXML private TreeView componentWorkshop; @FXML private HBox simulatorHBox; @FXML private VBox simulatorVBox; @FXML private Label labelToolBar;

// Other Elements private Circuit circuit; private List wireList; private List totalList; private Wire wire; private int nodeIndex; private Stage stage; private FileUtility fileUtility; private NetlistSetup netlistSetup;

////////////////////////////////////////////////////////////////////////////////////////////// /** * Initialization Factors */ ////////////////////////////////////////////////////////////////////////////////////////////// @Override public void initialize(URL location, ResourceBundle resources){ // Initialize variables nodeIndex = 1; circuit = new Circuit(); wireList = new ArrayList<>(); // Set Styles circuitPane.setStyle("-fx-background-color: white"); setComponentWorkshop(); // Set TreeView/componentWorkshop setCircuitGrid(); // Set circuit grid on pane setCWD(); // Set the current working directory of the program // TODO: dont forget to lock the buttons eventually //blockButtons(); }

/* Function: setComponentWorkshop * Returns: void * Description: * Setup for the component tool box with all of the circuit components * so that they can be placed on the canvas. */ private void setComponentWorkshop() { // Root Directory TreeItem root = new TreeItem<>("All Components"); // Basic Components TreeItem basic_Comp = new TreeItem<>("Basic Components"); TreeItem comp_Resistor = new TreeItem<>("Resistor"); TreeItem comp_Capacitor = new TreeItem<>("Capacitor"); TreeItem comp_Inductor = new TreeItem<>("Inductor"); // Sources TreeItem sources_Comp = new TreeItem<>("Sources"); TreeItem comp_Ind_Voltage = new TreeItem<>("Voltage Source"); TreeItem comp_Ind_Current = new TreeItem<>("Current Source"); TreeItem comp_Dep_Voltage = new TreeItem<>("Dependent Voltage Source"); TreeItem comp_Dep_Current = new TreeItem<>("Dependent Current Source"); // Diodes TreeItem diodes_Comp = new TreeItem<>("Diodes"); TreeItem comp_diode = new TreeItem<>("Diode"); TreeItem comp_LED = new TreeItem<>("LED"); TreeItem comp_Zener = new TreeItem<>("Zener"); // Transistors TreeItem transistors_Comp = new TreeItem<>("Transistors"); TreeItem comp_NMOS = new TreeItem<>("NMOS"); TreeItem comp_PMOS = new TreeItem<>("PMOS"); TreeItem comp_NPN = new TreeItem<>("NPN"); TreeItem comp_PNP = new TreeItem<>("PNP"); // Misc TreeItem misc_Comp = new TreeItem<>("Misc."); TreeItem comp_Ground = new TreeItem<>("Ground");

// Set Children basic_Comp.getChildren().addAll(comp_Resistor, comp_Capacitor, comp_Inductor); sources_Comp.getChildren().addAll(comp_Ind_Voltage, comp_Ind_Current, comp_Dep_Voltage, comp_Dep_Current); diodes_Comp.getChildren().addAll(comp_diode, comp_LED, comp_Zener); transistors_Comp.getChildren().addAll(comp_NMOS, comp_PMOS, comp_NPN, comp_PNP); misc_Comp.getChildren().addAll(comp_Ground); // Set Children to root root.getChildren().addAll(basic_Comp, sources_Comp, diodes_Comp, transistors_Comp, misc_Comp); // Set Root root.setExpanded(true); componentWorkshop.setRoot(root); }

private void setCircuitGrid() { Group grid = new Group(); // X axis for(int i=0; i<1200; i+=10){ // Y axis for(int j=0; j<600; j+=10){ Line line = new Line(i, j, i, j); line.setStroke(Color.BLACK); grid.getChildren().add(line); } } circuitPane.getChildren().add(grid); }

private void setCWD(){ fileUtility = new FileUtility("TestCases"); }

public void setStage(Stage stage){ this.stage = stage; }

private void blockButtons(){ btnWire.setDisable(true); btnDelete.setDisable(true); btnAnalysis.setDisable(true); btnRun.setDisable(true); menuItem_Save.setDisable(true); }

private void unblockButtons(){ btnAnalysis.setDisable(false); btnWire.setDisable(false); btnDelete.setDisable(false); btnRun.setDisable(false); menuItem_Save.setDisable(false); }

////////////////////////////////////////////////////////////////////////////////////////////// /** * Mouse Listening Event Functions */ ////////////////////////////////////////////////////////////////////////////////////////////// @FXML void cw_DragDetected(MouseEvent event) { componentWorkshop.startFullDrag(); componentWorkshop.setCursor(Cursor.CROSSHAIR); circuitPane.setCursor(Cursor.CROSSHAIR); }

@FXML void cW_MouseDragged(MouseEvent event) { event.setDragDetect(false); }

@FXML void cW_MousePressed(MouseEvent event) { componentWorkshop.setMouseTransparent(true); event.setDragDetect(true); componentWorkshop.setCursor(Cursor.CLOSED_HAND); }

@FXML void cW_MouseReleased(MouseEvent event) { componentWorkshop.setMouseTransparent(false); componentWorkshop.setCursor(Cursor.DEFAULT); circuitPane.setCursor(Cursor.DEFAULT); }

@FXML void cP_MouseDragReleased(MouseEvent event) { String selectedItem = componentWorkshop.getSelectionModel().getSelectedItem().getValue(); if((!selectedItem.equals("Basic Components")) && (!selectedItem.equals("Sources")) && (!selectedItem.equals("Diodes")) && (!selectedItem.equals("Transistors")) && (!selectedItem.equals("Misc."))){ double mouseX = snapToGrid(event.getX()); double mouseY = snapToGrid(event.getY()); addType(selectedItem, mouseX, mouseY); } }

////////////////////////////////////////////////////////////////////////////////////////////// /** * Button Press Events */ ////////////////////////////////////////////////////////////////////////////////////////////// @FXML void btnAnalysis_Pressed(ActionEvent event) { try{ FXMLLoader loader = new FXMLLoader(); loader.setLocation(Main.class.getResource("AnalysisDialog/analysisDialog.fxml")); TabPane dialog = loader.load();

// Create stage Stage dialogStage = new Stage(); dialogStage.setTitle("Analysis Window"); dialogStage.initModality(Modality.WINDOW_MODAL); dialogStage.initOwner(stage); Scene scene = new Scene(dialog); dialogStage.setScene(scene);

// Set Controller AnalysisController controller = loader.getController(); controller.setDialogStage(dialogStage); controller.setCirFile(fileUtility.getFile_netlist());

// Show and wait dialogStage.show(); } catch (IOException e){ e.printStackTrace(); } }

@FXML void btnRun_Pressed(ActionEvent event) { netlistSetup.setEnd(); try{ String[] command = {fileUtility.getSpicefile(), fileUtility.getFile_netlist().getAbsolutePath()};

Runtime runtime = Runtime.getRuntime(); Process process = runtime.exec(command); process.waitFor();

} catch (Exception e){ e.printStackTrace(); } }

@FXML void btnDelete_Pressed(ActionEvent event) { circuitPane.setCursor(Cursor.HAND); circuitPane.setOnMouseClicked(e -> { if(e.getButton() == MouseButton.PRIMARY){ if(e.getTarget().getClass().getSimpleName().equals("Rectangle")){ circuit.findAndDeleteComponent(e.getTarget().toString(), circuitPane); } else if(e.getTarget().getClass().getSimpleName().equals("Wire")){ circuitPane.getChildren().remove(e.getTarget()); } } else { circuitPane.setCursor(Cursor.DEFAULT); circuitPane.setOnMouseClicked(null); } }); }

@FXML void btnWire_Pressed(ActionEvent event) { circuitPane.setCursor(Cursor.CROSSHAIR); // Mouse pressed on pane btnWire_Helper_MousePressedListener(); // Mouse dragged on pane btnWire_Helper_MouseDraggedListener(); // Mouse released on pane btnWire_Helper_MouseReleasedListener(); // Mouse clicked btnWire_Helper_MouseClickedListener(); } private void btnWire_Helper_MousePressedListener(){ circuitPane.setOnMousePressed(event1 -> { if(event1.getButton() == MouseButton.SECONDARY && wire == null){ btnWire_Helper_KillListeners(); return; } double x = snapToGrid(event1.getX()); double y = snapToGrid(event1.getY()); wire = new Wire(x, y, x, y); circuitPane.getChildren().add(wire); }); } private void btnWire_Helper_MouseDraggedListener(){ circuitPane.setOnMouseDragged(event1 -> { double endX = snapToGrid(event1.getX()); double endY = snapToGrid(event1.getY()); wire.setEndX(endX); wire.setEndY(endY); }); } private void btnWire_Helper_MouseReleasedListener(){ circuitPane.setOnMouseReleased(event1 -> { if(wire != null) { if(!(wire.getStartX() == wire.getEndX() && wire.getStartY() == wire.getEndY())){ wireList.add(wire); wire.setNode(nodeIndex); nodeIndex++; } else { circuitPane.getChildren().remove(wire); btnWire_Helper_KillListeners(); } } }); } private void btnWire_Helper_MouseClickedListener(){ circuitPane.setOnMouseClicked(event1 -> { if(event1.getButton() == MouseButton.SECONDARY) { // Cancel the drawing operation if right clicked btnWire_Helper_KillListeners(); //wireList.forEach(wire -> checkIntersection(wire)); return; }

// Forward go through each line to check the node wireList.forEach(line -> { checkIntersection(line); circuit.checkLineComponentIntersection(line); });

// Flip the lists Collections.reverse(wireList); circuit.reverseCircuitList(); // Go through the reversed list to check each line wireList.forEach(line -> { checkIntersection(line); circuit.checkLineComponentIntersection(line); }); // Put the list back in original order Collections.reverse(wireList); circuit.reverseCircuitList();

}); } private void btnWire_Helper_KillListeners(){ circuitPane.setCursor(Cursor.DEFAULT); circuitPane.setOnMousePressed(null); circuitPane.setOnMouseDragged(null); circuitPane.setOnMouseReleased(null); circuitPane.setOnMouseClicked(null); }

////////////////////////////////////////////////////////////////////////////////////////////// /** * Menu Items Click Events */ ////////////////////////////////////////////////////////////////////////////////////////////// @FXML void menuItem_New_Action(ActionEvent event) { fileUtility.newFile(stage); unblockButtons(); }

@FXML void menuItem_Save_Action(ActionEvent event) { netlistSetup = new NetlistSetup(circuit.getCircuitList()); totalList = circuit.formatCircuitList(); wireList.forEach(wire -> { String wireStr = wire.format(); totalList.add(wireStr); });

File file = fileUtility.saveFileChooser(stage); if(netlistSetup != null){ fileUtility.saveFile(netlistSetup.returnSpiceList(), file); //file.getName() -> name.cir, so take the last 3 off the string and replace with txt // to get a name.txt file String fileName = file.getName(); if(fileName.endsWith("cir")){ fileName = fileName.substring(0, fileName.length()-3); fileName += "txt"; File txtFile = new File(fileName).getAbsoluteFile(); fileUtility.saveFile(totalList, txtFile); } } }

@FXML void menuItem_Open_Action(ActionEvent event) { // Clear the field if there is anything on it. circuitPane.getChildren().remove(1, circuitPane.getChildren().size()); File file = fileUtility.openFileChooser(stage); totalList = fileUtility.openFile(file); // TODO: Input file will have a header and .commands so these cannot be parsed. totalList.forEach(item -> { if(!circuit.getInfo(item, "type=").equals("Wire")){ circuit.parseCircuitList(item, circuitPane); } else { int node = Integer.parseInt(circuit.getInfo(item, "node=")); Double xStart = Double.parseDouble(circuit.getInfo(item, "xStart=")); Double yStart = Double.parseDouble(circuit.getInfo(item, "yStart=")); Double xEnd = Double.parseDouble(circuit.getInfo(item, "xEnd=")); Double yEnd = Double.parseDouble(circuit.getInfo(item, "yEnd=")); Wire newWire = new Wire(node, xStart, yStart, xEnd, yEnd); wireList.add(newWire); circuitPane.getChildren().add(newWire); } });

wireList.forEach(wire1 -> { checkIntersection(wire1); circuit.checkLineComponentIntersection(wire1); });

unblockButtons(); }

////////////////////////////////////////////////////////////////////////////////////////////// /** * Zooming/Panning Functions */ ////////////////////////////////////////////////////////////////////////////////////////////// // TODO: figure out how to scale relative to where the mouse pointer is @FXML void cP_MouseScroll(ScrollEvent event){ double deltaScalar = 1.05; double scalar = circuitPane.getScaleY();

double delta = event.getDeltaY(); if(delta > 0) { scalar *= deltaScalar; circuitPane.setScaleY(scalar); circuitPane.setScaleX(scalar); } else { scalar /= deltaScalar; circuitPane.setScaleY(scalar); circuitPane.setScaleX(scalar); } }

////////////////////////////////////////////////////////////////////////////////////////////// /** * MISC Helper Functions */ ////////////////////////////////////////////////////////////////////////////////////////////// private void addType(String selectedItem, double x, double y){ if(selectedItem.equals("Resistor") || selectedItem.equals("Capacitor") || selectedItem.equals("Inductor")){ circuit.AddBasicComponent(selectedItem, x, y, circuitPane); } else if(selectedItem.equals("Diode") || selectedItem.equals("LED") || selectedItem.equals("Zener")){ circuit.AddDiodes(selectedItem, x, y, circuitPane); } else if(selectedItem.equals("NPN") || selectedItem.equals("PNP")){ circuit.AddBJTs(selectedItem, x, y, circuitPane); } else if(selectedItem.equals("NMOS") || selectedItem.equals("PMOS")){ circuit.AddMOSFETs(selectedItem, x, y, circuitPane); } else if(selectedItem.equals("Voltage Source") || selectedItem.equals("Current Source")){ circuit.AddSources(selectedItem, x, y, circuitPane); } else if(selectedItem.equals("Dependent Voltage Source") || selectedItem.equals("Dependent Current Source")){ circuit.AddDepSources(selectedItem, x, y, circuitPane); } else if(selectedItem.equals("Ground")){ circuit.AddGround(x, y, circuitPane); }

}

private double snapToGrid(double in){ double var; if(in%10 >= 5){ var = in+(10-(in%10)); } else { var = in - (in%10); } return var; }

private void checkIntersection(Wire wire){ Wire wireStart = new Wire(wire.getStartX(), wire.getStartY(), wire.getStartX(), wire.getStartY());

Wire wireEnd = new Wire(wire.getEndX(), wire.getEndY(), wire.getEndX(), wire.getEndY());

circuitPane.getChildren().addAll(wireStart, wireEnd); wireList.forEach(event -> { if(wire != event){ if(wireStart.getBoundsInParent().intersects(event.getBoundsInParent()) || wireEnd.getBoundsInLocal().intersects(event.getBoundsInParent())){ wire.setNode(event.getNode()); //wire.setStroke(Color.RED); //event.setStroke(Color.RED); } } }); circuitPane.getChildren().removeAll(wireStart, wireEnd); } }

////////////////////////////////////////////////////////////////////////////////////////////// /** * Listener.java: Controls the Listening events for the components */ ////////////////////////////////////////////////////////////////////////////////////////////// package CircuitSimulator; import Components.Component; import Components.Transistor; import javafx.geometry.Insets; import javafx.scene.Cursor; import javafx.scene.Node; import javafx.scene.control.*; import javafx.scene.input.MouseButton; import javafx.scene.input.MouseEvent; import javafx.scene.layout.GridPane; import javafx.scene.transform.Rotate; import javafx.util.Pair; import java.util.Optional; public class Listener { private final int ROTATION_FACTOR = 90; private int ROTATION_COUNT = 0;

public Listener(Component component){ // Set old x and y values component.setOldXLoc(component.getStartX()); component.setOldYLoc(component.getStartY()); // Start listening events // Listen for mouse over component.getGroup().setOnMouseEntered(event -> MouseEnter(event)); // Listen for mouse released component.getGroup().setOnMouseReleased(event -> MouseLeave(event)); // Listen for mouse dragged component.getGroup().setOnMouseDragged(event -> { if(event.getButton() == MouseButton.PRIMARY){ Pair location = MouseDrag(event, component.getOldXLoc(), component.getOldYLoc()); component.setStartX(location.getKey()); component.setStartY(location.getValue()); } }); // Listen for mouse clicked component.getGroup().setOnMouseClicked(event -> MouseClick_Component(event, component)); }

public Listener(Transistor transistor){ transistor.setOldXLoc(transistor.getGateX()); transistor.setOldYLoc(transistor.getGateY()); // Start listening events // Listen for mouse over transistor.getGroup().setOnMouseEntered(event -> MouseEnter(event)); // Listen for mouse released transistor.getGroup().setOnMouseReleased(event -> MouseLeave(event)); // Listen for mouse dragged transistor.getGroup().setOnMouseDragged(event -> { if(event.getButton() == MouseButton.PRIMARY){ Pair location = MouseDrag(event, transistor.getOldXLoc(), transistor.getOldYLoc()); transistor.setGateX(location.getKey()); transistor.setGateY(location.getValue()); } }); // Listen for mouse clicked transistor.getGroup().setOnMouseClicked(event -> MouseClick_Transistor(event, transistor)); }

////////////////////////////////////////////////////////////////////////////////////////////// /** * Listening Helper Methods * */ ////////////////////////////////////////////////////////////////////////////////////////////// private void MouseEnter(MouseEvent event){ Node n = (Node)event.getSource(); n.setCursor(Cursor.OPEN_HAND); } private void MouseLeave(MouseEvent event){ Node n = (Node)event.getSource(); n.setCursor(Cursor.DEFAULT); } private Pair MouseDrag(MouseEvent event, Double oldX, Double oldY){ Node n = (Node) event.getSource(); n.setCursor(Cursor.CLOSED_HAND); double transX = snapToGrid(n.getTranslateX()+event.getX()-oldX); double transY = snapToGrid(n.getTranslateY()+event.getY()-oldY); n.setTranslateX(transX); n.setTranslateY(transY);

Pair location = new Pair<>(snapToGrid( n.getTranslateX()+event.getX()), snapToGrid(n.getTranslateY()+event.getY())); return location; } private void MouseClick_Component(MouseEvent event, Component component){ // Double click if(event.getButton() == MouseButton.PRIMARY){ if(component.getType().equals("Ground")){ return; } String title, nameLabel, valueLabel; title = component.getType() + " Dialog"; nameLabel = component.getType() + " Name: "; valueLabel = component.getType() + " Value: "; if (event.getClickCount() >= 2) { if(component.getType().equals("Voltage Source") || component.getType().equals("Current Source")){ DialogBox_Sources(title, nameLabel, valueLabel, component); } else { DialogBox_Component(title, nameLabel, valueLabel, component); } } // Rotation } else if(event.getButton() == MouseButton.SECONDARY){ component.getGroup().getTransforms().add( new Rotate(ROTATION_FACTOR, component.getStartX(), component.getStartY())); //component.getComponent().setRotate(ROTATION_FACTOR*ROTATION_COUNT); //ROTATION_COUNT++; } }

private void MouseClick_Transistor(MouseEvent event, Transistor transistor){ // Double click if(event.getButton() == MouseButton.PRIMARY){ String title, nameLabel, valueLabel; title = transistor.getType() + " Dialog"; nameLabel = transistor.getType() + " Name: "; valueLabel = transistor.getType() + " Models: "; if (event.getClickCount() >= 2) { DialogBox_Transistor(title, nameLabel, valueLabel, transistor); } // Rotation } else if(event.getButton() == MouseButton.SECONDARY){ transistor.getGroup().getTransforms().add( new Rotate(ROTATION_FACTOR, transistor.getGateX(), transistor.getGateY())); //component.getComponent().setRotate(ROTATION_FACTOR*ROTATION_COUNT); //ROTATION_COUNT++; } }

private double snapToGrid(double in){ double var; if(in%10 >= 5){ var = in+(10-(in%10)); } else { var = in - (in%10); } return var; }

////////////////////////////////////////////////////////////////////////////////////////////// /** * Create Basic Components Dialog Box * */ ////////////////////////////////////////////////////////////////////////////////////////////// private void DialogBox_Component(String title, String nameLabel, String valueLabel, Component component){ Dialog> dialog = new Dialog<>(); dialog.setTitle(title);

// Set the button types ButtonType btnOkay = new ButtonType("OK", ButtonBar.ButtonData.OK_DONE); dialog.getDialogPane().getButtonTypes().addAll(btnOkay, ButtonType.CANCEL);

GridPane gridPane = new GridPane(); gridPane.setHgap(10); gridPane.setVgap(10); gridPane.setPadding(new Insets(20, 10, 10, 10));

Label lbl_name = new Label(nameLabel); Label lbl_value = new Label(valueLabel); TextField txt_name = new TextField(); txt_name.setPromptText(component.getName()); TextField txt_value = new TextField(); txt_value.setPromptText(component.getValue());

gridPane.add(lbl_name, 0, 0); gridPane.add(txt_name, 1, 0); gridPane.add(lbl_value, 0, 1); gridPane.add(txt_value, 1, 1); dialog.getDialogPane().setContent(gridPane);

// If okay button is pressed then get the results in from the text fields // If the cancel button is pressed then return null and the function exit dialog.setResultConverter(dialogButton -> { if (dialogButton == btnOkay) { return new Pair<>(txt_name.getText(), txt_value.getText()); } return null; });

Optional> result = dialog.showAndWait(); result.ifPresent(e -> { // Make sure there is a result in the text boxes if(!e.getKey().equals("")){ component.setName(e.getKey()); } if(!e.getValue().equals("")){ component.setValue(e.getValue()); } }); }

private void DialogBox_Sources(String title, String nameLabel, String valueLabel, Component component){ Dialog dialog = new Dialog<>(); dialog.setTitle(title); // Set the button types ButtonType btnOkay = new ButtonType("OK", ButtonBar.ButtonData.OK_DONE); dialog.getDialogPane().getButtonTypes().addAll(btnOkay, ButtonType.CANCEL);

// Create a tabpane TabPane pane = new TabPane(); // Set the AC source tab Tab acTab = new Tab("AC Tab"); GridPane acGrid = setContent_AC(component); acTab.setContent(acGrid); acTab.closableProperty().setValue(false); // Set the DC source tab Tab dcTab = new Tab("DC Tab"); GridPane dcGrid = setContent_DC(valueLabel); dcTab.setContent(dcGrid); dcTab.closableProperty().setValue(false); // Set the pulse tab Tab pulseTab = new Tab("Pulse Tab"); GridPane pulseGrid = setContent_PULSE(nameLabel, component); pulseTab.setContent(pulseGrid); pulseTab.closableProperty().setValue(false); // Set Tabs to the pane pane.getTabs().addAll(acTab, dcTab, pulseTab);

dialog.getDialogPane().setContent(pane); // If okay button is pressed then get the results in from the text fields // If the cancel button is pressed then return null and the function exit dialog.setResultConverter(dialogButton -> { // Get type of tab selected String currentTab = pane.getSelectionModel().getSelectedItem().getText(); if (dialogButton == btnOkay && currentTab.equals("AC Tab")) { // return ac tab results GridPane grid = (GridPane)pane.getSelectionModel().getSelectedItem().getContent(); String textResult = ""; int count = 0; for(Node node: grid.getChildren()){ if(node instanceof TextField){ if(((TextField) node).getText().startsWith("sin")){ continue; } if(!((TextField) node).getText().equals("") && count == 0) { textResult += "sin ( "+((TextField) node).getText() + " "; count++; } else if(!((TextField) node).getText().equals("")){ textResult += ((TextField) node).getText()+" "; count++; } } } textResult += ")"; return textResult; } else if(dialogButton == btnOkay && currentTab.equals("DC Tab")){ GridPane grid = (GridPane)pane.getSelectionModel().getSelectedItem().getContent(); String textResult = ""; for(Node node: grid.getChildren()){ if(node instanceof TextField){ textResult += "dc " + ((TextField) node).getText(); } } return textResult; } else if(dialogButton == btnOkay && currentTab.equals("Pulse Tab")){ GridPane grid = (GridPane)pane.getSelectionModel().getSelectedItem().getContent(); String textResult = ""; int count = 0; for(Node node: grid.getChildren()){ if(node instanceof TextField){ if(((TextField) node).getText().startsWith("pulse")){ continue; } if(!((TextField) node).getText().equals("") && count == 0) { textResult += "pulse ( "+((TextField) node).getText() + " "; count++; } else if(!((TextField) node).getText().equals("")){ textResult += ((TextField) node).getText()+" "; count++; } } } textResult += ")"; return textResult; } return null; });

Optional result = dialog.showAndWait(); result.ifPresent(textResult -> component.setValue(textResult)); } private GridPane setContent_AC(Component component){ GridPane gridPane = new GridPane(); gridPane.setHgap(10); gridPane.setVgap(10); gridPane.setPadding(new Insets(20, 10, 10, 10));

// need sin([offset] [voltage] [frequency] [delay] [damping factor]) // Offset Label lbl_offset = new Label("Offset: "); TextField txt_offset = new TextField(); gridPane.add(lbl_offset, 0, 0); gridPane.add(txt_offset, 1, 0); // Voltage Label lbl_voltage = new Label("Voltage: "); TextField txt_voltage = new TextField(); gridPane.add(lbl_voltage, 0, 1); gridPane.add(txt_voltage, 1, 1); // Frequency Label lbl_freq = new Label("Frequency: "); TextField txt_freq = new TextField(); gridPane.add(lbl_freq, 0, 2); gridPane.add(txt_freq, 1, 2); // Delay Label lbl_delay = new Label("Delay: "); TextField txt_delay = new TextField(); gridPane.add(lbl_delay, 0, 3); gridPane.add(txt_delay, 1, 3); // Damping Factor Label lbl_damping = new Label("Damping Factor: "); TextField txt_damping = new TextField(); gridPane.add(lbl_damping, 0, 4); gridPane.add(txt_damping, 1, 4); // Current Value Label lbl_current = new Label("Current Value:"); TextField txt_current = new TextField(component.getValue()); txt_current.setDisable(true); gridPane.add(lbl_current, 0, 5); gridPane.add(txt_current, 1, 5);

return gridPane; } private GridPane setContent_DC(String valueLabel){ GridPane gridPane = new GridPane(); gridPane.setHgap(10); gridPane.setVgap(10); gridPane.setPadding(new Insets(20, 10, 10, 10));

Label lbl_value = new Label(valueLabel); TextField txt_value = new TextField();

gridPane.add(lbl_value, 0, 0); gridPane.add(txt_value, 1, 0); return gridPane; } private GridPane setContent_PULSE(String nameLabel, Component component){ GridPane gridPane = new GridPane(); gridPane.setHgap(10); gridPane.setVgap(10); gridPane.setPadding(new Insets(20, 10, 10, 10));

//txt_name.setPromptText(component.getName()); Label lbl_init_value = new Label("Initial Value: "); TextField txt_init_value = new TextField(); gridPane.add(lbl_init_value, 0, 0); gridPane.add(txt_init_value, 1, 0);

Label lbl_pulse_value = new Label("Pulse Value: "); TextField txt_pulse_value = new TextField(); gridPane.add(lbl_pulse_value, 0, 1); gridPane.add(txt_pulse_value, 1, 1);

Label lbl_time_delay = new Label("Time Delay: "); TextField txt_time_delay = new TextField(); gridPane.add(lbl_time_delay, 0, 2); gridPane.add(txt_time_delay, 1, 2);

Label lbl_rise_time = new Label("Rise Time: "); TextField txt_rise_time = new TextField(); gridPane.add(lbl_rise_time, 0, 3); gridPane.add(txt_rise_time, 1, 3);

Label lbl_fall_time = new Label("Fall Time: "); TextField txt_fall_time = new TextField(); gridPane.add(lbl_fall_time, 0, 4); gridPane.add(txt_fall_time, 1, 4);

Label lbl_pulse_width = new Label("Pulse Width: "); TextField txt_pulse_width = new TextField(); gridPane.add(lbl_pulse_width, 0, 5); gridPane.add(txt_pulse_width, 1, 5);

Label lbl_period = new Label("Period: "); TextField txt_period = new TextField(); gridPane.add(lbl_period, 0, 6); gridPane.add(txt_period, 1, 6);

return gridPane; }

private void DialogBox_Transistor(String title, String nameLabel, String modelLabel, Transistor transistor){ Dialog> dialog = new Dialog<>(); dialog.setTitle(title);

// Set the button types ButtonType btnOkay = new ButtonType("OK", ButtonBar.ButtonData.OK_DONE); dialog.getDialogPane().getButtonTypes().addAll(btnOkay, ButtonType.CANCEL);

GridPane gridPane = new GridPane(); gridPane.setHgap(10); gridPane.setVgap(10); gridPane.setPadding(new Insets(20, 10, 10, 10));

Label lbl_name = new Label(nameLabel); Label lbl_value = new Label(modelLabel); TextField txt_name = new TextField(); txt_name.setPromptText(transistor.getName()); ChoiceBox cmb_model = new ChoiceBox<>(); CircuitModels circuitModels = new CircuitModels(); if(transistor.getType().equals("NPN")){ circuitModels.getNpnModels().forEach(item -> cmb_model.getItems().add(item.getKey())); } else if(transistor.getType().equals("PNP")){ circuitModels.getPnpModels().forEach(item -> cmb_model.getItems().add(item.getKey())); } else if(transistor.getType().equals("NMOS")){ circuitModels.getNmosModels().forEach(item -> cmb_model.getItems().add(item.getKey())); } else if(transistor.getType().equals("PMOS")){ circuitModels.getPmosModels().forEach(item -> cmb_model.getItems().add(item.getKey())); } cmb_model.getSelectionModel().selectFirst();

gridPane.add(lbl_name, 0, 0); gridPane.add(txt_name, 1, 0); gridPane.add(lbl_value, 0, 1); gridPane.add(cmb_model, 1, 1); dialog.getDialogPane().setContent(gridPane);

// If okay button is pressed then get the results in from the text fields // If the cancel button is pressed then return null and the function exit dialog.setResultConverter(dialogButton -> { if (dialogButton == btnOkay) { return new Pair<>(txt_name.getText(), cmb_model.getSelectionModel().getSelectedItem()); } return null; });

Optional> result = dialog.showAndWait(); result.ifPresent(e -> { // Make sure there is a result in the text boxes if(!e.getKey().equals("")){ transistor.setName(e.getKey()); } if(!e.getValue().equals("")){ transistor.setModelName(e.getValue()); } }); } }

////////////////////////////////////////////////////////////////////////////////////////////// /** * FileUtility.java: Handles all file requests, Save/Open */ ////////////////////////////////////////////////////////////////////////////////////////////// package CircuitSimulator; import javafx.stage.FileChooser; import javafx.stage.Stage; import java.io.*; import java.util.ArrayList; import java.util.List; public class FileUtility { private String cwdString; private File cwd, file_netlist; private FileChooser fileChooser; public FileUtility(String directory){ setCWD(directory); }

////////////////////////////////////////////////////////////////////////////////////////////// /** * Set Current Working Directory * */ ////////////////////////////////////////////////////////////////////////////////////////////// public void setCWD(String directoryName){ boolean result = false; File directory; directory = new File(directoryName).getAbsoluteFile(); if(directory.exists() || directory.mkdirs()){ result = (System.setProperty("user.dir", directory.getAbsolutePath()) != null); if(result){ cwdString = directory.toString(); cwd = directory; } } } public String getSpicefile(){ return "\\ngSpice\\bin\\ngspice.exe"; } public File getFile_netlist(){ return this.file_netlist; }

////////////////////////////////////////////////////////////////////////////////////////////// /** * Open a file * */ ////////////////////////////////////////////////////////////////////////////////////////////// public File openFileChooser(Stage stage){ fileChooser = new FileChooser(); fileChooser.setTitle("Open Resource File"); fileChooser.getExtensionFilters().addAll( new FileChooser.ExtensionFilter("Text Files", "*.txt"), new FileChooser.ExtensionFilter("SPICE Files", "*.cir")); fileChooser.setInitialDirectory(cwd); File selectedFile = fileChooser.showOpenDialog(stage); return selectedFile; }

public List openFile(File file){ List items = new ArrayList<>();

BufferedReader bufferedReader = null; try { bufferedReader = new BufferedReader(new FileReader(file)); String text; while((text = bufferedReader.readLine()) != null){ items.add(text); }

} catch (FileNotFoundException e){ e.printStackTrace(); } catch (IOException e){ e.printStackTrace(); }

return items; }

////////////////////////////////////////////////////////////////////////////////////////////// /** * Save a file * */ ////////////////////////////////////////////////////////////////////////////////////////////// public File saveFileChooser(Stage stage){ fileChooser = new FileChooser(); fileChooser.setTitle("Save Resource File"); fileChooser.getExtensionFilters().addAll( new FileChooser.ExtensionFilter("SPICE Files", "*.cir")); fileChooser.setInitialDirectory(cwd); this.file_netlist = fileChooser.showSaveDialog(stage); return this.file_netlist; } public void saveFile(List list, File file){ try{ FileWriter fileWriter = null; fileWriter = new FileWriter(file); for(String s: list){ fileWriter.write(s); fileWriter.write('\n'); } fileWriter.close(); } catch(IOException e){ e.printStackTrace(); } } ////////////////////////////////////////////////////////////////////////////////////////////// /** * New file dialog, creates a directory that save and open now default to. * */ ////////////////////////////////////////////////////////////////////////////////////////////// public void newFile(Stage stage){ fileChooser = new FileChooser(); fileChooser.setTitle("Save Resource File"); fileChooser.setInitialDirectory(cwd); File selectedFile = fileChooser.showSaveDialog(stage); cwdString = cwdString+"/"+selectedFile.toString(); setCWD(selectedFile.toString()); } }

////////////////////////////////////////////////////////////////////////////////////////////// /** * CircuitModels.java: creates all the circuit component models */ ////////////////////////////////////////////////////////////////////////////////////////////// package CircuitSimulator; import javafx.util.Pair; import java.util.ArrayList; import java.util.List; public class CircuitModels { private List> diodeModels; private List> npnModels; private List> pnpModels; private List> nmosModels; private List> pmosModels;

public CircuitModels(){ diodeModels = new ArrayList<>(); npnModels = new ArrayList<>(); pnpModels = new ArrayList<>(); nmosModels = new ArrayList<>(); pmosModels = new ArrayList<>(); setPmosModels(); setNmosModels(); setNpnModels(); setPnpModels(); setDiodes(); }

private void setDiodes(){ diodeModels.add(new Pair<>("Diode", ".model Diode D(Is=2.52n Rs=.568 N=1.752 Cjo=4p "+ "M=.4 tt=20n)")); diodeModels.add(new Pair<>("Zener", ".model Zener D(Is=149.54E-18 N=1.0155 Rs=.2126 Ikf=1.2516 "+ "Cjo=22.000E-12 M=.3719 Vj=.75178 Bv=6.1950 Ibv=5.0000E-3 "+ "tt=65n Nbv=0.2)")); diodeModels.add(new Pair<>("LED", ".model LED D(Is=1e-22 Rs=6 N=1.5 Cjo=50p Xti=100)")); }

public List> getDiodeModels(){ return this.diodeModels;}

private void setNpnModels(){ npnModels.add(new Pair<>("2N2222", ".model 2N2222 NPN(IS=1E-14 VAF=100" + " BF=200 IKF=0.3 XTB=1.5 BR=3 CJC=8E-12 CJE=25E-12 TR=100E-9 TF=400E- 12" + " ITF=1 VTF=2 XTF=3 RB=10 RC=.3 RE=.2)")); npnModels.add(new Pair<>("2N4124", ".model 2N4124 NPN(Is=6.734f Xti=3 Eg=1.11 Vaf=74.03 Bf=495 Ne=1.28"+ " Ise=6.734f Ikf=69.35m Xtb=1.5 Br=.7214 Nc=2 Isc=0 Ikr=0 Rc=1 Cjc=3.638p" + " Mjc=.3085 Vjc=.75 Fc=.5 Cje=4.493p Mje=.2593 Vje=.75 Tr=238.3n Tf=301.3p"+ " Itf=.4 Vtf=4 Xtf=2 Rb=10)")); npnModels.add(new Pair<>("2N3055", ".model 2N3055 NPN(Bf=73 Br=2.66 Rb=.81 Rc=.0856 Re=.000856" + " CJC=1000P PC=.75 MC=.33 Tr=.5703U Is=2.37E-8" + " CJE=415P PE=.75 ME=.5 TF=99.52N NE=1.26 IK=1)")); npnModels.add(new Pair<>("BCW60A", ".model BCW60A NPN(IS=20f VAF=100 BF=120 IKF=0.8 XTB=1.5 BR=5" + " CJC=20p CJE=8p TR=100n TF=600p RB=10 RC=3 RE=1)")); }

public List> getNpnModels(){ return this.npnModels;}

private void setPnpModels(){ pnpModels.add(new Pair<>("2N2907", ".model 2N2907 PNP(IS=1E-14 VAF=120" + " BF=250 IKF=0.3 XTB=1.5 BR=3 CJC=8E-12 CJE=30E-12 TR=100E-9 TF=400E- 12" + " ITF=1 VTF=2 XTF=3 RB=10 RC=.3 RE=.2)")); pnpModels.add(new Pair<>("2N4126", ".model 2N4126 PNP(Is=1.41f Xti=3 Eg=1.11 Vaf=18.7 Bf=203.7" + " Ne=1.5 Ise=0 Ikf=80m Xtb=1.5 Br=4.924 Nc=2 Isc=0 Ikr=0 Rc=2.5 Cjc=9.728p" + " Mjc=.5776 Vjc=.75 Fc=.5 Cje=8.063p Mje=.3677 Vje=.75 Tr=33.23n Tf=179.3p Itf=.4"+ " Vtf=4 Xtf=6 Rb=10 Rb=10)")); pnpModels.add(new Pair<>("2N5087", ".model 2N5087 PNP(Is=6.734f Xti=3 Eg=1.11 Vaf=45.7 Bf=254.1 "+ "Ne=1.741 Ise=6.734f Ikf=.1962 Xtb=1.5 Br=2.683 Nc=2 Isc=0 Ikr=0 Rc=1.67 Cjc=6.2p " + "Mjc=.301 Vjc=.75 Fc=.5 Cje=7.5p Mje=.2861 Vje=.75 Tr=10.1n Tf=467.8p Itf=.17 Vtf=5 Xtf=8 "+ "Rb=10)")); pnpModels.add(new Pair<>("BCW68F", ".model BCW68F PNP(Is=40f VAF=100 Bf=175" + " IKF=2 Br=6 RC=.5 RE=0.1 RB=0.2 CJC=13p CJE=60p TR=12n TF=600p)")); }

public List> getPnpModels(){ return this.pnpModels;}

private void setNmosModels(){ nmosModels.add(new Pair<>("CPH3457", ".MODEL CPH3457 NMOS (LEVEL=8" + " VERSION= 3.2 TNOM= 27 TOX= 3.3E-08" + " VTH0= 0.96 K1= 1.96 K2= -0.04" + " NLX= 1.70E-07 DVT0= 3.12 DVT1= 0.58" + " DVT2= -0.01 U0= 630 UA= 2.20E-09" + " UB= 1.00E-21 VSAT= 1.75E05 A0= 0.34" + " AGS= 0.00E00 A1= 0 A2= 1" + " RDSW= 2.00E03 PRWG= -8.0E-03 WR= 0.82" + " WINT= 0 LINT= 3.00E-08 VOFF= -0.15" + " NFACTOR= 0.80 CIT= 0 CDSC= 2.40E-04" + " CDSCD= 0 ETA0= 1.00 DSUB= 0.96" + " PCLM= 0.80 PDIBLC1= 0.00 PDIBLC2= 4.9051E-03" + " DROUT= 0.96 PSCBE1= 5.946E09 PSCBE2= 1.04713E-05" + " PVAG= 0.01 DELTA= 0.05 NGATE= 1.0E19" + " MOBMOD= 1 NQSMOD= 0 NOIMOD= 1" + " CAPMOD= 3 XPART= 0.5 CGSO= 4.00E-11" + " CGDO= 1.00E-10 CGBO= 0 CGSL= 0" + " CGDL= 4.00E-10 CKAPPA= 1.20 CF= 0" + " CLC= 3.0E-09 CLE= 0.8 DWC= 0" + " DLC= -1.00E-11 NOFF= 1.5 VOFFCV= -0.40" + " ACDE= 1.0 MOIN= 15 CJ= 3.50E-04" + " MJ= 0.30 PB= 0.60 JS= 5.00E-07" + " NJ= 1.05 XTI= 3.0 IJTH= 0" + " KT1= -0.5 UTE= -0.9 PRT= 3.50E03" + " AT=3.3E04)")); nmosModels.add(new Pair<>("CPH3448", ".MODEL CPH3448 NMOS (LEVEL=8" + " VERSION=3.2 TNOM=27 TOX=3.3E-08" + " VTH0=0.96 K1=1.96 K2=-0.04" + " NLX=1.70E-07 DVT0=3.12 DVT1=0.58" + " DVT2=-0.01 U0=630 UA=2.20E-09" + " UB=1.00E-21 VSAT=1.75E05 A0=0.86" + " AGS=1.00 A1=0 A2=1" + " RDSW=1.45E03 PRWG=0.0E00 WR=0.82" + " WINT=0 LINT=-6.00E-08 VOFF=-0.13" + " NFACTOR=0.72 CIT=0 CDSC=2.40E-04" + " CDSCD=0 ETA0=0.12 DSUB=0.96" + " PCLM=0.80 PDIBLC1=0.00 PDIBLC2=1.00E-05" + " DROUT=0.96 PSCBE1=1.80E09 PSCBE2=6.61E-07" + " PVAG=0.01 DELTA=0.05 NGATE=1.0E19" + " MOBMOD=1 NQSMOD=0 NOIMOD=1" + " CAPMOD=3 XPART=0.5 CGSO=4.00E-11" + " CGDO=1.00E-12 CGBO=0 CGSL=0" + " CGDL=3.50E-10 CKAPPA=1.20 CF=0" + " CLC=3.0E-09 CLE=0.8 DWC=0" + " DLC=-1.00E-12 NOFF=3.66 VOFFCV=-0.40" + " ACDE=1.0 MOIN=15 CJ=2.00E-04" + " MJ=0.56 PB=0.70 JS=5.00E-07" + " NJ=1.05 XTI=3.0 IJTH=0" + " KT1=-0.7 UTE=-1.75 PRT=2.20E03" + " AT=3.3E04)")); }

public List> getNmosModels(){ return this.nmosModels;}

private void setPmosModels(){ pmosModels.add(new Pair<>("CPH3348", ".MODEL CPH3348 PMOS( LEVEL=8 VERSION=3.2 TNOM=27 TOX=3.0E-08" + " VTH0=-0.80 K1=1.96 K2=-0.04 NLX=3.20E-07 DVT0=1.12 DVT1=0.22 DVT2=- 0.01 U0=250 UA=1.80E-09" + " UB=1.00E-21 VSAT = 1.80E05 A0=0.16" + " AGS=0.04 A1=0 A2=1" + " RDSW=1.90E03 PRWG=-2.0E-03 WR=0.83" + " WINT=0 LINT=4.67E-08 VOFF=-0.13" + " NFACTOR=0.70 CIT=0 CDSC=2.40E-04" + " CDSCD=0 ETA0=0.08 DSUB=0.96" + " PCLM=2.80 PDIBLC1=0.32 PDIBLC2=1.60E-03" + " DROUT=0.96 PSCBE1=7.00E08 PSCBE2=1.00E-05" + " PVAG=0.01 DELTA=0.10 NGATE =1.0E19" + " MOBMOD=1 NQSMOD=0 NOIMOD=1" + " CAPMOD=3 XPART=0.5 CGSO=6.00E-11" + " CGDO=5.00E-11 CGBO=0 CGSL=0" + " CGDL=6.00E-10 CKAPPA=1.20 CF=0" + " CLC=3.0E-09 CLE=0.8 DWC=0" + " DLC=-1.00E-08 NOFF=3.66 VOFFCV=-0.40" + " ACDE=1.0 MOIN=15 CJ=3.50E-04" + " MJ=0.50 PB=0.60 JS=1.50E-07" + " NJ=1.00 XTI=3.0 IJTH=0" + " KT1=-0.5 UTE=-0.5 PRT=4.00E03" + " AT=3.3E04) ")); pmosModels.add(new Pair<>("CPH6341", ".MODEL CPH6341 PMOS ( LEVEL=8" + " VERSION=3.2 TNOM=27 TOX=6.0E-08" + " VTH0=-2.16 K1=3.12 K2=-0.04" + " NLX=1.70E-07 DVT0=1.12 DVT1=0.38" + " DVT2=-0.01 U0=230 UA=8.00E-10" + " UB=1.00E-21 VSAT=1.25E05 A0=0.62" + " AGS=0.30 A1=0 A2=1" + " RDSW=8.07E05 PRWG=-2.9E-03 WR=1.22" + " WINT=1.00E-01 LINT=1.20E-07 VOFF=-0.15" + " NFACTOR=0.76 CIT=0 CDSC=2.40E-04" + " CDSCD=0 ETA0=0.36 DSUB=1.00" + " PCLM=1.70 PDIBLC1=0.00 PDIBLC2=1.00E-05" + " DROUT=1.00 PSCBE1=2.30E09 PSCBE2 =3.00E-04" + " PVAG=0.01 DELTA=0.024 NGATE=1.0E19" + " MOBMOD=1 NQSMOD =0 NOIMOD =1" + " CAPMOD=3 XPART=0.5 CGSO=1.00E-12" + " CGDO=3.00E-11 CGBO=0 CGSL=0" + " CGDL=2.50E-10 CKAPPA=1.20 CF=0" + " CLC =3.0E-09 CLE=0.8 DWC =0" + " DLC=-1.00E-10 NOFF=3.66 VOFFCV =-0.80" + " ACDE=1.0 MOIN=15 CJ=1.80E-04" + " MJ=0.55 PB=0.41 JS=2.00E-07" + " NJ=1.00 XTI=3.0 IJTH=0" + " KT1=-0.7 UTE=-1.2 PRT=8.07E05" + " AT=3.3E04)")); }

public List> getPmosModels(){ return this.pmosModels;} }

////////////////////////////////////////////////////////////////////////////////////////////// /** * AnalysisController.java: Handles the Analysis Window */ ////////////////////////////////////////////////////////////////////////////////////////////// package CircuitSimulator.AnalysisDialog; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.scene.control.*; import javafx.stage.Stage; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; public class AnalysisController { // Panes and tabs @FXML private TabPane Analysis_Pane; @FXML private Tab tab_transient; @FXML private Tab tab_ac; @FXML private Tab tab_dc;

// Buttons @FXML private Button btn_trans_apply; @FXML private Button btn_trans_cancel; @FXML private Button btn_ac_apply; @FXML private Button btn_ac_cancel; @FXML private Button btn_dc_apply; @FXML private Button btn_dc_cancel;

// Text Fields @FXML private TextField txt_trans_time_incr; @FXML private TextField txt_trans_stop_time; @FXML private TextField txt_trans_start_time; @FXML private TextField txt_trans_comp_incr; @FXML private TextField txt_ac_pts; @FXML private TextField txt_ac_start_freq; @FXML private TextField txt_ac_end_freq; @FXML private TextField txt_dc_source_name; @FXML private TextField txt_dc_start_time; @FXML private TextField txt_dc_stop_time; @FXML private TextField txt_dc_incr;

// ComboBoxes @FXML private ChoiceBox cmb_ac_curve;

// Variables private Stage stage; private String netlistValue; private File file;

@FXML private void initialize() { netlistValue = ""; cmb_ac_curve.getItems().addAll("Linear", "Decade", "Octave"); cmb_ac_curve.getSelectionModel().selectFirst(); txt_trans_start_time.setPromptText("0.0"); txt_trans_stop_time.setPromptText("1.0"); txt_trans_time_incr.setPromptText("0.1"); txt_trans_comp_incr.setPromptText("1"); txt_ac_start_freq.setPromptText("60"); txt_ac_end_freq.setPromptText("60"); txt_ac_pts.setPromptText("1"); } public void setDialogStage(Stage stage) { this.stage = stage; } public void setCirFile(File file){ this.file = file;}

@FXML void btn_trans_cancel_pressed(ActionEvent event) { stage.close(); }

@FXML void btn_trans_apply_pressed(ActionEvent event) { netlistValue = ".tran " + txt_trans_time_incr.getText() + " " + txt_trans_stop_time.getText() + " " + txt_trans_start_time.getText() + " " + txt_trans_comp_incr.getText(); // TODO: Send the netlistValue over to the MainController class so that it can be used saveAnalysis(); stage.close(); }

@FXML void btn_ac_cancel_pressed(ActionEvent event) { stage.close(); }

@FXML void btn_ac_apply_pressed(ActionEvent event) { String selectedItem = cmb_ac_curve.getSelectionModel().getSelectedItem(); if(selectedItem.equals("Linear")){ selectedItem = "lin"; } else if(selectedItem.equals("Decade")){ selectedItem = "dec"; } else if(selectedItem.equals("Octave")){ selectedItem = "oct"; }

netlistValue = ".ac " + selectedItem + " " + txt_ac_pts.getText() + " " + txt_ac_start_freq.getText() + " " + txt_ac_end_freq.getText(); // TODO: send the netlistValue over to the MainController class so that it can be used saveAnalysis(); stage.close(); }

@FXML void btn_dc_cancel_pressed(ActionEvent event) { stage.close(); }

@FXML void btn_dc_apply_pressed(ActionEvent event) { netlistValue = ".dc " + txt_dc_source_name.getText() + " "+ txt_dc_start_time.getText() + " "+ txt_dc_stop_time.getText() + " "+ txt_dc_incr.getText(); saveAnalysis(); stage.close(); }

private void saveAnalysis(){ String str = "********* Analysis *********\n" + netlistValue;

try{ BufferedWriter bw = new BufferedWriter(new FileWriter(file, true)); bw.append(str); bw.flush(); bw.close(); } catch(IOException e){ e.printStackTrace(); } } }

////////////////////////////////////////////////////////////////////////////////////////////// /** * Circuit.java: Controls all circuit components */ ////////////////////////////////////////////////////////////////////////////////////////////// package Circuit; import CircuitSimulator.Listener; import Components.BasicComponents.BasicComponents; import Components.Component; import Components.Diodes.Diodes; import Components.Misc.Ground; import Components.Sources.*; import Components.Transistor; import Components.Transistors.*; import Components.Misc.Wire; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.scene.Group; import javafx.scene.Node; import javafx.scene.layout.AnchorPane; import javafx.scene.paint.Color; import javafx.scene.shape.Shape; import javafx.util.Pair; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class Circuit { // Private variables private ObservableList circuitList; private Object obj;

///////////////////////////////////////////////////////////////////////////////////////////// /** * Circuit Constructors * */ ///////////////////////////////////////////////////////////////////////////////////////////// // Initial Value Constructor public Circuit(){ // initialize circuit list circuitList = FXCollections.observableArrayList(); obj = new Object(); }

////////////////////////////////////////////////////////////////////////////////////////////// /** * Circuit Component Methods * */ ////////////////////////////////////////////////////////////////////////////////////////////// public void AddBasicComponent(String type, Double x, Double y, AnchorPane pane){ BasicComponents component = new BasicComponents(type, x, y); component.setName(component.getName()+circuitList.size()); component.Draw(pane); Listener listener = new Listener(component); circuitList.add(component); } public void AddDiodes(String type, Double x, Double y, AnchorPane pane){ Diodes diodes = new Diodes(type, x, y); diodes.setName(diodes.getName()+circuitList.size()); diodes.Draw(pane); Listener listener = new Listener(diodes); circuitList.add(diodes); } public void AddBJTs(String type, Double x, Double y, AnchorPane pane){ BJTs bjts = new BJTs(type, x ,y); bjts.setName(bjts.getName()+circuitList.size()); bjts.Draw(pane); Listener listener = new Listener(bjts); circuitList.add(bjts); } public void AddMOSFETs(String type, Double x, Double y, AnchorPane pane){ MOSFETs mosfeTs = new MOSFETs(type, x, y); mosfeTs.setName(mosfeTs.getName()+circuitList.size()); mosfeTs.Draw(pane); Listener listener = new Listener(mosfeTs); circuitList.add(mosfeTs); } public void AddSources(String type, Double x, Double y, AnchorPane pane){ Sources sources = new Sources(type, x, y); sources.setName(sources.getName()+circuitList.size()); sources.Draw(pane); Listener listener = new Listener(sources); circuitList.add(sources); } public void AddDepSources(String type, Double x, Double y, AnchorPane pane){ DependentSources dependentSources = new DependentSources(type, x, y); dependentSources.setName(dependentSources.getName()+circuitList.size()); dependentSources.Draw(pane); Listener listener = new Listener(dependentSources); circuitList.add(dependentSources); }

public void AddGround(Double x, Double y, AnchorPane pane){ Ground ground = new Ground(x, y); ground.Draw(pane); Listener listener = new Listener(ground); circuitList.add(ground); }

////////////////////////////////////////////////////////////////////////////////////////////// /** * Parse/Format Circuit List * */ ////////////////////////////////////////////////////////////////////////////////////////////// public List formatCircuitList(){ List circuitOutput = new ArrayList<>();

circuitList.forEach(item -> { String output = ""; if(item.getClass() == BasicComponents.class){ BasicComponents component = (BasicComponents)item; output = component.format(); } else if(item.getClass() == Diodes.class){ Diodes diodes = (Diodes)item; output = diodes.format(); } else if(item.getClass() == BJTs.class){ BJTs bjts = (BJTs)item; output = bjts.format(); } else if(item.getClass() == MOSFETs.class){ MOSFETs mosfeTs = (MOSFETs)item; output = mosfeTs.format(); } else if(item.getClass() == Sources.class){ Sources sources = (Sources)item; output = sources.format(); } else if(item.getClass() == DependentSources.class){ DependentSources depSources = (DependentSources) item; output = depSources.format(); } else if(item.getClass() == Ground.class){ Ground ground = (Ground)item; output = ground.format(); } circuitOutput.add(output); }); return circuitOutput; } public void parseCircuitList(String input, AnchorPane pane){ if(checkType(input, "Resistor") || checkType(input, "Capacitor") || checkType(input, "Inductor")){ Pair xValues = getValues(input, "xStart=", "xEnd="); Pair yValues = getValues(input, "yStart=", "yEnd="); Pair nodeValues = getNodes(input, "startNode=", "endNode="); BasicComponents basicComponents = new BasicComponents(getInfo(input, "type="), getInfo(input, "value="), getInfo(input, "name="), xValues, yValues, nodeValues, pane); circuitList.add(basicComponents); } else if(checkType(input, "Diode") || checkType(input, "LED") || checkType(input, "Zener")){ Pair xValues = getValues(input, "xStart=", "xEnd="); Pair yValues = getValues(input, "yStart=", "yEnd="); Pair nodeValues = getNodes(input, "startNode=", "endNode="); Diodes diodes = new Diodes(getInfo(input, "type="), getInfo(input, "name="), xValues, yValues, nodeValues, pane); circuitList.add(diodes); } else if(checkType(input, "Dependent Voltage Source") || checkType(input, "Dependent Current Source")){ Pair xValues = getValues(input, "xStart=", "xEnd="); Pair yValues = getValues(input, "yStart=", "yEnd="); Pair nodeValues = getNodes(input, "startNode=", "endNode="); DependentSources dependentSources = new DependentSources(getInfo(input, "type="), getInfo(input, "value="), getInfo(input, "name="), xValues, yValues, nodeValues, pane); circuitList.add(dependentSources); } else if(checkType(input, "Voltage Source") || checkType(input, "Current Source")){ Pair xValues = getValues(input, "xStart=", "xEnd="); Pair yValues = getValues(input, "yStart=", "yEnd="); Pair nodeValues = getNodes(input, "startNode=", "endNode="); Sources sources = new Sources(getInfo(input, "type="), getInfo(input, "value="), getInfo(input, "name="), xValues, yValues, nodeValues, pane); circuitList.add(sources); } else if(checkType(input, "NPN") || checkType(input, "PNP")){ Pair gate = getValues(input, "xGate=", "yGate="); Pair source = getValues(input, "xSource=", "ySource="); Pair drain = getValues(input, "xDrain=", "yDrain="); int gateNode = Integer.parseInt(getInfo(input, "gateNode=")); int sourceNode = Integer.parseInt(getInfo(input, "sourceNode=")); int drainNode = Integer.parseInt(getInfo(input, "drainNode=")); BJTs bjTs = new BJTs(getInfo(input, "type="), getInfo(input, "name="), gate, source, drain, gateNode, sourceNode, drainNode, pane); circuitList.add(bjTs); } else if(checkType(input, "NMOS") || checkType(input, "PMOS")){ Pair gate = getValues(input, "xGate=", "yGate="); Pair source = getValues(input, "xSource=", "ySource="); Pair drain = getValues(input, "xDrain=", "yDrain="); int gateNode = Integer.parseInt(getInfo(input, "gateNode=")); int sourceNode = Integer.parseInt(getInfo(input, "sourceNode=")); int drainNode = Integer.parseInt(getInfo(input, "drainNode=")); MOSFETs mosfeTs = new MOSFETs(getInfo(input, "type="), getInfo(input, "name="), gate, source, drain, gateNode, sourceNode, drainNode, pane); circuitList.add(mosfeTs); } else if(checkType(input, "Ground")){ Pair pair = getValues(input, "xStart=", "yStart="); Ground ground = new Ground(pair.getKey(), pair.getValue()); ground.Draw(pane); circuitList.add(ground); } }

private boolean checkType(String input, String type){ if(getInfo(input, "type=").equals(type)){ return true; } else { return false; } } private Pair getValues(String input, String x, String y){ Double xStart = Double.parseDouble(getInfo(input, x)); Double xEnd = Double.parseDouble(getInfo(input, y)); Pair pair = new Pair<>(xStart, xEnd); return pair; } private Pair getNodes(String input, String x, String y){ int startNode = Integer.parseInt(getInfo(input, x)); int endNode = Integer.parseInt(getInfo(input, y)); Pair pair = new Pair<>(startNode, endNode); return pair; } public String getInfo(String item, String type){ String[] tokens = item.split(";"); for(String t: tokens){ String[] subtoken = t.split(" "); for(int i=0; i

public List getCircuitList(){ return this.circuitList; } ////////////////////////////////////////////////////////////////////////////////////////////// /** * Remove Component from list * */ ////////////////////////////////////////////////////////////////////////////////////////////// public void findAndDeleteComponent(String input, AnchorPane pane){ double x = parseFindHelper(input, 'x'); double y = parseFindHelper(input, 'y');

circuitList.forEach(item -> { if(item.getClass() == BasicComponents.class){ BasicComponents component = (BasicComponents) item; if((component.getType().equals("Resistor") || component.getType().equals("Capacitor")) && (component.getStartX() == (x-5) && component.getStartY() == (y+10))){ component.remove(pane); obj = component; } else if(component.getType().equals("Inductor") && (component.getStartX() == (x-10) && component.getStartX() == (y+10))){ component.remove(pane); obj = component; } } else if(item.getClass() == Diodes.class){ Diodes diodes = (Diodes) item; if(diodes.getStartX() == (x-5) && diodes.getStartY() == (y+10)){ diodes.remove(pane); obj = diodes; } } else if(item.getClass() == BJTs.class){ BJTs bjts = (BJTs) item; if(bjts.getGateX() == (x-5) && bjts.getGateY() == (y+20)){ bjts.remove(pane); obj = bjts; } } else if(item.getClass() == MOSFETs.class){ MOSFETs mosfeTs = (MOSFETs) item; if(mosfeTs.getGateX() == (x-5) && mosfeTs.getGateY() == (y+45)){ mosfeTs.remove(pane); obj = mosfeTs; } } else if(item.getClass() == Sources.class){ Sources sources = (Sources) item; if(sources.getStartX() == (x-10) && sources.getStartY() == (y+20)){ sources.remove(pane); obj = sources; } } else if(item.getClass() == DependentSources.class){ DependentSources depSources = (DependentSources) item; if(depSources.getStartX() == (x-10) && depSources.getStartY() == (y+20)){ depSources.remove(pane); obj = depSources; } } else if(item.getClass() == Ground.class){ Ground ground = (Ground) item; if(ground.getStartX() == (x+10) && ground.getStartY() == (y-2.5)){ ground.remove(pane); obj = ground; } }

}); if(circuitList.size() > 0) { circuitList.remove(obj); } } private double parseFindHelper(String input, char var){ double num = -1; String numStr = ""; for(int i=0; i< input.length(); i++){ if(input.charAt(i) == '=' && input.charAt(i-1) == var){ int j=i+1; while(input.charAt(j) != ','){ numStr += input.charAt(j); j++; } break; } } num = Double.parseDouble(numStr); return num; }

////////////////////////////////////////////////////////////////////////////////////////////// /** * Check intersection and Set Nodes * */ ////////////////////////////////////////////////////////////////////////////////////////////// public void checkLineComponentIntersection(Wire wire){ circuitList.forEach(item -> { if(item.getClass() == BasicComponents.class){ BasicComponents component = (BasicComponents) item; checkComponentIntersection(component, wire); } else if(item.getClass() == Diodes.class){ Diodes diodes = (Diodes)item; checkComponentIntersection(diodes, wire); } else if(item.getClass() == BJTs.class){ BJTs bjts = (BJTs)item; checkTransistorIntersection(bjts, wire); } else if(item.getClass() == MOSFETs.class){ MOSFETs mosfeTs = (MOSFETs)item; checkTransistorIntersection(mosfeTs, wire); } else if(item.getClass() == Sources.class){ Sources sources = (Sources)item; checkComponentIntersection(sources, wire); } else if(item.getClass() == DependentSources.class){ DependentSources depSources = (DependentSources)item; checkComponentIntersection(depSources, wire); } else if(item.getClass() == Ground.class){ Ground ground = (Ground) item; checkGroundIntersection(ground, wire); } }); } public void reverseCircuitList(){ Collections.reverse(circuitList); }

////////////////////////////////////////////////////////////////////////////////////////////// /** * Intersection Check Helper Functions * */ ////////////////////////////////////////////////////////////////////////////////////////////// private void checkComponentIntersection(Component component, Wire wire){ Group group = component.getGroup(); group.getChildren().forEach(item -> { if(item.getClass().toString().equals("class javafx.scene.shape.Path")){ if(checkIntersection(wire, item)){ int flag = checkBounds(wire, component.getStartX(), component.getEndX(), component.getStartY(), component.getEndY()); if(flag == 0){ component.setStartNode(wire.getNode()); } else if(flag == 1){ component.setEndNode(wire.getNode()); } } } }); }

private void checkTransistorIntersection(Transistor transistor, Wire wire){ Group group = transistor.getGroup(); group.getChildren().forEach(item -> { if(item.getClass().toString().equals("class javafx.scene.shape.Path")){ if(checkIntersection(wire, item)){ int flag = checkTransistorBounds(wire, transistor.getGateX(), transistor.getGateY(), transistor.getDrainX(), transistor.getDrainY(), transistor.getSourceX(), transistor.getSourceY()); if(flag == 0){ transistor.setGateNode(wire.getNode()); } else if(flag == 1){ transistor.setDrainNode(wire.getNode()); } else if(flag == 2){ transistor.setSourceNode(wire.getNode()); } } } }); }

private void checkGroundIntersection(Ground ground, Wire wire){ Group group = ground.getGroup(); group.getChildren().forEach(item -> { if(item.getClass().toString().equals("class javafx.scene.shape.Path")){ if(checkIntersection(wire, item)){ if((wire.getStartX() == ground.getStartX() && wire.getStartY() == ground.getStartY()) || (wire.getEndX() == ground.getStartX() && wire.getEndY() == ground.getStartY())){ wire.setNode(ground.getStartNode()); } } } }); }

private int checkBounds(Wire wire, double startX, double endX, double startY, double endY){ if((wire.getStartX() == startX && wire.getStartY() == startY) || (wire.getEndX() == startX && wire.getEndY() == startY)){ return 0; } else if((wire.getStartX() == endX && wire.getStartY() == endY) || (wire.getEndX() == endX && wire.getEndY() == endY)){ return 1; } return 2; }

private int checkTransistorBounds(Wire wire, double xBase, double yBase, double xCollect, double yCollect, double xEmitter, double yEmitter){ if((wire.getStartX() == xBase && wire.getStartY() == yBase) || (wire.getEndX() == xBase && wire.getEndY() == yBase)){ return 0; } else if((wire.getStartX() == xCollect && wire.getStartY() == yCollect) || (wire.getEndX() == xCollect && wire.getEndY() == yCollect)){ return 1; } else if((wire.getStartX() == xEmitter && wire.getStartY() == yEmitter) || (wire.getEndX() == xEmitter && wire.getEndY() == yEmitter)){ return 2; } return 3; }

private boolean checkIntersection(Wire wire, Node path){ Shape intersect = Shape.intersect(wire, (Shape)path); if (intersect.getBoundsInLocal().getWidth() != -1) { //wire.setStroke(Color.GREEN); //((Shape) path).setStroke(Color.CYAN); return true; } return false; } }

////////////////////////////////////////////////////////////////////////////////////////////// /** * Component.java: Overlord class for components with 2 nodes */ ////////////////////////////////////////////////////////////////////////////////////////////// package Components; import javafx.scene.Group; import javafx.scene.layout.AnchorPane; import javafx.util.Pair; public class Component { private String name, type, value; private Double startX, oldXLoc, endX; private Double startY, oldYLoc, endY; private int startNode, endNode; private Group group;

public Component(String type, Double startX, Double startY){ this.type = type; this.startX = startX; this.startY = startY; }

public Component(String type, String name, String value, Pair xValues, Pair yValues, Pair nodes){ this.type = type; this.name = name; this.value = value; this.startX = xValues.getKey(); this.endX = xValues.getValue(); this.startY = yValues.getKey(); this.endY = yValues.getValue(); this.startNode = nodes.getKey(); this.endNode = nodes.getValue(); }

////////////////////////////////////////////////////////////////////////////////////////////// /** * Get Methods * */ ////////////////////////////////////////////////////////////////////////////////////////////// public String getName(){ return this.name;} public String getType(){ return this.type;} public String getValue(){ return this.value;} public Group getGroup(){ return this.group;} public Double getStartX(){ return this.startX;} public Double getOldXLoc(){ return this.oldXLoc;} public Double getEndX(){ return this.endX;} public Double getStartY(){ return this.startY;} public Double getOldYLoc(){ return this.oldYLoc;} public Double getEndY(){ return this.endY;} public int getStartNode(){ return this.startNode;} public int getEndNode(){ return this.endNode;}

////////////////////////////////////////////////////////////////////////////////////////////// /** * Set Methods * */ ////////////////////////////////////////////////////////////////////////////////////////////// public void setName(String name){ this.name = name;} public void setValue(String value){ this.value = value;} public void setGroup(Group group){ this.group = group;} public void setStartNode(int startNode){ this.startNode = startNode;} public void setEndNode(int endNode){ this.endNode = endNode;} public void setStartX(Double startX) { this.startX = startX;} public void setStartY(Double startY) { this.startY = startY;} public void setOldXLoc(Double oldXLoc){ this.oldXLoc = oldXLoc;} public void setOldYLoc(Double oldYLoc){ this.oldYLoc = oldYLoc;} public void setEndX(Double endX){ this.endX = endX;} public void setEndY(Double endY){ this.endY = endY;}

////////////////////////////////////////////////////////////////////////////////////////////// /** * Misc Helper Functions * */ ////////////////////////////////////////////////////////////////////////////////////////////// public void remove(AnchorPane pane){ pane.getChildren().remove(this.getGroup()); } }

////////////////////////////////////////////////////////////////////////////////////////////// /** * BasicComponents.java: Resistor, Capacitor, and Inductors all included here */ ////////////////////////////////////////////////////////////////////////////////////////////// package Components.BasicComponents; import Components.Component; import Components.Misc.DrawingOption; import javafx.scene.Group; import javafx.scene.layout.AnchorPane; import javafx.scene.shape.*; import javafx.util.Pair;

public class BasicComponents extends Component{

public BasicComponents(String type, double x, double y){ super(type, x, y); if(type.equals("Resistor")){ setName("Res"); } else if(type.equals("Capacitor")){ setName("Cap"); } else if(type.equals("Inductor")){ setName("Ind"); } setValue("0.0"); }

public BasicComponents(String type, String name, String value, Pair xValues, Pair yValues, Pair nodes, AnchorPane pane){ super(type, name, value, xValues, yValues, nodes); Draw(pane); }

////////////////////////////////////////////////////////////////////////////////////////////// /** * Draw Methods * */ ////////////////////////////////////////////////////////////////////////////////////////////// public void Draw(AnchorPane pane){ this.setGroup(new Group()); if(this.getType().equals("Resistor")){ DrawResistor(pane); } else if(this.getType().equals("Capacitor")){ DrawCapacitor(pane); } else if(this.getType().equals("Inductor")){ DrawInductor(pane); } }

private void DrawResistor(AnchorPane pane){ this.setEndX(this.getStartX()+70); this.setEndY(this.getStartY()); DrawingOption option = new DrawingOption(); Path resPath = option.createResistorPath(this.getStartX(), this.getStartY()); Rectangle startRect = option.createBlueRectangle(this.getStartX()-2.5, this.getStartY()-2.5, 5d, 5d); Rectangle endRect = option.createBlueRectangle(this.getStartX()+67.5, getStartY()-2.5, 5d, 5d); Rectangle invsRect = option.createInvisibleRectangle(this.getStartX()+5, this.getStartY()- 10, 60d, 20d); this.getGroup().getChildren().addAll(resPath, startRect, endRect, invsRect); pane.getChildren().add(this.getGroup()); }

private void DrawCapacitor(AnchorPane pane){ this.setEndX(this.getStartX()+50); this.setEndY(this.getStartY()); DrawingOption option = new DrawingOption(); Path capPath = option.createCapacitorPath(this.getStartX(), this.getStartY()); Rectangle startRect = option.createBlueRectangle(this.getStartX()-2.5, this.getStartY()-2.5, 5d, 5d); Rectangle endRect = option.createBlueRectangle(this.getStartX()+47.5, getStartY()-2.5, 5d, 5d); Rectangle invsRect = option.createInvisibleRectangle(this.getStartX()+5, this.getStartY()- 10, 40d, 20d); this.getGroup().getChildren().addAll(capPath, startRect, endRect, invsRect); pane.getChildren().add(this.getGroup()); }

private void DrawInductor(AnchorPane pane){ this.setEndX(this.getStartX()+50); this.setEndY(this.getStartY()); DrawingOption option = new DrawingOption(); Path indPath = option.createInductorPath(this.getStartX(), this.getStartY()); Group arcs = option.createInductorArcs(this.getStartX(), this.getStartY()); Rectangle startRect = option.createBlueRectangle(this.getStartX()-2.5, this.getStartY()-2.5, 5d, 5d); Rectangle endRect = option.createBlueRectangle(this.getStartX()+77.5, this.getStartY()- 2.5, 5d, 5d); Rectangle invsRect = option.createInvisibleRectangle(this.getStartX()+5, this.getStartY()- 10, 40d, 20d); this.getGroup().getChildren().addAll(indPath, arcs, startRect, endRect, invsRect); pane.getChildren().add(this.getGroup()); }

////////////////////////////////////////////////////////////////////////////////////////////// /** * Parse/Format Values * */ ////////////////////////////////////////////////////////////////////////////////////////////// public String format(){ String input = "<;type= "+this.getType()+" ;"+ "name= "+this.getName()+" ;"+ "value= "+this.getValue()+" ;"+ "xStart= "+this.getStartX()+" ;"+ "yStart= "+this.getStartY()+" ;"+ "xEnd= "+this.getEndX()+" ;"+ "yEnd= "+this.getEndY()+" ;"+ "startNode= "+this.getStartNode()+" ;"+ "endNode= "+this.getEndNode()+" ;/>"; return input; } }

////////////////////////////////////////////////////////////////////////////////////////////// /** * Diodes.java: Diodes, LEDs, and Zener Diodes contained here */ ////////////////////////////////////////////////////////////////////////////////////////////// package Components.Diodes; import Components.Component; import Components.Misc.DrawingOption; import javafx.scene.Group; import javafx.scene.layout.AnchorPane; import javafx.scene.shape.*; import javafx.util.Pair; public class Diodes extends Component{

public Diodes(String type, double x, double y){ super(type, x, y); String name=""; if(type.equals("Diode")){ name = "Diode"; } else if(type.equals("LED")){ name = "LED"; } else if(type.equals("Zener")){ name = "Zener"; } this.setName(name); }

public Diodes(String type, String name, Pair xValues, Pair yValues, Pair nodes, AnchorPane pane){ super(type, name, "", xValues, yValues, nodes); Draw(pane); }

////////////////////////////////////////////////////////////////////////////////////////////// /** * Draw Methods * */ ////////////////////////////////////////////////////////////////////////////////////////////// public void Draw(AnchorPane pane){ this.setEndX(this.getStartX()+50); this.setEndY(this.getStartY()); this.setGroup(new Group());

if(this.getType().equals("Diode")){ DrawDiode(pane); } else if(this.getType().equals("LED")){ DrawLED(pane); } else if(this.getType().equals("Zener")){ DrawZener(pane); } }

// endX = start+50 for all three private void DrawDiode(AnchorPane pane){ DrawingOption option = new DrawingOption(); Path path = option.createDiodePath(this.getStartX(), this.getStartY()); Rectangle startRect = option.createBlueRectangle(this.getStartX()-2.5, this.getStartY()-2.5, 5d, 5d); Rectangle endRect = option.createBlueRectangle(this.getStartX()+47.5, this.getStartY()- 2.5, 5d, 5d); Rectangle invsRect = option.createInvisibleRectangle(this.getStartX()+5, this.getStartY()- 10, 40d, 20d); this.getGroup().getChildren().addAll(path, startRect, endRect, invsRect); pane.getChildren().add(this.getGroup()); }

private void DrawLED(AnchorPane pane){ DrawingOption option = new DrawingOption(); Path path = option.createDiodePath(this.getStartX(), this.getStartY()); Path path2 = option.createLEDArrowPath(this.getStartX(), this.getStartY()); Rectangle startRect = option.createBlueRectangle(this.getStartX()-2.5, this.getStartY()-2.5, 5d, 5d); Rectangle endRect = option.createBlueRectangle(this.getStartX()+47.5, this.getStartY()- 2.5, 5d, 5d); Rectangle invsRect = option.createInvisibleRectangle(this.getStartX()+5, this.getStartY()- 10, 40d, 20d); this.getGroup().getChildren().addAll(path, path2, startRect, endRect, invsRect); pane.getChildren().add(this.getGroup()); }

private void DrawZener(AnchorPane pane){ DrawingOption option = new DrawingOption(); Path path = option.createZenerPath(this.getStartX(), this.getStartY()); Rectangle startRect = option.createBlueRectangle(this.getStartX()-2.5, this.getStartY()-2.5, 5d, 5d); Rectangle endRect = option.createBlueRectangle(this.getStartX()+47.5, this.getStartY()- 2.5, 5d, 5d); Rectangle invsRect = option.createInvisibleRectangle(this.getStartX()+5, this.getStartY()- 10, 40d, 20d); this.getGroup().getChildren().addAll(path, startRect, endRect, invsRect); pane.getChildren().add(this.getGroup()); }

////////////////////////////////////////////////////////////////////////////////////////////// /** * Parse/Format Values * */ ////////////////////////////////////////////////////////////////////////////////////////////// public String format(){ String input = "<;type= "+this.getType()+" ;"+ "name= "+this.getName()+" ;"+ "xStart= "+this.getStartX()+" ;"+ "yStart= "+this.getStartY()+" ;"+ "xEnd= "+this.getEndX()+" ;"+ "yEnd= "+this.getEndY()+" ;"+ "startNode= "+this.getStartNode()+" ;"+ "endNode= "+this.getEndNode()+" ;/>"; return input; } }

////////////////////////////////////////////////////////////////////////////////////////////// /** * Sources.java: Controls voltage and current sources */ ////////////////////////////////////////////////////////////////////////////////////////////// package Components.Sources; import Components.Component; import Components.Misc.DrawingOption; import javafx.scene.Group; import javafx.scene.layout.AnchorPane; import javafx.scene.shape.*; import javafx.util.Pair;

public class Sources extends Component{

public Sources(String type, Double x, Double y){ super(type, x, y); if(type.equals("Voltage Source")){ this.setName("vSource"); } else if(type.equals("Current Source")){ this.setName("cSource"); } this.setValue(""); }

public Sources(String type, String value, String name, Pair xValues, Pair yValues, Pair nodes, AnchorPane pane){ super(type, name, value, xValues, yValues, nodes); Draw(pane); }

////////////////////////////////////////////////////////////////////////////////////////////// /** * Draw Methods * */ ////////////////////////////////////////////////////////////////////////////////////////////// public void Draw(AnchorPane pane){ this.setEndX(this.getStartX()+80); this.setEndY(this.getStartY()); this.setGroup(new Group());

if(this.getType().equals("Voltage Source")) { DrawVSource(pane); } else if(this.getType().equals("Current Source")){ DrawCSource(pane); } }

private void DrawVSource(AnchorPane pane){ DrawingOption option = new DrawingOption(); Path path = option.createVoltagePath(this.getStartX(), this.getStartY()); Rectangle startRect = option.createBlueRectangle(this.getStartX()-2.5, this.getStartY()-2.5, 5d, 5d); Rectangle endRect = option.createBlueRectangle(this.getStartX()+77.5, this.getStartY()- 2.5, 5d, 5d); Rectangle invsRect = option.createInvisibleRectangle(this.getStartX()+10, this.getStartY()- 20, 60d, 40d); this.getGroup().getChildren().addAll(path, startRect, endRect, invsRect); pane.getChildren().add(this.getGroup()); }

private void DrawCSource(AnchorPane pane){ DrawingOption option = new DrawingOption(); Path path = option.createCurrentPath(this.getStartX(), this.getStartY()); Rectangle startRect = option.createBlueRectangle(this.getStartX()-2.5, this.getStartY()-2.5, 5d, 5d); Rectangle endRect = option.createBlueRectangle(this.getStartX()+77.5, this.getStartY()- 2.5, 5d, 5d); Rectangle invsRect = option.createInvisibleRectangle(this.getStartX()+10, this.getStartY()- 20, 60d, 40d); this.getGroup().getChildren().addAll(path, startRect, endRect, invsRect); pane.getChildren().add(this.getGroup()); }

////////////////////////////////////////////////////////////////////////////////////////////// /** * Parse/Format Values * */ ////////////////////////////////////////////////////////////////////////////////////////////// public String format(){ String input = "<;type= "+this.getType()+" ;"+ "name= "+this.getName()+" ;"+ "value= "+this.getValue()+" ;"+ "xStart= "+this.getStartX()+" ;"+ "yStart= "+this.getStartY()+" ;"+ "xEnd= "+this.getEndX()+" ;"+ "yEnd= "+this.getEndY()+" ;"+ "startNode= "+this.getStartNode()+" ;"+ "endNode= "+this.getEndNode()+" ;/>"; return input; } }

////////////////////////////////////////////////////////////////////////////////////////////// /** * DependentSources.java: Controls all dependent sources */ ////////////////////////////////////////////////////////////////////////////////////////////// package Components.Sources;

import Components.Component; import Components.Misc.DrawingOption; import javafx.scene.Group; import javafx.scene.layout.AnchorPane; import javafx.scene.shape.*; import javafx.util.Pair; public class DependentSources extends Component{

public DependentSources(String type, Double x, Double y){ super(type, x, y); if(type.equals("Dependent Voltage Source")){ this.setName("DepVS"); } else if(type.equals("Dependent Current Source")){ this.setName("DepCS"); }

this.setValue(""); }

public DependentSources(String type, String value, String name, Pair xValues, Pair yValues, Pair nodes, AnchorPane pane){ super(type, name, value, xValues, yValues, nodes); Draw(pane); }

////////////////////////////////////////////////////////////////////////////////////////////// /** * Draw Methods * */ ////////////////////////////////////////////////////////////////////////////////////////////// public void Draw(AnchorPane pane){ this.setEndX(this.getStartX()+50); this.setEndY(this.getStartY()); this.setGroup(new Group());

if(this.getType().equals("Dependent Voltage Source")){ DrawDepVS(pane); } else if(this.getType().equals("Dependent Current Source")){ DrawDepCS(pane); } }

private void DrawDepVS(AnchorPane pane){ DrawingOption option = new DrawingOption(); Path path = option.createDepVoltagePath(this.getStartX(), this.getStartY()); Rectangle startRect = option.createBlueRectangle(this.getStartX()-2.5, this.getStartY()-2.5, 5d, 5d); Rectangle endRect = option.createBlueRectangle(this.getStartX()+57.5, this.getStartY()- 2.5, 5d, 5d); Rectangle invsRect = option.createInvisibleRectangle(this.getStartX()+10, this.getStartY()- 20, 40d, 40d); this.getGroup().getChildren().addAll(path, startRect, endRect, invsRect); pane.getChildren().add(this.getGroup()); }

private void DrawDepCS(AnchorPane pane){ DrawingOption option = new DrawingOption(); Path path = option.createCurrentDepPath(this.getStartX(), this.getStartY()); Rectangle startRect = option.createBlueRectangle(this.getStartX()-2.5, this.getStartY()-2.5, 5d, 5d); Rectangle endRect = option.createBlueRectangle(this.getStartX()+57.5, this.getStartY()- 2.5, 5d, 5d); Rectangle invsRect = option.createInvisibleRectangle(this.getStartX()+10, this.getStartY()- 20, 40d, 40d); this.getGroup().getChildren().addAll(path, startRect, endRect, invsRect); pane.getChildren().add(this.getGroup()); }

////////////////////////////////////////////////////////////////////////////////////////////// /** * Parse/Format Values * */ ////////////////////////////////////////////////////////////////////////////////////////////// public String format(){ String input = "<;type= "+this.getType()+" ;"+ "name= "+this.getName()+" ;"+ "value= "+this.getValue()+" ;"+ "xStart= "+this.getStartX()+" ;"+ "yStart= "+this.getStartY()+" ;"+ "xEnd= "+this.getEndX()+" ;"+ "yEnd= "+this.getEndY()+" ;"+ "startNode= "+this.getStartNode()+" ;"+ "endNode= "+this.getEndNode()+" ;/>"; return input; } }

////////////////////////////////////////////////////////////////////////////////////////////// /** * Transistor.java: Controls all components with 3 nodes */ ////////////////////////////////////////////////////////////////////////////////////////////// package Components; import javafx.scene.Group; import javafx.scene.layout.AnchorPane; import javafx.util.Pair; public class Transistor { private String name, type; private Double gateX, sourceX, drainX, oldXLoc; private Double gateY, sourceY, drainY, oldYLoc; private Group group; private int gateNode, sourceNode, drainNode; private String modelName;

public Transistor(String type, Double x, Double y){ this.type = type; this.gateX = x; this.gateY = y; this.sourceX = 0d; this.sourceY = 0d; this.drainX = 0d; this.drainX = 0d; this.gateNode = 0; this.sourceNode = 0; this.drainNode = 0; }

public Transistor(String type, String name, Pair gate, Pair source, Pair drain, int gateNode, int sourceNode, int drainNode){ this.type = type; this.name = name; this.gateX = gate.getKey(); this.gateY = gate.getValue(); this.sourceX = source.getKey(); this.sourceY = source.getValue(); this.drainX = drain.getKey(); this.drainY = drain.getValue(); this.gateNode = gateNode; this.sourceNode = sourceNode; this.drainNode = drainNode; }

////////////////////////////////////////////////////////////////////////////////////////////// /** * Get Methods * */ ////////////////////////////////////////////////////////////////////////////////////////////// public String getName(){ return this.name;} public String getType(){ return this.type;} public Double getGateX(){ return this.gateX;} public Double getSourceX(){ return this.sourceX;} public Double getDrainX(){ return this.drainX;} public Double getOldXLoc(){ return this.oldXLoc;} public Double getGateY(){ return this.gateY;} public Double getSourceY(){ return this.sourceY;} public Double getDrainY(){ return this.drainY;} public Double getOldYLoc(){ return this.oldYLoc;} public Group getGroup(){ return this.group;} public int getGateNode(){ return this.gateNode;} public int getSourceNode(){ return this.sourceNode;} public int getDrainNode(){ return this.drainNode;} public String getModelName(){ return this.modelName; }

////////////////////////////////////////////////////////////////////////////////////////////// /** * Set Methods * */ ////////////////////////////////////////////////////////////////////////////////////////////// public void setName(String name){ this.name = name;} public void setType(String type){ this.type = type;} public void setGateX(Double gateX){ this.gateX = gateX;} public void setSourceX(Double sourceX){ this.sourceX = sourceX;} public void setDrainX(Double drainX){ this.drainX = drainX;} public void setOldXLoc(Double oldXLoc){ this.oldXLoc = oldXLoc;} public void setGateY(Double gateY){ this.gateY = gateY;} public void setSourceY(Double sourceY){ this.sourceY = sourceY;} public void setDrainY(Double drainY){ this.drainY = drainY;} public void setOldYLoc(Double oldYLoc){ this.oldYLoc = oldYLoc;} public void setGroup(Group group){ this.group = group;} public void setGateNode(int node){ this.gateNode = node;} public void setSourceNode(int node){ this.sourceNode = node;} public void setDrainNode(int node){ this.drainNode = node;} public void setModelName(String modelName){ this.modelName = modelName; }

////////////////////////////////////////////////////////////////////////////////////////////// /** * Misc Helper Methods * */ ////////////////////////////////////////////////////////////////////////////////////////////// public void remove(AnchorPane pane){ pane.getChildren().remove(this.group); } }

////////////////////////////////////////////////////////////////////////////////////////////// /** * MOSFETs.java: controls NMOS and PMOS components */ ////////////////////////////////////////////////////////////////////////////////////////////// package Components.Transistors; import Components.Misc.DrawingOption; import Components.Transistor; import javafx.scene.Group; import javafx.scene.layout.AnchorPane; import javafx.scene.shape.Path; import javafx.scene.shape.Rectangle; import javafx.util.Pair;

public class MOSFETs extends Transistor{

public MOSFETs(String type, Double x, Double y){ super(type, x, y); if(type.equals("NMOS")){ this.setName("NMOS"); } else if(type.equals("PMOS")){ this.setName("PMOS"); } }

public MOSFETs(String type, String name, Pair gate, Pair source, Pair drain, int gateNode, int sourceNode, int drainNode, AnchorPane pane){ super(type, name, gate, source, drain, gateNode, sourceNode, drainNode); Draw(pane); }

////////////////////////////////////////////////////////////////////////////////////////////// /** * Draw Method * */ ////////////////////////////////////////////////////////////////////////////////////////////// public void Draw(AnchorPane pane){ this.setGroup(new Group()); this.setSourceX(this.getGateX()+40); this.setSourceY(this.getGateY()-50); this.setDrainX(this.getGateX()+40); this.setDrainY(this.getGateY()-5);

if(this.getType().equals("NMOS")){ DrawNMOS(pane); } else if(this.getType().equals("PMOS")){ DrawPMOS(pane); } }

private void DrawNMOS(AnchorPane pane){ DrawingOption option = new DrawingOption(); Path path = option.createNMOSPath(this.getGateX(), this.getGateY()); Rectangle gateRect = option.createBlueRectangle(this.getGateX()-2.5, this.getGateY()-2.5, 5d, 5d); Rectangle sourceRect = option.createBlueRectangle(this.getGateX()+37.5, this.getGateY()- 52.5, 5d, 5d); Rectangle drainRect = option.createBlueRectangle(this.getGateX()+37.5, this.getGateY()- 2.5, 5d, 5d); Rectangle invsRect = option.createInvisibleRectangle(this.getGateX()+5, this.getGateY()- 45, 30d, 50d); this.getGroup().getChildren().addAll(path, gateRect, sourceRect, drainRect, invsRect); pane.getChildren().add(this.getGroup()); }

private void DrawPMOS(AnchorPane pane){ DrawingOption option = new DrawingOption(); Path path = option.createPMOSPath(this.getGateX(), this.getGateY()); Rectangle gateRect = option.createBlueRectangle(this.getGateX()-2.5, this.getGateY()-2.5, 5d, 5d); Rectangle sourceRect = option.createBlueRectangle(this.getGateX()+37.5, this.getGateY()- 52.5, 5d, 5d); Rectangle drainRect = option.createBlueRectangle(this.getGateX()+37.5, this.getGateY()- 2.5, 5d, 5d); Rectangle invsRect = option.createInvisibleRectangle(this.getGateX()+5, this.getGateY()- 45, 30d, 50d); this.getGroup().getChildren().addAll(path, gateRect, sourceRect, drainRect, invsRect); pane.getChildren().add(this.getGroup()); } ////////////////////////////////////////////////////////////////////////////////////////////// /** * Parse/Format Values * */ ////////////////////////////////////////////////////////////////////////////////////////////// public String format(){ String input = "<;type= "+this.getType()+" ;"+ "name= "+this.getName()+" ;"+ "xGate= "+this.getGateX()+" ;"+ "yGate= "+this.getGateY()+" ;"+ "xSource= "+this.getSourceX()+" ;"+ "ySource= "+this.getSourceY()+" ;"+ "xDrain= "+this.getDrainX()+" ;"+ "yDrain= "+this.getDrainY()+" ;"+ "sourceNode= "+this.getSourceNode()+" ;"+ "gateNode= "+this.getGateNode()+" ;"+ "drainNode= "+this.getDrainNode()+" ;/>"; return input; } } ////////////////////////////////////////////////////////////////////////////////////////////// /** * BJTs.java: Controls NPN and PNP components */ ////////////////////////////////////////////////////////////////////////////////////////////// package Components.Transistors; import Components.Misc.DrawingOption; import Components.Transistor; import javafx.scene.Group; import javafx.scene.layout.AnchorPane; import javafx.scene.shape.Path; import javafx.scene.shape.Rectangle; import javafx.util.Pair; public class BJTs extends Transistor{

public BJTs(String type, Double x, Double y){ super(type, x, y); if(type.equals("NPN")){ this.setName("NPN"); } else if(type.equals("PNP")){ this.setName("PNP"); } }

public BJTs(String type, String name, Pair gate, Pair source, Pair drain, int gateNode, int sourceNode, int drainNode, AnchorPane pane){ super(type, name, gate, source, drain, gateNode, sourceNode, drainNode); Draw(pane); }

////////////////////////////////////////////////////////////////////////////////////////////// /** * Draw Method * */ ////////////////////////////////////////////////////////////////////////////////////////////// public void Draw(AnchorPane pane){ this.setGroup(new Group()); this.setSourceX(this.getGateX()+30); this.setSourceY(this.getGateY()-20); this.setDrainX(this.getGateX()+30); this.setDrainY(this.getGateY()+20);

if(this.getType().equals("NPN")){ DrawNPN(pane); } else if(this.getType().equals("PNP")){ DrawPNP(pane); } }

//xCollect = xBase+30; yCollect = yBase-20; //xEmitter = xBase+30; yEmitter = yBase+20; private void DrawNPN(AnchorPane pane){ DrawingOption option = new DrawingOption(); Path path = option.createNPNPath(this.getGateX(), this.getGateY()); Rectangle gateRect = option.createBlueRectangle(this.getGateX()-2.5, this.getGateY()-2.5, 5d, 5d); Rectangle sourceRect = option.createBlueRectangle(this.getGateX()+27.5, this.getGateY()- 22.5, 5d, 5d); Rectangle drainRect = option.createBlueRectangle(this.getGateX()+27.5, this.getGateY()+17.5, 5d, 5d); Rectangle invsRect = option.createInvisibleRectangle(this.getGateX()+5, this.getGateY()- 20, 20d, 40d); this.getGroup().getChildren().addAll(path, gateRect, sourceRect, drainRect, invsRect); pane.getChildren().add(this.getGroup()); }

private void DrawPNP(AnchorPane pane){ DrawingOption option = new DrawingOption(); Path path = option.createPNPPath(this.getGateX(), this.getGateY()); Rectangle gateRect = option.createBlueRectangle(this.getGateX()-2.5, this.getGateY()-2.5, 5d, 5d); Rectangle sourceRect = option.createBlueRectangle(this.getGateX()+27.5, this.getGateY()- 22.5, 5d, 5d); Rectangle drainRect = option.createBlueRectangle(this.getGateX()+27.5, this.getGateY()+17.5, 5d, 5d); Rectangle invsRect = option.createInvisibleRectangle(this.getGateX()+5, this.getGateY()- 20, 20d, 40d); this.getGroup().getChildren().addAll(path, gateRect, sourceRect, drainRect, invsRect); pane.getChildren().add(this.getGroup()); }

////////////////////////////////////////////////////////////////////////////////////////////// /** * Parse/Format Values * */ ////////////////////////////////////////////////////////////////////////////////////////////// public String format(){ String input = "<;type= "+this.getType()+" ;"+ "name= "+this.getName()+" ;"+ "xGate= "+this.getGateX()+" ;"+ "yGate= "+this.getGateY()+" ;"+ "xSource= "+this.getSourceX()+" ;"+ "ySource= "+this.getSourceY()+" ;"+ "xDrain= "+this.getDrainX()+" ;"+ "yDrain= "+this.getDrainY()+" ;"+ "gateNode= "+this.getGateNode()+" ;"+ "SourceNode= "+this.getSourceNode()+" ;"+ "drainNode= "+this.getDrainNode()+" ;/>"; return input; } }

////////////////////////////////////////////////////////////////////////////////////////////// /** * DrawingOption.java: Controls all path drawing */ ////////////////////////////////////////////////////////////////////////////////////////////// package Components.Misc;

import javafx.scene.Group; import javafx.scene.paint.Color; import javafx.scene.shape.*;

public class DrawingOption { public DrawingOption(){

}

////////////////////////////////////////////////////////////////////////////////////////////// /** * Draw Paths for components * */ ////////////////////////////////////////////////////////////////////////////////////////////// public Path createResistorPath(Double startX, Double startY){ Path path = new Path(); MoveTo moveTo = new MoveTo(startX, startY); LineTo line1 = new LineTo(startX+10, startY); LineTo line2 = new LineTo(startX+15, startY+10); LineTo line3 = new LineTo(startX+25, startY-10); LineTo line4 = new LineTo(startX+35, startY+10); LineTo line5 = new LineTo(startX+45, startY-10); LineTo line6 = new LineTo(startX+55, startY+10); LineTo line7 = new LineTo(startX+60, startY); LineTo line8 = new LineTo(startX+70, startY);

path.getElements().addAll(moveTo, line1, line2, line3, line4, line5, line6, line7, line8); return path; }

public Path createCapacitorPath(Double startX, Double startY){ Path path = new Path(); MoveTo moveTo = new MoveTo(startX, startY); LineTo line1 = new LineTo(startX+20, startY); MoveTo moveTo1 = new MoveTo(startX+20, startY-10); LineTo line2 = new LineTo(startX+20, startY+10); MoveTo moveTo2 = new MoveTo(startX+30, startY-10); LineTo line3 = new LineTo(startX+30,startY+10); MoveTo moveTo3 = new MoveTo(startX+30, startY); LineTo line4 = new LineTo(startX+50, startY);

path.getElements().addAll(moveTo, line1, moveTo1, line2, moveTo2, line3, moveTo3, line4); return path; }

public Path createInductorPath(Double startX, Double startY){ Path path = new Path(); MoveTo moveTo = new MoveTo(startX, startY); LineTo line1 = new LineTo(startX+12.5, startY); MoveTo moveTo1 = new MoveTo(startX+67.5, startY); LineTo line2 = new LineTo(startX+80, startY);

path.getElements().addAll(moveTo, line1, moveTo1, line2); return path; }

public Group createInductorArcs(Double startX, Double startY){ Group arcs = new Group(); // Create the arcs of the inductor Arc arc = SetArcValues(startX + 25, startY, 12.5, 12.5, 180, 235); Arc arc1 = SetArcValues(startX + 40, startY, 12.5, 12.5, 125, 290); Arc arc2 = SetArcValues(startX + 55, startY, 12.5, 12.5, 130, 230); arc.setDisable(true); arc1.setDisable(true); arc2.setDisable(true); // Now add to group arcs.getChildren().addAll(arc, arc1, arc2); return arcs; }

private Arc SetArcValues(double centerX, double centerY, double radiusX, double radiusY, double startAngle, double length){ Arc arc = new Arc(); arc.setCenterX(centerX); arc.setCenterY(centerY); arc.setRadiusX(radiusX); arc.setRadiusY(radiusY); arc.setStartAngle(startAngle); arc.setLength(length); arc.setType(ArcType.OPEN); arc.setFill(Color.TRANSPARENT); arc.setStroke(Color.BLACK); return arc; }

public Path createDiodePath(Double startX, Double startY){ Path path = new Path(); MoveTo moveTo = new MoveTo(startX, startY); LineTo lineTo = new LineTo(startX+15, startY); LineTo lineTo1 = new LineTo(startX+15, startY-10); LineTo lineTo2 = new LineTo(startX+35, startY); LineTo lineTo3 = new LineTo(startX+15, startY+10); LineTo lineTo4 = new LineTo(startX+15, startY); MoveTo moveTo1 = new MoveTo(startX+35, startY); LineTo lineTo5 = new LineTo(startX+50, startY); MoveTo moveTo2 = new MoveTo(startX+35, startY-10); LineTo lineTo6 = new LineTo(startX+35, startY+10); path.getElements().addAll(moveTo, lineTo, lineTo1, lineTo2, lineTo3, lineTo4, moveTo1, lineTo5, moveTo2, lineTo6); return path; } public Path createLEDArrowPath(Double startX, Double startY){ Path path = new Path(); MoveTo arrow_move = new MoveTo(startX+20, startY-2.5); LineTo arrow_line = new LineTo(startX+27.5, startY-10); LineTo arrow_line2 = new LineTo(startX+32.5, startY-5); LineTo arrow_line3 = new LineTo(startX+40, startY-12.5); MoveTo arrow_move2 = new MoveTo(startX+35, startY-12.5); LineTo arrow_line4 = new LineTo(startX+40, startY-12.5); LineTo arrow_line5 = new LineTo(startX+40, startY-7.5);

MoveTo arrow2_move = new MoveTo(startX+30, startY-2.5); LineTo arrow2_line = new LineTo(startX+37.5, startY-10); LineTo arrow2_line2 = new LineTo(startX+42.5, startY-5); LineTo arrow2_line3 = new LineTo(startX+50, startY-12.5); MoveTo arrow2_move2 = new MoveTo(startX+45, startY-12.5); LineTo arrow2_line4 = new LineTo(startX+50, startY-12.5); LineTo arrow2_line5 = new LineTo(startX+50, startY-7.5);

path.setStroke(Color.RED); path.getElements().addAll(arrow_move, arrow_line, arrow_line2,arrow_line3, arrow_move2, arrow_line4, arrow_line5, arrow2_move, arrow2_line, arrow2_line2, arrow2_line3, arrow2_move2, arrow2_line4, arrow2_line5); return path; } public Path createZenerPath(Double startX, Double startY){ Path path = new Path(); MoveTo moveTo = new MoveTo(startX, startY); LineTo lineTo = new LineTo(startX+15, startY); LineTo lineTo1 = new LineTo(startX+15, startY-10); LineTo lineTo2 = new LineTo(startX+35, startY); LineTo lineTo3 = new LineTo(startX+15, startY+10); LineTo lineTo4 = new LineTo(startX+15, startY); MoveTo moveTo1 = new MoveTo(startX+35, startY); LineTo lineTo5 = new LineTo(startX+50, startY); MoveTo moveTo2 = new MoveTo(startX+40, startY-10); LineTo lineTo6 = new LineTo(startX+35, startY-5); LineTo lineTo7 = new LineTo(startX+35, startY+5); LineTo lineTo8 = new LineTo(startX+30, startY+10); path.getElements().addAll(moveTo, lineTo, lineTo1, lineTo2, lineTo3, lineTo4, moveTo1, lineTo5, moveTo2, lineTo6, lineTo7, lineTo8); return path; } public Path createVoltagePath(Double startX, Double startY){ Path path = new Path();

MoveTo moveTo = new MoveTo(startX, startY); LineTo line1 = new LineTo(startX+15, startY); ArcTo arc1 = new ArcTo(); arc1.setX(startX+65); arc1.setY(startY); arc1.setRadiusX(20); arc1.setRadiusY(20);

ArcTo arc2 = new ArcTo(); arc2.setX(startX+15); arc2.setY(startY); arc2.setRadiusX(15); arc2.setRadiusY(15); MoveTo moveTo1 = new MoveTo(startX+40, startY);

// Add + sign to path. MoveTo moveTo2 = new MoveTo(startX+20, startY); LineTo line3 = new LineTo(startX+30, startY); MoveTo moveTo3 = new MoveTo(startX+25, startY-5); LineTo line4 = new LineTo(startX+25 , startY+5); // Add - sign to path. MoveTo moveTo4 = new MoveTo(startX+47.5, startY); LineTo line5 = new LineTo(startX+57.5, startY); // Negative leg MoveTo moveTo5 = new MoveTo(startX+65, startY); LineTo line6 = new LineTo(startX+80, startY);

path.getElements().addAll(moveTo, line1, arc1, arc2, moveTo1, moveTo2, line3, moveTo3, line4, moveTo4, line5, moveTo5, line6);

return path; } public Path createCurrentPath(Double startX, Double startY){ Path path = new Path();

MoveTo moveTo = new MoveTo(startX, startY); LineTo line1 = new LineTo(startX+15, startY); ArcTo arc1 = new ArcTo(); arc1.setX(startX+65); arc1.setY(startY); arc1.setRadiusX(20); arc1.setRadiusY(20);

ArcTo arc2 = new ArcTo(); arc2.setX(startX+15); arc2.setY(startY); arc2.setRadiusX(15); arc2.setRadiusY(15); MoveTo moveTo1 = new MoveTo(startX+40, startY);

// Negative leg MoveTo moveTo2 = new MoveTo(startX+65, startY); LineTo line2 = new LineTo(startX+80, startY);

// Create Arrow MoveTo moveTo3 = new MoveTo(startX+55, startY); LineTo line3 = new LineTo(startX+25, startY); LineTo line4 = new LineTo(startX+32.5, startY+7.5); LineTo line5 = new LineTo(startX+32.5, startY-7.5); LineTo line6 = new LineTo(startX+25, startY);

path.getElements().addAll(moveTo, line1, arc1, arc2, moveTo1, moveTo2, line2, moveTo3, line3, line4, line5, line6);

return path; } public Path createDepVoltagePath(Double startX, Double startY){ Path path = new Path();

MoveTo moveTo = new MoveTo(startX, startY); LineTo line1 = new LineTo(startX+10, startY); LineTo line2 = new LineTo(startX+30, startY-20); LineTo line3 = new LineTo(startX+50, startY); LineTo line4 = new LineTo(startX+30, startY+20); LineTo line5 = new LineTo(startX+10, startY); MoveTo moveTo1 = new MoveTo(startX+50, startY); LineTo line6 = new LineTo(startX+60, startY); // Add + sign to component MoveTo moveTo2 = new MoveTo(startX+15, startY); LineTo line7 = new LineTo(startX+25, startY); MoveTo moveTo3 = new MoveTo(startX+20, startY-5); LineTo line8 = new LineTo(startX+20, startY+5); // Add - sign to component MoveTo moveTo4 = new MoveTo(startX+35, startY); LineTo line9 = new LineTo(startX+42.5, startY);

path.getElements().addAll(moveTo, line1, line2, line3, line4, line5, moveTo1, line6, moveTo2, line7, moveTo3, line8, moveTo4, line9);

return path; } public Path createCurrentDepPath(Double startX, Double startY){ Path path = new Path();

MoveTo moveTo = new MoveTo(startX, startY); LineTo line1 = new LineTo(startX+10, startY); LineTo line2 = new LineTo(startX+30, startY-20); LineTo line3 = new LineTo(startX+50, startY); LineTo line4 = new LineTo(startX+30, startY+20); LineTo line5 = new LineTo(startX+10, startY); MoveTo moveTo1 = new MoveTo(startX+50, startY); LineTo line6 = new LineTo(startX+60, startY); // Draw Arrow MoveTo moveTo2 = new MoveTo(startX+40, startY); LineTo line7 = new LineTo(startX+20, startY); LineTo line8 = new LineTo(startX+25, startY+5); LineTo line9 = new LineTo(startX+25, startY-5); LineTo line10 = new LineTo(startX+20, startY); path.getElements().addAll(moveTo, line1, line2, line3, line4, line5, moveTo1, line6, moveTo2, line7, line8, line9, line10);

return path; }

public Path createGroundPath(Double startX, Double startY){ Path path = new Path(); MoveTo moveTo = new MoveTo(startX, startY); LineTo lineTo = new LineTo(startX, startY+10); MoveTo moveTo1 = new MoveTo(startX-9, startY+10); LineTo lineTo1 = new LineTo(startX+9, startY+10); MoveTo moveTo2 = new MoveTo(startX-6, startY+15); LineTo lineTo2 = new LineTo(startX+6, startY+15); MoveTo moveTo3 = new MoveTo(startX-3, startY+20); LineTo lineTo3 = new LineTo( startX+3, startY+20);

path.getElements().addAll(moveTo, lineTo, moveTo1, lineTo1, moveTo2, lineTo2, moveTo3, lineTo3); return path; }

public Path createNPNPath(Double startX, Double startY){ Path path = new Path();

MoveTo moveTo = new MoveTo(startX, startY); LineTo lineTo = new LineTo(startX+10, startY); MoveTo moveTo1 = new MoveTo(startX+10, startY-20); LineTo lineTo1 = new LineTo(startX+10, startY+20); // Upper Line MoveTo moveTo2 = new MoveTo(startX+10, startY-7.5); LineTo lineTo2 = new LineTo(startX+30, startY-20); // Lower Line and arrow MoveTo moveTo3 = new MoveTo(startX+10, startY+7.5); LineTo lineTo3 = new LineTo(startX+17.5, startY+12.2); LineTo lineTo4 = new LineTo(startX+20, startY+8.5); LineTo lineTo5 = new LineTo( startX+23, startY+16); LineTo lineTo6 = new LineTo(startX+15, startY+16); LineTo lineTo7 = new LineTo(startX+17.5, startY+12.2); MoveTo moveTo4 = new MoveTo(startX+23, startY+16); LineTo lineTo8 = new LineTo(startX+30, startY+20);

// Add elements path.getElements().addAll(moveTo, lineTo, moveTo1, lineTo1, moveTo2, lineTo2, moveTo3, lineTo3, lineTo4, lineTo5, lineTo6, lineTo7, moveTo4, lineTo8); return path; }

public Path createPNPPath(Double startX, Double startY){ Path path = new Path();

MoveTo moveTo = new MoveTo(startX, startY); LineTo lineTo = new LineTo(startX+10, startY); MoveTo moveTo1 = new MoveTo(startX+10, startY-20); LineTo lineTo1 = new LineTo(startX+10, startY+20); // Upper Line MoveTo moveTo2 = new MoveTo(startX+10, startY-7.5); LineTo lineTo2 = new LineTo(startX+30, startY-20); // Lower Line and arrow MoveTo moveTo3 = new MoveTo(startX+10, startY+7.5); LineTo lineTo3 = new LineTo(startX+22.5, startY+11.25); LineTo lineTo4 = new LineTo(startX+18.5, startY+18.5); LineTo lineTo5 = new LineTo(startX+10, startY+7.5); MoveTo moveTo4 = new MoveTo(startX+21.5, startY+15); LineTo lineTo6 = new LineTo(startX+30, startY+20);

// Add elements path.getElements().addAll(moveTo, lineTo, moveTo1, lineTo1, moveTo2, lineTo2, moveTo3, lineTo3, lineTo4, lineTo5, moveTo4, lineTo6); return path; } public Path createNMOSPath(Double startX, Double startY){ Path path = new Path(); // Initial line MoveTo moveTo = new MoveTo(startX, startY); LineTo lineTo = new LineTo(startX+10, startY); LineTo lineTo1 = new LineTo(startX+10, startY-50); // Dotted Line down the middle MoveTo moveTo1 = new MoveTo(startX+20, startY-50); LineTo lineTo2 = new LineTo(startX+20, startY-40); MoveTo moveTo2 = new MoveTo(startX+20, startY-30); LineTo lineTo3 = new LineTo(startX+20, startY-20); MoveTo moveTo3 = new MoveTo(startX+20, startY-10); LineTo lineTo4 = new LineTo(startX+20, startY); // Top Line MoveTo moveTo4 = new MoveTo(startX+20, startY-45); LineTo lineTo5 = new LineTo(startX+40, startY-45); LineTo lineTo6 = new LineTo(startX+40, startY-50); // Middle Arrow and line MoveTo moveTo5 = new MoveTo(startX+20, startY-25); LineTo lineTo7 = new LineTo(startX+30, startY-30); LineTo lineTo8 = new LineTo(startX+30, startY-20); LineTo lineTo9 = new LineTo(startX+20, startY-25); MoveTo moveTo6 = new MoveTo(startX+30, startY-25); LineTo lineTo10 = new LineTo(startX+40, startY-25); LineTo lineTo11 = new LineTo(startX+40, startY); // Bottom Line MoveTo moveTo7 = new MoveTo(startX+20, startY-5); LineTo lineTo12 = new LineTo(startX+40, startY-5);

path.getElements().addAll(moveTo, lineTo, lineTo1, moveTo1, lineTo2, moveTo2, lineTo3, moveTo3, lineTo4, moveTo4, lineTo5, lineTo6, moveTo5, lineTo7, lineTo8, lineTo9, moveTo6, lineTo10, lineTo11, moveTo7, lineTo12);

return path; }

public Path createPMOSPath(Double startX, Double startY){ Path path = new Path(); // Initial line MoveTo moveTo = new MoveTo(startX, startY); LineTo lineTo = new LineTo(startX+10, startY); LineTo lineTo1 = new LineTo(startX+10, startY-50); // Dotted Line down the middle MoveTo moveTo1 = new MoveTo(startX+20, startY-50); LineTo lineTo2 = new LineTo(startX+20, startY-40); MoveTo moveTo2 = new MoveTo(startX+20, startY-30); LineTo lineTo3 = new LineTo(startX+20, startY-20); MoveTo moveTo3 = new MoveTo(startX+20, startY-10); LineTo lineTo4 = new LineTo(startX+20, startY); // Top Line MoveTo moveTo4 = new MoveTo(startX+20, startY-45); LineTo lineTo5 = new LineTo(startX+40, startY-45); LineTo lineTo6 = new LineTo(startX+40, startY-50); // Middle Arrow and line MoveTo moveTo5 = new MoveTo(startX+20, startY-25); LineTo lineTo7 = new LineTo(startX+30, startY-25); LineTo lineTo8 = new LineTo(startX+30, startY-30); LineTo lineTo9 = new LineTo(startX+40, startY-25); LineTo lineTo10 = new LineTo(startX+30, startY-20); LineTo lineTo11 = new LineTo(startX+30, startY-25); MoveTo moveTo6 = new MoveTo(startX+40, startY-25); LineTo lineTo12 = new LineTo(startX+40, startY-25); LineTo lineTo13 = new LineTo(startX+40, startY); // Bottom Line MoveTo moveTo7 = new MoveTo(startX+20, startY-5); LineTo lineTo14 = new LineTo(startX+40, startY-5);

path.getElements().addAll(moveTo, lineTo, lineTo1, moveTo1, lineTo2, moveTo2, lineTo3, moveTo3, lineTo4, moveTo4, lineTo5, lineTo6, moveTo5, lineTo7, lineTo8, lineTo9, lineTo10, lineTo11, moveTo6, lineTo12, lineTo13, moveTo7, lineTo14);

return path; }

////////////////////////////////////////////////////////////////////////////////////////////// /** * Draw Rectangles * */ ////////////////////////////////////////////////////////////////////////////////////////////// public Rectangle createBlueRectangle(Double x, Double y, Double width, Double height){ Rectangle blueRect = new Rectangle(x, y, width, height); blueRect.setFill(Color.TRANSPARENT); blueRect.setStroke(Color.BLUE); blueRect.setDisable(true); return blueRect; }

public Rectangle createInvisibleRectangle(Double x, Double y, Double width, Double height){ Rectangle invRect = new Rectangle(x, y, width, height); invRect.setFill(Color.TRANSPARENT); return invRect; } }

////////////////////////////////////////////////////////////////////////////////////////////// /** * Ground.java: controls the ground component, not used in the SPICE file */ ////////////////////////////////////////////////////////////////////////////////////////////// package Components.Misc; import Components.Component; import javafx.scene.Group; import javafx.scene.layout.AnchorPane; import javafx.scene.shape.Path; import javafx.scene.shape.Rectangle; public class Ground extends Component {

public Ground(Double xStart, Double yStart) { super("Ground", xStart, yStart); this.setStartNode(0); this.setEndNode(0); }

////////////////////////////////////////////////////////////////////////////////////////////// /** * Draw Method * */ ////////////////////////////////////////////////////////////////////////////////////////////// public void Draw(AnchorPane pane){ this.setGroup(new Group()); DrawingOption option = new DrawingOption(); Path gnd = option.createGroundPath(this.getStartX(), this.getStartY()); Rectangle rect = option.createBlueRectangle(this.getStartX()-2.5, this.getStartY()-2.5, 5d, 5d); Rectangle invsRect = option.createInvisibleRectangle(this.getStartX()-10, this.getStartY()+2.5, 20d, 20d); this.getGroup().getChildren().addAll(gnd, rect, invsRect); pane.getChildren().add(this.getGroup()); }

////////////////////////////////////////////////////////////////////////////////////////////// /** * Formatting * */ ////////////////////////////////////////////////////////////////////////////////////////////// public String format(){ String input = "<;type= Ground ;xStart= " + this.getStartX() + " ;yStart= " + this.getStartY() + " ;/>";

return input; } }

////////////////////////////////////////////////////////////////////////////////////////////// /** * Wire.java: Controls the wire component */ ////////////////////////////////////////////////////////////////////////////////////////////// package Components.Misc; import javafx.scene.paint.Color; import javafx.scene.shape.Line; public class Wire extends Line { private int node;

public Wire(Double xStart, Double yStart, Double xEnd, Double yEnd){ setStartX(xStart); setStartY(yStart); setEndX(xEnd); setEndY(yEnd); setStroke(Color.BLACK); node = 0; }

public Wire(int node, Double xStart, Double yStart, Double xEnd, Double yEnd){ this.node = node; setStartX(xStart); setStartY(yStart); setEndX(xEnd); setEndY(yEnd); setStroke(Color.BLACK); }

public int getNode() { return this.node; } public void setNode(int node){ this.node = node; } public String format(){ String input = "<;type= Wire ;"+ "node= "+this.getNode()+" ;"+ "xStart= "+this.getStartX()+" ;"+ "yStart= "+this.getStartY()+" ;"+ "xEnd= "+this.getEndX()+" ;"+ "yEnd= "+this.getEndY()+" ;/>"; return input; } }

////////////////////////////////////////////////////////////////////////////////////////////// /** * NetlistSetup.java: controls the creation of the netlist file for ngspice */ ////////////////////////////////////////////////////////////////////////////////////////////// package Netlist; import CircuitSimulator.CircuitModels; import Components.BasicComponents.BasicComponents; import Components.Component; import Components.Diodes.Diodes; import Components.Sources.DependentSources; import Components.Sources.Sources; import Components.Transistor; import Components.Transistors.BJTs; import Components.Transistors.MOSFETs; import java.util.ArrayList; import java.util.List; public class NetlistSetup { private List circuitList; private List spiceList;

public NetlistSetup(List circuitList){ this.circuitList = circuitList; spiceList = new ArrayList<>(); setSpiceList(); }

////////////////////////////////////////////////////////////////////////////////////////////// /** * Set up for the Spice netlist file (.cir extension) * */ ////////////////////////////////////////////////////////////////////////////////////////////// private void setSpiceList(){ spiceList.add("Senior Project - Circuit Title"); spiceList.add("\n********* Options *********"); spiceList.add(".options noacct"); spiceList.add("\n********* Circuit Models *********"); setCircuitModels(); spiceList.add("\n********* Circuit Definition *********"); circuitList.forEach(obj -> { if(obj.getClass() == BasicComponents.class){ Component component = (Component)obj; spiceList.add(getComponentInfo(component)); } else if(obj.getClass() == Diodes.class) { Diodes diodes = (Diodes) obj; spiceList.add(getDiodeInfo(diodes)); } else if(obj.getClass() == BJTs.class){ BJTs bjTs = (BJTs) obj; spiceList.add(getTransistorInfo(bjTs)); } else if(obj.getClass() == MOSFETs.class){ MOSFETs mosfeTs = (MOSFETs) obj; spiceList.add(getTransistorInfo(mosfeTs)); } else if(obj.getClass() == Sources.class){ Sources sources = (Sources) obj; spiceList.add(getComponentInfo(sources)); } else if(obj.getClass() == DependentSources.class){

} }); } public void setEnd(){ spiceList.add("\n********* End *********"); spiceList.add(".end"); } public List returnSpiceList(){ return spiceList; } private void setCircuitModels(){ CircuitModels circuitModels = new CircuitModels(); circuitModels.getDiodeModels().forEach(item -> spiceList.add(item.getValue())); circuitModels.getNpnModels().forEach(item -> spiceList.add(item.getValue())); circuitModels.getPnpModels().forEach(item -> spiceList.add(item.getValue())); circuitModels.getNmosModels().forEach(item -> spiceList.add(item.getValue())); circuitModels.getPmosModels().forEach(item -> spiceList.add(item.getValue())); } ////////////////////////////////////////////////////////////////////////////////////////////// /** * Setup helper functions * */ ////////////////////////////////////////////////////////////////////////////////////////////// private String getComponentInfo(Component component){ String type = ""; if(component.getType().equals("Resistor")){ type = "r"; } else if(component.getType().equals("Capacitor")){ type = "c"; } else if(component.getType().equals("Inductor")){ type = "l"; } else if(component.getType().equals("Voltage Source")){ type = "v"; } else if(component.getType().equals("Current Source")){ type = "i"; }

String info = ""; info += type + component.getName()+" "+ component.getStartNode()+" "+ component.getEndNode()+" "+ component.getValue(); return info; }

private String getDiodeInfo(Diodes diodes){ String info; info = "d"+ diodes.getName()+" "+ diodes.getStartNode()+" "+ diodes.getEndNode()+" "+ diodes.getType(); return info; }

private String getTransistorInfo(Transistor transistor){ String type = ""; if(transistor.getType().equals("NPN") || transistor.getType().equals("PNP")){ type = "q"; } else if(transistor.getType().equals("NMOS") || transistor.getType().equals("PMOS")){ type = "m"; } String info = ""; info += type + transistor.getName()+" "+ transistor.getDrainNode()+" "+ transistor.getGateNode()+" "+ transistor.getSourceNode()+" "+ transistor.getType()+" "+ transistor.getModelName();

return info; } }

////////////////////////////////////////////////////////////////////////////////////////////// /** * circuitSimulator.fxml: fxml file for the main GUI component * */ //////////////////////////////////////////////////////////////////////////////////////////////