//Written for www.HomoFaciens.de
//Program simulates the behaviour of electrons and holes inside of a semiconductor diode.
//Copyright (C) 2011 Norbert Heinz
//
//This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation version 3 of the License.
//
//This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
//
//You should have received a copy of the GNU General Public License along with this program; if not, see http://www.gnu.org/licenses/.

import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.net.URL;
import javax.swing.*;
import java.text.*;
import java.util.Random;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.imageio.ImageIO;
import java.io.File;
import java.io.IOException;


public class PNJunction extends JApplet implements  ActionListener, ChangeListener {
	private static final long serialVersionUID = 1L;
	static String versionNumber="v1.0";

    ScenePainter painter;
    JSpinner crystalSizeSpinner, recombinationTimeSpinner, creationChanceSpinner, exFieldStrengthSpinner, electronMobilitySpinner, holeMobilitySpinner, temperatureSpinner;
    JButton startButton, arrangeNewButton, restartButton, moveUpButton, moveDownButton, moveRightButton, moveLeftButton;
    JLabel crystalSizeLabel, recombinationTimeLabel, creationChanceLabel, exFieldStrengthLabel, electronMobilityLabel, holeMobilityLabel, temperatureLabel;
    JLabel crystalSizeUnitLabel, exFieldStrengthUnitLabel, electronMobilityUnitLabel, temperatureUnitLabel, creationChanceUnitLabel;
    JLabel sourceLabel;
    JCheckBox showStatsCheckBox;
    JRadioButton forwardButton, reverseButton;
    Timer timer;             //timer for simulation
    int timerPause=100;       //delay between two calculated frames in milliseconds
    Random RandomGenerator = new Random(1);

    public void init() {
        UIManager.put("swing.boldMetal", Boolean.FALSE);
    }

    public void start() {
        initComponents();
        timer = new Timer(timerPause, new timerEvent());
    }

    public static void main(String[] args) {

        JFrame f = new JFrame("P/N junction");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JApplet ap = new PNJunction();
        ap.init();
        ap.start();
        f.getContentPane().add("Center", ap);
        f.pack();
        f.setVisible(true);

    }

    private BufferedImage loadImage(String name) {
        String imgFileName = "pictures/"+name;
        URL url = PNJunction.class.getResource(imgFileName);
        BufferedImage img = null;
        try {
            img =  ImageIO.read(url);
        }
        catch (Exception e){
          img=null;
          System.out.println("Error loading Image '" + imgFileName + "': " + e); // Display the string.
        }
        return img;
    }

    public void initComponents() {
        JFormattedTextField ftf = null;
        GridBagConstraints c = new GridBagConstraints();
        Font font = new Font("Arial", Font.PLAIN, 12);

        getContentPane().setLayout(new BorderLayout());

        //create panels
        JPanel controlPanel = new JPanel();
        JPanel movePanel = new JPanel();
        JPanel scenePanel = new JPanel();
        JPanel mainPane = new JPanel();

        //set panel properties
        controlPanel.setLayout(new GridBagLayout());
        movePanel.setLayout(new GridBagLayout());
        mainPane.setLayout(new BoxLayout(mainPane, BoxLayout.PAGE_AXIS));
        mainPane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));

        //create buttons, labels etcetera
          startButton = new JButton("Start");
          startButton.setMnemonic(KeyEvent.VK_S);

          restartButton = new JButton("Restart");

          moveUpButton = new JButton("Up");
          moveDownButton = new JButton("Down");
          moveLeftButton = new JButton("Left");
          moveRightButton = new JButton("Right");

          arrangeNewButton = new JButton("Arrange new");
          arrangeNewButton.setMnemonic(KeyEvent.VK_R);

          creationChanceLabel = new JLabel("New pairs:");
          crystalSizeLabel = new JLabel("Size:");
          recombinationTimeLabel = new JLabel("Recombination Time:");
          exFieldStrengthLabel = new JLabel("External Field");
          electronMobilityLabel = new JLabel("Elect. mob.");
          holeMobilityLabel = new JLabel("Hole mobility");
          temperatureLabel = new JLabel("Temperature");
          forwardButton = new JRadioButton("Forward polarity");
          reverseButton = new JRadioButton("Reverse polarity");
          showStatsCheckBox = new JCheckBox("Show statistic");
          forwardButton.setMnemonic(KeyEvent.VK_Q);
          reverseButton.setMnemonic(KeyEvent.VK_T);

        crystalSizeUnitLabel = new JLabel("%");
        exFieldStrengthUnitLabel = new JLabel("%");
        electronMobilityUnitLabel = new JLabel("%");
        temperatureUnitLabel = new JLabel("%");
        creationChanceUnitLabel = new JLabel("%");
        sourceLabel = new JLabel(versionNumber + "   Source & info:    www.HomoFaciens.de");

        showStatsCheckBox.setFont(font);
        showStatsCheckBox.setSelected(true);
        showStatsCheckBox.setActionCommand("showStatsPressed");
        showStatsCheckBox.addActionListener(this);

        forwardButton.setFont(font);
        forwardButton.setActionCommand("forwardButtonPressed");
        forwardButton.setSelected(true);
        forwardButton.addActionListener(this);

        reverseButton.setFont(font);
        reverseButton.setActionCommand("reverseButtonPressed");
        reverseButton.setSelected(false);
        reverseButton.addActionListener(this);

        ButtonGroup group = new ButtonGroup();
        group.add(forwardButton);
        group.add(reverseButton);


        SpinnerNumberModel exFieldStrengthModel = new SpinnerNumberModel(0, 0, 100, 10);
        exFieldStrengthSpinner = new JSpinner(exFieldStrengthModel);
        ftf = getTextField(exFieldStrengthSpinner);
        if (ftf != null ) {
            ftf.setFont(font);
            ftf.setColumns(4); //specify more width than we need
            ftf.setHorizontalAlignment(JTextField.RIGHT);
        }
        exFieldStrengthSpinner.addChangeListener(this);

        SpinnerNumberModel electronMobilityModel = new SpinnerNumberModel(18, 1, 20, 1);
        electronMobilitySpinner = new JSpinner(electronMobilityModel);
        ftf = getTextField(electronMobilitySpinner);
        if (ftf != null ) {
            ftf.setFont(font);
            ftf.setColumns(4); //specify more width than we need
            ftf.setHorizontalAlignment(JTextField.RIGHT);
        }
        electronMobilitySpinner.addChangeListener(this);

        SpinnerNumberModel holeMobilityModel = new SpinnerNumberModel(10, 1, 20, 1);
        holeMobilitySpinner = new JSpinner(holeMobilityModel);
        ftf = getTextField(holeMobilitySpinner);
        if (ftf != null ) {
            ftf.setFont(font);
            ftf.setColumns(4); //specify more width than we need
            ftf.setHorizontalAlignment(JTextField.RIGHT);
        }
        holeMobilitySpinner.addChangeListener(this);

        SpinnerNumberModel creationChanceModel = new SpinnerNumberModel(0, 0, 100, 1);
        creationChanceSpinner = new JSpinner(creationChanceModel);
        ftf = getTextField(creationChanceSpinner);
        if (ftf != null ) {
            ftf.setFont(font);
            ftf.setColumns(4); //specify more width than we need
            ftf.setHorizontalAlignment(JTextField.RIGHT);
        }
        creationChanceSpinner.addChangeListener(this);

        SpinnerNumberModel crystalSizeModel = new SpinnerNumberModel(5, 5, 100, 1);
        crystalSizeSpinner = new JSpinner(crystalSizeModel);
        ftf = getTextField(crystalSizeSpinner);
        if (ftf != null ) {
            ftf.setFont(font);
            ftf.setColumns(4); //specify more width than we need
            ftf.setHorizontalAlignment(JTextField.RIGHT);
        }
        crystalSizeSpinner.addChangeListener(this);
        crystalSizeLabel.setLabelFor(crystalSizeSpinner);

        SpinnerNumberModel recombinationTimeModel = new SpinnerNumberModel(15, 0, 100, 1);
        recombinationTimeSpinner = new JSpinner(recombinationTimeModel);
        ftf = getTextField(recombinationTimeSpinner);
        if (ftf != null ) {
            ftf.setFont(font);
            ftf.setColumns(4); //specify more width than we need
            ftf.setHorizontalAlignment(JTextField.RIGHT);
        }
        recombinationTimeSpinner.addChangeListener(this);

        SpinnerNumberModel temperatureModel = new SpinnerNumberModel(10, 1, 100, 1);
        temperatureSpinner = new JSpinner(temperatureModel);
        ftf = getTextField(temperatureSpinner);
        if (ftf != null ) {
            ftf.setFont(font);
            ftf.setColumns(4); //specify more width than we need
            ftf.setHorizontalAlignment(JTextField.RIGHT);
        }
        temperatureSpinner.addChangeListener(this);


        painter = new ScenePainter();

        painter.atomSilicon = loadImage("silicon.png");
        painter.atomLightBlueUp = loadImage("silicon-lightblue-up.png");
        painter.atomLightBlueDown = loadImage("silicon-lightblue-down.png");
        painter.atomLightRedUp = loadImage("silicon-lightred-up.png");
        painter.atomLightRedDown = loadImage("silicon-lightred-down.png");
        painter.atomDarkBlueUp = loadImage("silicon-darkblue-up.png");
        painter.atomDarkBlueDown = loadImage("silicon-darkblue-down.png");
        painter.atomDarkRedUp = loadImage("silicon-darkred-up.png");
        painter.atomDarkRedDown = loadImage("silicon-darkred-down.png");
        painter.atomGreyUp = loadImage("aluminum-up.png");
        painter.atomGreyDown = loadImage("aluminum-down.png");
        painter.atomGoldUp = loadImage("phosphorus-up.png");
        painter.atomGoldDown = loadImage("phosphorus-down.png");
        painter.recombination = loadImage("recombination.png");


        //set properties of controls
        crystalSizeUnitLabel.setFont(font);
        creationChanceUnitLabel.setFont(font);

        creationChanceLabel.setFont(font);
        crystalSizeLabel.setFont(font);
        recombinationTimeLabel.setFont(font);

        sourceLabel.setFont(font);

        startButton.setFont(font);
        startButton.setActionCommand("startButtonPressed");
        startButton.addActionListener(this);

        restartButton.setFont(font);
        restartButton.setActionCommand("restartButtonPressed");
        restartButton.addActionListener(this);


        arrangeNewButton.setFont(font);
        arrangeNewButton.setActionCommand("arrangeNewButtonPressed");
        arrangeNewButton.addActionListener(this);

        moveUpButton.setFont(font);
        moveUpButton.setActionCommand("moveUpButtonPressed");
        moveUpButton.addActionListener(this);

        moveDownButton.setFont(font);
        moveDownButton.setActionCommand("moveDownButtonPressed");
        moveDownButton.addActionListener(this);

        moveLeftButton.setFont(font);
        moveLeftButton.setActionCommand("moveLeftButtonPressed");
        moveLeftButton.addActionListener(this);

        moveRightButton.setFont(font);
        moveRightButton.setActionCommand("moveRightButtonPressed");
        moveRightButton.addActionListener(this);


        //add components
//Learn about GridBagConstraints and about GridBagLayout
        c.fill = GridBagConstraints.HORIZONTAL;
        c.anchor=GridBagConstraints.CENTER;
        c.gridx = 1;
        c.gridy = 0;
        c.gridwidth=1;
        movePanel.add(moveUpButton, c);

        c.fill = GridBagConstraints.HORIZONTAL;
        c.anchor=GridBagConstraints.CENTER;
        c.gridx = 0;
        c.gridy = 1;
        c.gridwidth=1;
        movePanel.add(moveLeftButton, c);

        c.fill = GridBagConstraints.HORIZONTAL;
        c.anchor=GridBagConstraints.CENTER;
        c.gridx = 2;
        c.gridy = 1;
        c.gridwidth=1;
        movePanel.add(moveRightButton, c);

        c.fill = GridBagConstraints.HORIZONTAL;
        c.anchor=GridBagConstraints.CENTER;
        c.gridx = 1;
        c.gridy = 2;
        c.gridwidth=1;
        movePanel.add(moveDownButton, c);

        c.fill = GridBagConstraints.HORIZONTAL;
        c.anchor=GridBagConstraints.CENTER;
        c.gridx = 9;
        c.gridy = 1;
        c.gridwidth=3;
        c.gridheight=3;
        controlPanel.add(movePanel, c);


        c.fill = GridBagConstraints.HORIZONTAL;
        c.ipadx = 10;
        c.insets = new Insets(5,5,5,5);  //padding
        c.gridheight=1;


        c.fill = GridBagConstraints.HORIZONTAL;
        c.anchor=GridBagConstraints.CENTER;

        c.gridx = 9;
        c.gridy = 0;
        c.gridwidth=1;
        controlPanel.add(crystalSizeLabel, c);

        c.gridx = 10;
        c.gridy = 0;
        c.gridwidth=1;
        controlPanel.add(crystalSizeSpinner, c);

        c.gridx = 11;
        c.gridy = 0;
        c.gridwidth=1;
        controlPanel.add(crystalSizeUnitLabel, c);

        c.gridx = 3;
        c.gridy = 0;
        c.gridwidth=1;
        controlPanel.add(temperatureLabel, c);

        c.gridx = 4;
        c.gridy = 0;
        c.gridwidth=1;
        controlPanel.add(temperatureSpinner, c);

        c.gridx = 5;
        c.gridy = 0;
        c.gridwidth=1;
        controlPanel.add(temperatureUnitLabel, c);

        c.gridx = 6;
        c.gridy = 0;
        c.gridwidth=1;
        controlPanel.add(recombinationTimeLabel, c);

        c.gridx = 7;
        c.gridy = 0;
        c.gridwidth=1;
        controlPanel.add(recombinationTimeSpinner, c);

        c.gridx = 0;
        c.gridy = 0;
        c.gridwidth=1;
        controlPanel.add(creationChanceLabel, c);

        c.gridx = 1;
        c.gridy = 0;
        c.gridwidth=1;
        controlPanel.add(creationChanceSpinner, c);

        c.gridx = 2;
        c.gridy = 0;
        c.gridwidth=1;
        controlPanel.add(creationChanceUnitLabel, c);

        c.gridx = 0;
        c.gridy = 1;
        c.gridwidth=1;
        controlPanel.add(exFieldStrengthLabel, c);

        c.gridx = 1;
        c.gridy = 1;
        c.gridwidth=1;
        controlPanel.add(exFieldStrengthSpinner, c);

        c.gridx = 2;
        c.gridy = 1;
        c.gridwidth=1;
        controlPanel.add(exFieldStrengthUnitLabel, c);

        c.gridx = 3;
        c.gridy = 1;
        c.gridwidth=1;
        controlPanel.add(electronMobilityLabel, c);

        c.gridx = 4;
        c.gridy = 1;
        c.gridwidth=1;
        controlPanel.add(electronMobilitySpinner, c);

        c.gridx = 5;
        c.gridy = 1;
        c.gridwidth=1;
        //controlPanel.add(electronMobilityUnitLabel, c);

        c.gridx = 6;
        c.gridy = 1;
        c.gridwidth=1;
        controlPanel.add(holeMobilityLabel, c);

        c.gridx = 7;
        c.gridy = 1;
        c.gridwidth=1;
        controlPanel.add(holeMobilitySpinner, c);

        c.gridx = 8;
        c.gridy = 1;
        c.gridwidth=1;
        //controlPanel.add(electronMobilityUnitLabel, c);

        c.gridx = 0;
        c.gridy = 2;
        c.gridwidth=6;
        controlPanel.add(showStatsCheckBox, c);

        c.gridx = 6;
        c.gridy = 2;
        c.gridwidth=1;
        controlPanel.add(forwardButton, c);

        c.gridx = 7;
        c.gridy = 2;
        c.gridwidth=1;
        controlPanel.add(reverseButton, c);

        c.gridx = 0;
        c.gridy = 3;
        c.gridwidth=2;
        controlPanel.add(arrangeNewButton, c);
        c.gridx = 2;
        c.gridy = 3;
        c.gridwidth=2;
        controlPanel.add(restartButton, c);
        c.gridx = 4;
        c.gridy = 3;
        c.gridwidth=2;
        controlPanel.add(startButton, c);

        c.gridx = 6;
        c.gridy = 3;
        c.gridwidth=3;
        c.fill = GridBagConstraints.NONE;
        c.anchor=GridBagConstraints.LINE_END;
        controlPanel.add(sourceLabel, c);


        //setup panels
        scenePanel.add("Center", painter);

        mainPane.add(controlPanel);
        mainPane.add(scenePanel);
        getContentPane().add(mainPane);
        getRootPane().setDefaultButton(startButton);


        //initialize crystal

        initCrystal();

    }

    public JFormattedTextField getTextField(JSpinner spinner) {
        JComponent editor = spinner.getEditor();
        if (editor instanceof JSpinner.DefaultEditor) {
            return ((JSpinner.DefaultEditor)editor).getTextField();
        } else {
            System.err.println("Unexpected editor type: "  + spinner.getEditor().getClass() + " isn't a descendant of DefaultEditor");
            return null;
        }
    }

    public void initValues(){
      painter.crystalSize = ((Integer)crystalSizeSpinner.getValue()).doubleValue() * painter.sceneWidth / (1920 / 2) * 2.2;
      painter.recombinationTime = ((Integer)recombinationTimeSpinner.getValue()).intValue();
      painter.creationChance = (int)((Integer)creationChanceSpinner.getValue()).doubleValue();
      painter.holeMobility = 21 - (int)((Integer)holeMobilitySpinner.getValue()).doubleValue();
      painter.exFieldStrength = (int)((Integer)exFieldStrengthSpinner.getValue()).intValue();
      painter.exFieldStrength = (int)(painter.exFieldStrength / 10) * 10;
      exFieldStrengthSpinner.setValue(painter.exFieldStrength);
      painter.electronMobility = 21 - (int)((Integer)electronMobilitySpinner.getValue()).intValue();
      painter.crystalTemperature = (int)((Integer)temperatureSpinner.getValue()).doubleValue();
      painter.showStats=showStatsCheckBox.isSelected();
    }

    public void initCrystal(){
      double RowX, RowY, Spacer;
      int i, i2, i3, i4, i5, j;

      initValues();

      Spacer=(50.0);

        i2=0;
        i3=0;
        i4=0;
        i5=0;
        painter.electronNumber=0;
        painter.holeNumber=0;
        RowX = 0;
        RowY = 3 * Math.cos(60.0 * 3.1415927 / 180.0) * Spacer + 2 * Spacer;
        for(i=0;i<8;i++){
          painter.impuritySumZones[i]=0;
        }
        for(i=0;i<12200;i++){
          j=i;
          while(j>200)j-=200;
          if(j>99){
            painter.AtomIsUp[i] = 1;
          }
          else{
            painter.AtomIsUp[i]= 0;
          }
          painter.AtomCoords[i][0]=RowX;
          painter.AtomCoords[i][1]=RowY;
          RowX+=Math.sin(60.0 * 3.1415927 / 180.0) * Spacer * 2;
          i3++;
          if(i3==100){
            i3=0;
            if(i2==0 | i2==1){
              RowX=Math.sin(60.0 * 3.1415927 / 180.0) * Spacer;
            }
            else{
              RowX=0;
            }
            if(i2==0 | i2==2){
              RowY += Math.cos(60.0 * 3.1415927 / 180.0) * Spacer;
            }
            else{
              RowY += Spacer;
            }
            i2++;
            if(i2==4)i2=0;
          }
          painter.directionState[i]=i2;
          painter.AtomType[i]=0; //Silicon atoms
          painter.AtomElectron[i]=0; //Normal number of electrons
          painter.AtomMoveNow[i]=0; //Time electron/hole needs to move
          painter.blockRecombination[i]=0; //Time electron/hole needs to move
          i4++;
          if(i4==120){
            i4=RandomGenerator.nextInt(10)-1;
            painter.chargePlaces[i5] = i;
            painter.chargePlacesNew[i5][0] = i;
            if(i<6100){
              painter.chargePlacesNew[i5][1] = 1;
              painter.AtomMoveNow[i] = RandomGenerator.nextInt(2);
            }
            else{
              painter.chargePlacesNew[i5][1] = 2;
              painter.AtomMoveNow[i] = RandomGenerator.nextInt(6);
            }
            painter.chargePlacesNew[i5][2] = 0;//Recombination not blocked
            painter.impurityPlaces[i5] = i;
            i5++;
            if(i<6100){
              painter.AtomType[i]=1; //Phosphorous atoms
              painter.AtomElectron[i]=1; //Additional electron
              painter.electronNumber++;
              if(i < 1600){
                painter.impuritySumZones[0]++;
              }
              if(i >= 1600 && i < 3100){
                painter.impuritySumZones[1]++;
              }
              if(i >= 3100 && i < 4600){
                painter.impuritySumZones[2]++;
              }
              if(i >= 4600 && i < 6100){
                painter.impuritySumZones[3]++;
              }
              if(i >= 6100 && i < 7600){
                painter.impuritySumZones[4]++;
              }
              if(i >= 7600 && i < 9100){
                painter.impuritySumZones[5]++;
              }
              if(i >= 9100 && i < 10600){
                painter.impuritySumZones[6]++;
              }
              if(i >= 10600){
                painter.impuritySumZones[7]++;
              }
            }
            else{
              painter.AtomType[i]=2; //Aluminum atoms
              painter.AtomElectron[i]=2; //Missing electron
              painter.holeNumber++;
              if(i < 1600){
                painter.impuritySumZones[0]--;
              }
              if(i >= 1600 && i < 3100){
                painter.impuritySumZones[1]--;
              }
              if(i >= 3100 && i < 4600){
                painter.impuritySumZones[2]--;
              }
              if(i >= 4600 && i < 6100){
                painter.impuritySumZones[3]--;
              }
              if(i >= 6100 && i < 7600){
                painter.impuritySumZones[4]--;
              }
              if(i >= 7600 && i < 9100){
                painter.impuritySumZones[5]--;
              }
              if(i >= 9100 && i < 10600){
                painter.impuritySumZones[6]--;
              }
              if(i >= 10600){
                painter.impuritySumZones[7]--;
              }
            }
          }
        }//for(i=0;i<12200;i++){
        for(i=0;i<8;i++){
          painter.chargeSumZones[i] = -painter.impuritySumZones[i];
        }
        painter.chargeNumber=i5;
        painter.impurityNumber=i5;

   }

    public void stateChanged(ChangeEvent e) {
      initValues();
      painter.refreshScene();
    }

    // Listens to the buttons.
    public void actionPerformed(ActionEvent e) {

        if("showStatsPressed".equals(e.getActionCommand())) {
          if(!painter.isRunning){
            painter.showStats=showStatsCheckBox.isSelected();
            painter.refreshScene();
          }
        }
        if("moveDownButtonPressed".equals(e.getActionCommand())) {
          painter.upperLeftCornerY+=25;
          if(!painter.isRunning){
            painter.refreshScene();
          }
        }
        if("moveUpButtonPressed".equals(e.getActionCommand())) {
          painter.upperLeftCornerY-=25;
          if(!painter.isRunning){
            painter.refreshScene();
          }
        }
        if("moveLeftButtonPressed".equals(e.getActionCommand())) {
          painter.upperLeftCornerX-=25;
          if(!painter.isRunning){
            painter.refreshScene();
          }
        }
        if("moveRightButtonPressed".equals(e.getActionCommand())) {
          painter.upperLeftCornerX+=25;
          if(!painter.isRunning){
            painter.refreshScene();
          }
        }
        if("forwardButtonPressed".equals(e.getActionCommand())) {
          painter.polarity=1;
          if(!painter.isRunning){
            painter.refreshScene();
          }
        }
        if("reverseButtonPressed".equals(e.getActionCommand())) {
          painter.polarity=-1;
          if(!painter.isRunning){
            painter.refreshScene();
          }
        }
        if("arrangeNewButtonPressed".equals(e.getActionCommand())) {
          initCrystal();
          painter.refreshScene();
        }
        if("restartButtonPressed".equals(e.getActionCommand())) {
          int i;
          Random RandomGenerator = new Random();
          painter.electronNumber=0;
          painter.holeNumber=0;
          for(i=0;i<12200;i++){
            painter.AtomElectron[i]=0; //Normal number of electrons
            painter.AtomMoveNow[i]=0; //Time electron/hole needs to move
            painter.blockRecombination[i]=0; //Time electron/hole needs to move
          }
          for(i=0;i<painter.impurityNumber;i++){
            painter.chargePlacesNew[i][0] = painter.impurityPlaces[i];
            painter.chargePlaces[i] =  painter.impurityPlaces[i];
            if (painter.AtomType[painter.impurityPlaces[i]]==1){
              painter.AtomElectron[painter.impurityPlaces[i]]=1;
              painter.electronNumber++;
              painter.chargePlacesNew[i][1] = 1;
              painter.AtomMoveNow[painter.impurityPlaces[i]] = RandomGenerator.nextInt(2);
            }
            if (painter.AtomType[painter.impurityPlaces[i]]==2){
              painter.AtomElectron[painter.impurityPlaces[i]]=2;
              painter.holeNumber++;
              painter.chargePlacesNew[i][1] = 2;
              painter.AtomMoveNow[painter.impurityPlaces[i]] = RandomGenerator.nextInt(6);
            }
          }
          painter.chargeNumber=painter.impurityNumber;
          painter.refreshScene();
        }
        if("startButtonPressed".equals(e.getActionCommand())) {

          if(painter.isRunning){
            painter.isRunning=false;
          }
          else{
              startButton.setText("Stop");
            if(timer.getInitialDelay()!=timerPause){
              timer.setInitialDelay(timerPause);
            }
            timer.start();
            painter.isRunning=true;
          }//if(!isRunning)
        }//if ("startButtonPressed".equals(e.getActionCommand())) {
    }

    private class timerEvent implements ActionListener{
      public void actionPerformed(ActionEvent e){

        if(!painter.isRunning){
          timer.stop();
            startButton.setText("Start");
        }

        initValues();

        painter.refreshScene();
      }
    }
}

class ScenePainter extends Component {
	private static final long serialVersionUID = 1L;
	int sceneHeight = 1080 / 2, sceneWidth = 1920 / 2;
    double[][] AtomCoords = new double[12200][2];
    int[] AtomIsUp = new int[12200];
    int[] AtomElectron = new int[12200];
    int[] AtomType = new int[12200];
    int[] AtomMoveNow = new int[12200];
    int[] blockRecombination = new int[12200];
    int chargeNumber = 0, impurityNumber = 0;
    int[] directionState = new int[12200];
    int[] chargePlaces = new int[12200];
    int[] impurityPlaces = new int[12200];
    int[][] chargePlacesNew = new int[12200][3];
    int[][] electronInjectUpperHalf = new int[100][2];
    int[][] electronInjectLowerHalf = new int[100][2];
    int[] chargeSumZones = new int[8];
    int[] impuritySumZones = new int[8];
    double crystalSize = 20.0, crystalSize2 = 21.0;
    int recombinationTime = 5;
    double crystalTemperature = 0.0;
    int creationChance = 0;
    int holeMobility = 15;
    int exFieldStrength = 0;
    int electronMobility = 10;
    int electronNumber = 0, holeNumber = 0;
    BufferedImage bufferImage = null, bufferImage2 = null;
    Graphics2D bufferImageSurface = null, bufferImageSurface2 = null;
    boolean isRunning=false;
    boolean showStats=true;
    BufferedImage atomSilicon = null, atomLightRedUp = null, atomLightRedDown = null, atomLightBlueUp = null, atomLightBlueDown = null, atomDarkBlueUp = null, atomDarkBlueDown = null;
    BufferedImage atomDarkRedUp = null, atomDarkRedDown = null, atomGreyUp = null, atomGreyDown = null, atomGoldUp = null, atomGoldDown = null, recombination = null;
    String picName = "drawing.jpg";
    int picNumber = 0;
    int savePics = 0;
    int vibration = 0;
    double vibrateX = 0.0, vibrateY = 0.0;
    int polarity = 1;
    int upperLeftCornerX = 30, upperLeftCornerY = 0, upperLeftCornerXold = 0, upperLeftCornerYold = 0, exFieldStrengthOld = 0, polarityOld = 1;

    public Dimension getPreferredSize(){
        return new Dimension(sceneWidth, sceneHeight);
    }

    void refreshScene() {
      repaint();
    }

    public void paint(Graphics g) {
        Graphics2D g2 = (Graphics2D) g;
        Composite origComposite;
        int i, i2, i4, i5, iNew, j;
        double TopLeftX = 0.0, TopLeftY = 0.0;
        double forceX = 0.0, forceY = 0.0, totalDistance = 0.0, totalForce = 0.0;
        double alpha = 0.0;
        int newElectron=0, newHole=0;
        int[] triangleX = new int[3];
        int[] triangleY = new int[3];

        //create buffer to avoid flickering while scene is calculated and painted
        if(bufferImage==null){
          bufferImage = (BufferedImage)createImage(sceneWidth, sceneHeight);
          bufferImageSurface = bufferImage.createGraphics();
          bufferImage2 = (BufferedImage)createImage(sceneWidth, sceneHeight);
          bufferImageSurface2 = bufferImage2.createGraphics();
          Font font = new Font("Arial", Font.PLAIN, 16);
          bufferImageSurface.setFont(font);
        }

        origComposite = g2.getComposite();

        Random RandomGenerator = new Random();

        if(crystalSize != crystalSize2 | upperLeftCornerXold != upperLeftCornerX | upperLeftCornerYold != upperLeftCornerY | exFieldStrength != exFieldStrengthOld | polarity != polarityOld | vibration == 1){
          bufferImageSurface2.setBackground(new Color(217, 217, 217));
          bufferImageSurface2.clearRect(0, 0, sceneWidth, sceneHeight);
          if (exFieldStrength==0){
            bufferImageSurface2.setColor(new Color(200, 200, 200));
          }
          else{
            if(polarity==1){
              bufferImageSurface2.setColor(new Color(150, 150, 150));
            }
            else{
              bufferImageSurface2.setColor(new Color(255, 150, 150));
            }
          }
          bufferImageSurface2.fillRect( (int)(upperLeftCornerX * crystalSize / 100.0),  (int)(upperLeftCornerY * crystalSize / 100.0), (int)(8660 * crystalSize / 100.0), (int)(230 * crystalSize / 100.0));
          if (exFieldStrength==0){
            bufferImageSurface2.setColor(new Color(200, 200, 200));
          }
          else{
            if(polarity==1){
              bufferImageSurface2.setColor(new Color(255, 150, 150));
            }
            else{
              bufferImageSurface2.setColor(new Color(150, 150, 150));
            }
          }
          bufferImageSurface2.fillRect( (int)(upperLeftCornerX * crystalSize / 100.0),  (int)((4750 + upperLeftCornerY) * crystalSize / 100.0), (int)(8660 * crystalSize / 100.0), (int)(230 * crystalSize / 100.0));
          for(i=0;i<12200;i++){
              if(vibration==1){
                vibrateX = (RandomGenerator.nextInt(100) - 50) / 20.0;
                vibrateY = (RandomGenerator.nextInt(100) - 50) / 20.0;
              }
              bufferImageSurface2.drawImage(atomSilicon,
                                          (int)((AtomCoords[i][0] + upperLeftCornerX + vibrateX) * crystalSize / 100.0),
                                          (int)((AtomCoords[i][1] + upperLeftCornerY + vibrateY) * crystalSize / 100.0),
                                          (int) (0.6 * crystalSize),
                                          (int) (0.6 * crystalSize),
                                          this);
              if(vibration==1){
                if (i==605 && picNumber > 1 && picNumber < 50){
                  bufferImageSurface2.drawImage(atomLightRedUp,
                                              (int)((AtomCoords[i][0] + upperLeftCornerX + vibrateX) * crystalSize / 100.0),
                                              (int)((AtomCoords[i][1] + upperLeftCornerY + vibrateY) * crystalSize / 100.0),
                                              (int) (0.6 * crystalSize),
                                              (int) (0.6 * crystalSize),
                                              this);
                }
                if (i==605 && picNumber > 500 && picNumber < 600){
                  bufferImageSurface2.drawImage(atomLightRedUp,
                                              (int)((AtomCoords[i][0] + upperLeftCornerX + vibrateX) * crystalSize / 100.0),
                                              (int)((AtomCoords[i][1] + upperLeftCornerY + vibrateY) * crystalSize / 100.0),
                                              (int) (0.6 * crystalSize),
                                              (int) (0.6 * crystalSize),
                                              this);
                }
                if (i==705 && picNumber > 599 && picNumber < 700){
                  bufferImageSurface2.drawImage(atomLightRedUp,
                                              (int)((AtomCoords[i][0] + upperLeftCornerX + vibrateX) * crystalSize / 100.0),
                                              (int)((AtomCoords[i][1] + upperLeftCornerY + vibrateY) * crystalSize / 100.0),
                                              (int) (0.6 * crystalSize),
                                              (int) (0.6 * crystalSize),
                                              this);
                }
                if (i==706 && picNumber > 500 && picNumber < 550){
                  bufferImageSurface2.drawImage(atomLightBlueUp,
                                              (int)((AtomCoords[i][0] + upperLeftCornerX + vibrateX) * crystalSize / 100.0),
                                              (int)((AtomCoords[i][1] + upperLeftCornerY + vibrateY) * crystalSize / 100.0),
                                              (int) (0.6 * crystalSize),
                                              (int) (0.6 * crystalSize),
                                              this);
                }
                if (i==606 && picNumber > 549 && picNumber < 570){
                  bufferImageSurface2.drawImage(atomLightBlueUp,
                                              (int)((AtomCoords[i][0] + upperLeftCornerX + vibrateX) * crystalSize / 100.0),
                                              (int)((AtomCoords[i][1] + upperLeftCornerY + vibrateY) * crystalSize / 100.0),
                                              (int) (0.6 * crystalSize),
                                              (int) (0.6 * crystalSize),
                                              this);
                }
                if (i==707 && picNumber > 569 && picNumber < 620){
                  bufferImageSurface2.drawImage(atomLightBlueUp,
                                              (int)((AtomCoords[i][0] + upperLeftCornerX + vibrateX) * crystalSize / 100.0),
                                              (int)((AtomCoords[i][1] + upperLeftCornerY + vibrateY) * crystalSize / 100.0),
                                              (int) (0.6 * crystalSize),
                                              (int) (0.6 * crystalSize),
                                              this);
                }
                if (i==807 && picNumber > 619 && picNumber < 700){
                  bufferImageSurface2.drawImage(atomLightBlueUp,
                                              (int)((AtomCoords[i][0] + upperLeftCornerX + vibrateX) * crystalSize / 100.0),
                                              (int)((AtomCoords[i][1] + upperLeftCornerY + vibrateY) * crystalSize / 100.0),
                                              (int) (0.6 * crystalSize),
                                              (int) (0.6 * crystalSize),
                                              this);
                }
              }
              j=i;
              while(j>200)j-=200;
              if(j==99){
                i+=100;
              }
          }
          for(i=100;i<12200;i++){
              if(vibration==1){
                vibrateX = (RandomGenerator.nextInt(100) - 50) / 20.0;
                vibrateY = (RandomGenerator.nextInt(100) - 50) / 20.0;
              }
              bufferImageSurface2.drawImage(atomSilicon,
                                          (int)((AtomCoords[i][0] + upperLeftCornerX + vibrateX) * crystalSize / 100.0),
                                          (int)((AtomCoords[i][1] + upperLeftCornerY + vibrateY) * crystalSize / 100.0),
                                          (int) (0.6 * crystalSize),
                                          (int) (0.6 * crystalSize),
                                          this);
              if(vibration==1){
                if (i==605 && picNumber > 1 && picNumber < 50){
                  bufferImageSurface2.drawImage(atomLightRedUp,
                                              (int)((AtomCoords[i][0] + upperLeftCornerX + vibrateX) * crystalSize / 100.0),
                                              (int)((AtomCoords[i][1] + upperLeftCornerY + vibrateY) * crystalSize / 100.0),
                                              (int) (0.6 * crystalSize),
                                              (int) (0.6 * crystalSize),
                                              this);
                }
                if (i==605 && picNumber > 500 && picNumber < 600){
                  bufferImageSurface2.drawImage(atomLightRedUp,
                                              (int)((AtomCoords[i][0] + upperLeftCornerX + vibrateX) * crystalSize / 100.0),
                                              (int)((AtomCoords[i][1] + upperLeftCornerY + vibrateY) * crystalSize / 100.0),
                                              (int) (0.6 * crystalSize),
                                              (int) (0.6 * crystalSize),
                                              this);
                }
                if (i==705 && picNumber > 599 && picNumber < 700){
                  bufferImageSurface2.drawImage(atomLightRedUp,
                                              (int)((AtomCoords[i][0] + upperLeftCornerX + vibrateX) * crystalSize / 100.0),
                                              (int)((AtomCoords[i][1] + upperLeftCornerY + vibrateY) * crystalSize / 100.0),
                                              (int) (0.6 * crystalSize),
                                              (int) (0.6 * crystalSize),
                                              this);
                }
                if (i==706 && picNumber > 500 && picNumber < 550){
                  bufferImageSurface2.drawImage(atomLightBlueUp,
                                              (int)((AtomCoords[i][0] + upperLeftCornerX + vibrateX) * crystalSize / 100.0),
                                              (int)((AtomCoords[i][1] + upperLeftCornerY + vibrateY) * crystalSize / 100.0),
                                              (int) (0.6 * crystalSize),
                                              (int) (0.6 * crystalSize),
                                              this);
                }
                if (i==606 && picNumber > 549 && picNumber < 570){
                  bufferImageSurface2.drawImage(atomLightBlueUp,
                                              (int)((AtomCoords[i][0] + upperLeftCornerX + vibrateX) * crystalSize / 100.0),
                                              (int)((AtomCoords[i][1] + upperLeftCornerY + vibrateY) * crystalSize / 100.0),
                                              (int) (0.6 * crystalSize),
                                              (int) (0.6 * crystalSize),
                                              this);
                }
                if (i==707 && picNumber > 569 && picNumber < 620){
                  bufferImageSurface2.drawImage(atomLightBlueUp,
                                              (int)((AtomCoords[i][0] + upperLeftCornerX + vibrateX) * crystalSize / 100.0),
                                              (int)((AtomCoords[i][1] + upperLeftCornerY + vibrateY) * crystalSize / 100.0),
                                              (int) (0.6 * crystalSize),
                                              (int) (0.6 * crystalSize),
                                              this);
                }
                if (i==807 && picNumber > 619 && picNumber < 700){
                  bufferImageSurface2.drawImage(atomLightBlueUp,
                                              (int)((AtomCoords[i][0] + upperLeftCornerX + vibrateX) * crystalSize / 100.0),
                                              (int)((AtomCoords[i][1] + upperLeftCornerY + vibrateY) * crystalSize / 100.0),
                                              (int) (0.6 * crystalSize),
                                              (int) (0.6 * crystalSize),
                                              this);
                }
              }
              j=i;
              while(j>200)j-=200;
              if(j==199){
                i+=100;
              }
          }
          upperLeftCornerXold = upperLeftCornerX;
          upperLeftCornerYold = upperLeftCornerY;
          exFieldStrengthOld = exFieldStrength;
          polarityOld = polarity;
        }

        bufferImageSurface.drawImage(bufferImage2, 0, 0, sceneWidth, sceneHeight, this);

        //painting atoms
        if(vibration == 0){
          for(i=0;i<12200;i++){
            if((AtomCoords[i][0] + upperLeftCornerX) * crystalSize / 100.0 >= TopLeftX &&
              (AtomCoords[i][0] + upperLeftCornerX) * crystalSize / 100.0 <= sceneWidth &&
              (AtomCoords[i][1] + upperLeftCornerY) * crystalSize / 100.0 >= TopLeftY &&
              (AtomCoords[i][1] + upperLeftCornerY) * crystalSize / 100.0 <= sceneHeight){

              if(AtomType[i]==1.0){
                if(AtomIsUp[i]==1){
                  bufferImageSurface.drawImage(atomDarkRedUp,
                                              (int)((AtomCoords[i][0] + upperLeftCornerX) * crystalSize / 100.0),
                                              (int)((AtomCoords[i][1] + upperLeftCornerY) * crystalSize / 100.0),
                                              (int) (0.6 * crystalSize),
                                              (int) (0.6 * crystalSize),
                                              this);
                }
                else{
                  bufferImageSurface.drawImage(atomDarkRedDown,
                                              (int)((AtomCoords[i][0] + upperLeftCornerX) * crystalSize / 100.0),
                                              (int)((AtomCoords[i][1] + upperLeftCornerY) * crystalSize / 100.0),
                                              (int) (0.6 * crystalSize),
                                              (int) (0.6 * crystalSize),
                                              this);
                }
              }
              if(AtomType[i]==2.0){
                if(AtomIsUp[i]==1){
                  bufferImageSurface.drawImage(atomDarkBlueUp,
                                              (int)((AtomCoords[i][0] + upperLeftCornerX) * crystalSize / 100.0),
                                              (int)((AtomCoords[i][1] + upperLeftCornerY) * crystalSize / 100.0),
                                              (int) (0.6 * crystalSize),
                                              (int) (0.6 * crystalSize),
                                              this);
                }
                else{
                  bufferImageSurface.drawImage(atomDarkBlueDown,
                                              (int)((AtomCoords[i][0] + upperLeftCornerX) * crystalSize / 100.0),
                                              (int)((AtomCoords[i][1] + upperLeftCornerY) * crystalSize / 100.0),
                                              (int) (0.6 * crystalSize),
                                              (int) (0.6 * crystalSize),
                                              this);
                }
              }
              if(AtomElectron[i]==2.0){
                if(AtomType[i]==2.0){//Positive aluminum atom
                  if(AtomIsUp[i]==1){
                    bufferImageSurface.drawImage(atomGreyUp,
                                                (int)((AtomCoords[i][0] + upperLeftCornerX) * crystalSize / 100.0),
                                                (int)((AtomCoords[i][1] + upperLeftCornerY) * crystalSize / 100.0),
                                                (int) (0.6 * crystalSize),
                                                (int) (0.6 * crystalSize),
                                                this);
                  }
                  else{
                    bufferImageSurface.drawImage(atomGreyDown,
                                                (int)((AtomCoords[i][0] + upperLeftCornerX) * crystalSize / 100.0),
                                                (int)((AtomCoords[i][1] + upperLeftCornerY) * crystalSize / 100.0),
                                                (int) (0.6 * crystalSize),
                                                (int) (0.6 * crystalSize),
                                                this);
                  }
                }
                else{
                  if(AtomIsUp[i]==1){
                    bufferImageSurface.drawImage(atomLightRedUp,
                                                (int)((AtomCoords[i][0] + upperLeftCornerX) * crystalSize / 100.0),
                                                (int)((AtomCoords[i][1] + upperLeftCornerY) * crystalSize / 100.0),
                                                (int) (0.6 * crystalSize),
                                                (int) (0.6 * crystalSize),
                                                this);
                  }
                  else{
                    bufferImageSurface.drawImage(atomLightRedDown,
                                                (int)((AtomCoords[i][0] + upperLeftCornerX) * crystalSize / 100.0),
                                                (int)((AtomCoords[i][1] + upperLeftCornerY) * crystalSize / 100.0),
                                                (int) (0.6 * crystalSize),
                                                (int) (0.6 * crystalSize),
                                                this);
                  }
                }
              }
              if(AtomElectron[i]==1.0){
                if(AtomType[i]==1.0){//Negative phosphorus atom
                  if(AtomIsUp[i]==1){
                    bufferImageSurface.drawImage(atomGoldUp,
                                                (int)((AtomCoords[i][0] + upperLeftCornerX) * crystalSize / 100.0),
                                                (int)((AtomCoords[i][1] + upperLeftCornerY) * crystalSize / 100.0),
                                                (int) (0.6 * crystalSize),
                                                (int) (0.6 * crystalSize),
                                                this);
                  }
                  else{
                    bufferImageSurface.drawImage(atomGoldDown,
                                                (int)((AtomCoords[i][0] + upperLeftCornerX) * crystalSize / 100.0),
                                                (int)((AtomCoords[i][1] + upperLeftCornerY) * crystalSize / 100.0),
                                                (int) (0.6 * crystalSize),
                                                (int) (0.6 * crystalSize),
                                                this);
                  }
                }
                else{
                  if(AtomIsUp[i]==1){
                    bufferImageSurface.drawImage(atomLightBlueUp,
                                                (int)((AtomCoords[i][0] + upperLeftCornerX) * crystalSize / 100.0),
                                                (int)((AtomCoords[i][1] + upperLeftCornerY) * crystalSize / 100.0),
                                                (int) (0.6 * crystalSize),
                                                (int) (0.6 * crystalSize),
                                                this);
                  }
                  else{
                    bufferImageSurface.drawImage(atomLightBlueDown,
                                                (int)((AtomCoords[i][0] + upperLeftCornerX) * crystalSize / 100.0),
                                                (int)((AtomCoords[i][1] + upperLeftCornerY) * crystalSize / 100.0),
                                                (int) (0.6 * crystalSize),
                                                (int) (0.6 * crystalSize),
                                                this);
                  }
                }
              }
              if(AtomMoveNow[i]<0){
                  bufferImageSurface.drawImage(recombination,
                                              (int)((AtomCoords[i][0] + upperLeftCornerX) * crystalSize / 100.0 - 0.6 * crystalSize * 0.75),
                                              (int)((AtomCoords[i][1] + upperLeftCornerY) * crystalSize / 100.0 - 0.6 * crystalSize * 0.75),
                                              (int) (0.6 * crystalSize * 3.0),
                                              (int) (0.6 * crystalSize * 3.0),
                                              this);
              }
            }//if atom is visible

            if(AtomMoveNow[i]>0){
              AtomMoveNow[i]--;
            }
            if(AtomMoveNow[i]<0 && recombinationTime < 100){
              AtomMoveNow[i]++;
            }

            if(i<100){//draw triangles to show flow of electrons
              if(electronInjectUpperHalf[i][0]>0){
                bufferImageSurface.setColor(new Color(100, 100, 217));
                if(electronInjectUpperHalf[i][1]==1){
                  triangleX[0]=(int) ((i + 0.5) * Math.sin(60.0 * 3.1415927 / 180.0) * 50.0 * 2  * crystalSize / 100.0) - 5;
                  triangleX[1]= triangleX[0] + 10;
                  triangleX[2]= triangleX[0] + 20;
                  triangleY[0]=(int)(upperLeftCornerY * crystalSize / 100.0);
                  triangleY[1]=(int) (200.0 * crystalSize / 100.0 + upperLeftCornerY * crystalSize / 100.0);
                  triangleY[2]=(int)(upperLeftCornerY * crystalSize / 100.0);
                }
                else{
                  triangleX[0]=(int) ((i + 0.5) * Math.sin(60.0 * 3.1415927 / 180.0) * 50.0 * 2  * crystalSize / 100.0) - 5;
                  triangleX[1]= triangleX[0] + 10;
                  triangleX[2]= triangleX[0] + 20;
                  triangleY[0]=(int) (200.0 * crystalSize / 100.0 + upperLeftCornerY * crystalSize / 100.0);
                  triangleY[1]=(int)(upperLeftCornerY * crystalSize / 100.0);
                  triangleY[2]=(int) (200.0 * crystalSize / 100.0 + upperLeftCornerY * crystalSize / 100.0);
                }
                bufferImageSurface.fillPolygon( triangleX, triangleY, 3);
                electronInjectUpperHalf[i][0]--;
              }
              if(electronInjectLowerHalf[i][0]>0){
                bufferImageSurface.setColor(new Color(100, 100, 217));
                if(electronInjectLowerHalf[i][1]==1){
                  triangleX[0]=(int) ((i + 0.5) * Math.sin(60.0 * 3.1415927 / 180.0) * 50.0 * 2  * crystalSize / 100.0) - 5;
                  triangleX[1]= triangleX[0] + 10;
                  triangleX[2]= triangleX[0] + 20;
                  triangleY[0]=(int) ((4750.0 + upperLeftCornerY) * crystalSize / 100.0);
                  triangleY[1]=(int) ((4750.0 + 200.0 + upperLeftCornerY) * crystalSize / 100.0);
                  triangleY[2]=(int) ((4750.0 + upperLeftCornerY) * crystalSize / 100.0);
                }
                else{
                  triangleX[0]=(int) ((i + 0.5) * Math.sin(60.0 * 3.1415927 / 180.0) * 50.0 * 2  * crystalSize / 100.0) - 5;
                  triangleX[1]= triangleX[0] + 10;
                  triangleX[2]= triangleX[0] + 20;
                  triangleY[0]=(int) ((4750.0 + 200.0 + upperLeftCornerY) * crystalSize / 100.0);
                  triangleY[1]=(int) ((4750.0 + upperLeftCornerY) * crystalSize / 100.0);
                  triangleY[2]=(int) ((4750.0 + 200.0 + upperLeftCornerY) * crystalSize / 100.0);
                }
                bufferImageSurface.fillPolygon( triangleX, triangleY, 3);
                electronInjectLowerHalf[i][0]--;
              }
            }
          }//if(vibration==0)
        }//for(i=0;i<12200;i++){


        if(showStats){
          bufferImageSurface.setColor(new Color(0, 150, 0));
          bufferImageSurface.drawString("Charge = " + new DecimalFormat("#").format(impuritySumZones[0] + chargeSumZones[0]), 30, (int)((550.0 + upperLeftCornerY) * crystalSize / 100.0));
          bufferImageSurface.drawString("Charge = " + new DecimalFormat("#").format(impuritySumZones[1] + chargeSumZones[1]), 30, (int)((1112.5 + upperLeftCornerY) * crystalSize / 100.0));
          bufferImageSurface.drawString("Charge = " + new DecimalFormat("#").format(impuritySumZones[2] + chargeSumZones[2]), 30, (int)((1675.5 + upperLeftCornerY) * crystalSize / 100.0));
          bufferImageSurface.drawString("Charge = " + new DecimalFormat("#").format(impuritySumZones[3] + chargeSumZones[3]), 30, (int)((2237.5 + upperLeftCornerY) * crystalSize / 100.0));
          bufferImageSurface.drawString("Charge = " + new DecimalFormat("#").format(impuritySumZones[4] + chargeSumZones[4]), 30, (int)((2800.0 + upperLeftCornerY) * crystalSize / 100.0));
          bufferImageSurface.drawString("Charge = " + new DecimalFormat("#").format(impuritySumZones[5] + chargeSumZones[5]), 30, (int)((3362.5 + upperLeftCornerY) * crystalSize / 100.0));
          bufferImageSurface.drawString("Charge = " + new DecimalFormat("#").format(impuritySumZones[6] + chargeSumZones[6]), 30, (int)((3925.0 + upperLeftCornerY) * crystalSize / 100.0));
          bufferImageSurface.drawString("Charge = " + new DecimalFormat("#").format(impuritySumZones[7] + chargeSumZones[7]), 30, (int)((4425.0 + upperLeftCornerY) * crystalSize / 100.0));

          bufferImageSurface.drawLine(0, (int)((750.0 + upperLeftCornerY) * crystalSize / 100.0 + 0.3 * crystalSize), sceneWidth, (int)((750.0 + upperLeftCornerY ) * crystalSize / 100.0 + 0.3 * crystalSize));
          bufferImageSurface.drawLine(0, (int)((1312.5 + upperLeftCornerY) * crystalSize / 100.0 + 0.3 * crystalSize), sceneWidth, (int)((1312.5 + upperLeftCornerY) * crystalSize / 100.0 + 0.3 * crystalSize));
          bufferImageSurface.drawLine(0, (int)((1875.5 + upperLeftCornerY) * crystalSize / 100.0 + 0.3 * crystalSize), sceneWidth, (int)((1875.5 + upperLeftCornerY) * crystalSize / 100.0 + 0.3 * crystalSize));
          bufferImageSurface.drawLine(0, (int)((2437.5 + upperLeftCornerY) * crystalSize / 100.0 + 0.3 * crystalSize), sceneWidth, (int)((2437.5 + upperLeftCornerY) * crystalSize / 100.0 + 0.3 * crystalSize));
          bufferImageSurface.drawLine(0, (int)((3000.0 + upperLeftCornerY) * crystalSize / 100.0 + 0.3 * crystalSize), sceneWidth, (int)((3000.0 + upperLeftCornerY) * crystalSize / 100.0 + 0.3 * crystalSize));
          bufferImageSurface.drawLine(0, (int)((3562.5 + upperLeftCornerY) * crystalSize / 100.0 + 0.3 * crystalSize), sceneWidth, (int)((3562.5 + upperLeftCornerY) * crystalSize / 100.0 + 0.3 * crystalSize));
          bufferImageSurface.drawLine(0, (int)((4125.0 + upperLeftCornerY) * crystalSize / 100.0 + 0.3 * crystalSize), sceneWidth, (int)((4125.0 + upperLeftCornerY) * crystalSize / 100.0 + 0.3 * crystalSize));
        }

        if(isRunning){
          for(i5=0;i5<chargeNumber;i5++){
            i=chargePlaces[i5];
            if(AtomElectron[i] != 0 && AtomMoveNow[i]==0){
              forceX = 0.0;
              forceY = 0.0;
              if(blockRecombination[i]==0){
                for (i4 = 0; i4 < impurityNumber; i4++){
                  j=impurityPlaces[i4];
                  if(j != i){
                    totalDistance = Math.sqrt(Math.pow(AtomCoords[i][0]-AtomCoords[j][0], 2.0) + Math.pow(AtomCoords[i][1]-AtomCoords[j][1], 2.0));
                    totalForce = 1.0 / Math.pow(totalDistance, 2.0);
                    if((AtomType[j] == 2.0 && AtomElectron[i] == 1.0) |
                      (AtomType[j] == 1.0 && AtomElectron[i] == 2.0)){//Repelling force
                      forceX += totalForce * (AtomCoords[i][0]-AtomCoords[j][0]) / totalDistance;
                      forceY += totalForce * (AtomCoords[j][1]-AtomCoords[i][1]) / totalDistance;
                    }
                    if( (AtomElectron[j] == 2.0 && AtomElectron[i] == 1.0) |
                        (AtomType[j] == AtomElectron[i])){////Attracting force
                      if(AtomType[j] == AtomElectron[i]){////Attracting force
                        if (totalDistance < 800.0){//Electrons/holes are just weakly attracted by nearby impurity atoms
                          totalForce *= 0.05;
                        }
                      }
                      forceX -= totalForce * (AtomCoords[i][0]-AtomCoords[j][0]) / totalDistance;
                      forceY -= totalForce * (AtomCoords[j][1]-AtomCoords[i][1]) / totalDistance;
                    }
                  }
                }
                for (i4 = 0; i4 < chargeNumber; i4++){
                  j=chargePlaces[i4];
                  if(j != i){
                    totalDistance = Math.sqrt(Math.pow(AtomCoords[i][0]-AtomCoords[j][0], 2.0) + Math.pow(AtomCoords[i][1]-AtomCoords[j][1], 2.0));
                    totalForce = 1.0 / Math.pow(totalDistance, 2.0);
                    if(totalDistance < 51.0 && ((AtomElectron[i] == 2 && AtomElectron[j]==1) | (AtomElectron[i] == 1 && AtomElectron[j]==2))
                       && blockRecombination[i] == 0 && blockRecombination[j] == 0
                       && AtomMoveNow[i] == 0 && AtomMoveNow[j] == 0){//Recombination of electron and hole
                      AtomMoveNow[i]=-1 * recombinationTime;
                      AtomMoveNow[j]=-1 * recombinationTime;
                      break;
                    }
                    else{
                      if(AtomElectron[j] == AtomElectron[i]){//Repelling force
                        forceX = forceX + totalForce * (AtomCoords[i][0] - AtomCoords[j][0]) / totalDistance;
                        forceY = forceY + totalForce * (AtomCoords[j][1] - AtomCoords[i][1]) / totalDistance;
                      }
                      if((AtomElectron[j] == 1.0 && AtomElectron[i] == 2.0) |
                        (AtomElectron[j] == 2.0 && AtomElectron[i] == 1.0)){////Attracting force
                        forceX = forceX - totalForce * (AtomCoords[i][0] - AtomCoords[j][0]) / totalDistance;
                        forceY = forceY - totalForce * (AtomCoords[j][1] - AtomCoords[i][1]) / totalDistance;
                      }
                    }
                  }
                }//for (i4 = 0; i4 < chargeNumber; i4++){
              }//if(blockRecombination[i]==0){

//Add forces caused by thermal movement and external field

              forceX += (RandomGenerator.nextInt(100) - 50) * crystalTemperature * 10 / 250000000.0;//random movement caused by temperature
              forceY += (RandomGenerator.nextInt(100) - 50) * crystalTemperature * 10 / 250000000.0;//random movement caused by temperature

              if(AtomElectron[i]==1){
                forceY = forceY - polarity * exFieldStrength / 10000000.0;//random movement caused by external field
              }
              else{
                forceY = forceY + polarity * exFieldStrength / 10000000.0;//random movement caused by external field
              }

              if(AtomMoveNow[i] == 0){//Move electron/hole of the atom
                alpha = Math.atan2(forceY, forceX);
                i2=-1;

                if(directionState[i]==0){
                  if(alpha > 0.523598783333 && alpha < 2.61799391667){
                    i2=i-100;
                  }
                  else if(alpha >= 2.61799391667 | alpha < -1.57079635) {
                    i2=i+99;
                  }
                  else{
                    i2=i+100;
                  }
                }
                if(directionState[i]==1){
                  if(alpha < -0.523598783333 && alpha > -2.61799391667){
                    i2=i+100;
                  }
                  else if(alpha <= -2.61799391667 | alpha > 1.57079635) {
                    i2=i-100;
                  }
                  else{
                    i2=i-99;
                  }
                }
                if(directionState[i]==2){
                  if(alpha > 0.523598783333 && alpha < 2.61799391667){
                    i2=i-100;
                  }
                  else if(alpha >= 2.61799391667 | alpha < -1.57079635) {
                    i2=i+100;
                  }
                  else{
                    i2=i+101;
                  }
                }
                if(directionState[i]==3){
                  if(alpha < -0.523598783333 && alpha > -2.61799391667){
                    i2=i+100;
                  }
                  else if(alpha <= -2.61799391667 | alpha > 1.57079635) {
                    i2=i-101;
                  }
                  else{
                    i2=i-100;
                  }
                }
                if(i2<12200 && i2>-1){//Atom is not at the upper or lower edge of the crystal
                  if(Math.abs(AtomCoords[i2][0] - AtomCoords[i][0]) < 200.0){//Atom is not at the left or right edge of the crystal
                    iNew=-1;
                    for(i4=0;i4<chargeNumber;i4++){
                      if(chargePlacesNew[i4][0]==i2){
                        iNew=-1;
                        break;
                      }
                      if(chargePlaces[i4]==i){
                        iNew=i4;
                      }
                    }
                    if(iNew>-1){
                      if(AtomElectron[i]==1){
                        AtomMoveNow[i2] = RandomGenerator.nextInt(electronMobility);
                      }
                      else{
                        AtomMoveNow[i2] = RandomGenerator.nextInt(holeMobility);
                      }
                      chargePlacesNew[iNew][0]=i2;
                      chargePlacesNew[iNew][1]=AtomElectron[i];
                    }
                  }
                }
              }//if(AtomMoveNow[i] == 0){//No recombination process

            }//if(AtomElectron[i]!=0){
          }//for(i=0;i<12200;i++){

        if(RandomGenerator.nextInt(100)<creationChance){//create additional electron/hole pair
          newElectron=RandomGenerator.nextInt(12200 - 102);
          newHole=newElectron + 102;
          if(Math.abs(AtomCoords[newElectron][0] - AtomCoords[newHole][0])>200){
            newHole=newElectron + 98;
          }
          i2=0;
          for(i4=0;i4<chargeNumber;i4++){
            if(chargePlacesNew[i4][0] == newElectron | chargePlacesNew[i4][0] == newHole){
              i2=-1;
              break;
            }
          }
          if(i2==0){
            chargeNumber += 2;
            electronNumber++;
            holeNumber++;
            chargePlacesNew[chargeNumber-1][0] = newElectron;
            chargePlacesNew[chargeNumber-1][1] = 1;
            chargePlacesNew[chargeNumber-1][2] = 20;
            chargePlacesNew[chargeNumber-2][0] = newHole;
            chargePlacesNew[chargeNumber-2][1] = 2;
            chargePlacesNew[chargeNumber-2][2] = 20;
          }
        }

        //inject electrons/holes depending on the external field strength
        if(exFieldStrength > 0){
          if((polarity==1 && exFieldStrength > 0 && impuritySumZones[0] + chargeSumZones[0] > 0 && impuritySumZones[7] + chargeSumZones[7] < 0) |      //forward polarity
            (polarity==-1 && exFieldStrength > 0   && impuritySumZones[0] + chargeSumZones[0] > 1 && impuritySumZones[7] + chargeSumZones[7] < -1) ){  //reverse polarity at decreasing external field
            newElectron = RandomGenerator.nextInt(100);
            newHole = 12200 - RandomGenerator.nextInt(100) - 1;
            i2=0;
            for(i4=0;i4<chargeNumber;i4++){
              if(chargePlacesNew[i4][0] == newElectron | chargePlacesNew[i4][0] == newHole){
                i2=-1;
                break;
              }
            }
            if(i2==0){
              chargeNumber += 2;
              electronNumber++;
              chargePlacesNew[chargeNumber-1][0] = newElectron;
              chargePlacesNew[chargeNumber-1][1] = 1;
              chargePlacesNew[chargeNumber-1][2] = 0;
              electronInjectUpperHalf[newElectron][0] = 30;
              electronInjectUpperHalf[newElectron][1] = 1;
              holeNumber++;
              chargePlacesNew[chargeNumber-2][0] = newHole;
              chargePlacesNew[chargeNumber-2][1] = 2;
              chargePlacesNew[chargeNumber-2][2] = 0;
              electronInjectLowerHalf[99-(12200 - newHole - 1)][0] = 30;
              electronInjectLowerHalf[99-(12200 - newHole - 1)][1] = 1;
            }
          }//if(polarity==1  && exFieldStrength > chargeNumber){
          if(polarity==-1 && exFieldStrength > 0  && impuritySumZones[0] + chargeSumZones[0] < 0 && impuritySumZones[7] + chargeSumZones[7] > 0){//reverse polarity
            newElectron = -1;
            newHole = -1;
            for(i4=0;i4<chargeNumber;i4++){
              if(chargePlacesNew[i4][0]<100 && newElectron==-1 && AtomMoveNow[chargePlacesNew[i4][0]]==0 && AtomElectron[chargePlacesNew[i4][0]]==1 && blockRecombination[chargePlacesNew[i4][0]] == 0){
                newElectron=chargePlacesNew[i4][0];
              }
              if(chargePlacesNew[i4][0]>12200 - 100 && newHole==-1 && AtomMoveNow[chargePlacesNew[i4][0]]==0 &&  AtomElectron[chargePlacesNew[i4][0]]==2 && blockRecombination[chargePlacesNew[i4][0]] == 0){
                newHole=chargePlacesNew[i4][0];
              }
            }
            if(newHole!=-1 && newElectron!=-1){
              AtomMoveNow[newHole]=-1 * recombinationTime;
              AtomMoveNow[newElectron]=-1;
              electronInjectUpperHalf[newElectron][0] = 30;
              electronInjectUpperHalf[newElectron][1] = 0;
              electronInjectLowerHalf[99-(12200 - newHole - 1)][0] = 30;
              electronInjectLowerHalf[99-(12200 - newHole - 1)][1] = 0;
            }
          }
        }//if(exFieldStrength>0){

          for(i4=0;i4<chargeNumber;i4++){
            AtomElectron[chargePlaces[i4]]=0;
            blockRecombination[chargePlaces[i4]]=0;
          }
          for(i4=0;i4<chargeNumber;i4++){//Remove electrons/holes after recombination process
            if(AtomMoveNow[chargePlacesNew[i4][0]]==-1){
              AtomMoveNow[chargePlacesNew[i4][0]]=0;
              if(chargePlacesNew[i4][1]==1){
                electronNumber--;
              }
              else{
                holeNumber--;
              }
              chargePlacesNew[i4][0]=chargePlacesNew[chargeNumber-1][0];
              chargePlacesNew[i4][1]=chargePlacesNew[chargeNumber-1][1];
              chargePlacesNew[i4][2]=chargePlacesNew[chargeNumber-1][2];
              chargeNumber--;
              i4--;
            }
          }
          for(i=0;i<8;i++){
            chargeSumZones[i]=0;
          }
          for(i4=0;i4<chargeNumber;i4++){
            chargePlaces[i4]=chargePlacesNew[i4][0];

            if(chargePlacesNew[i4][1]==1){
              if(chargePlacesNew[i4][0] < 1600){
                chargeSumZones[0]--;
              }
              if(chargePlacesNew[i4][0] >= 1600 && chargePlacesNew[i4][0] < 3100){
                chargeSumZones[1]--;
              }
              if(chargePlacesNew[i4][0] >= 3100 && chargePlacesNew[i4][0] < 4600){
                chargeSumZones[2]--;
              }
              if(chargePlacesNew[i4][0] >= 4600 && chargePlacesNew[i4][0] < 6100){
                chargeSumZones[3]--;
              }
              if(chargePlacesNew[i4][0] >= 6100 && chargePlacesNew[i4][0] < 7600){
                chargeSumZones[4]--;
              }
              if(chargePlacesNew[i4][0] >= 7600 && chargePlacesNew[i4][0] < 9100){
                chargeSumZones[5]--;
              }
              if(chargePlacesNew[i4][0] >= 9100 && chargePlacesNew[i4][0] < 10600){
                chargeSumZones[6]--;
              }
              if(chargePlacesNew[i4][0] >= 10600){
                chargeSumZones[7]--;
              }
            }
            if(chargePlacesNew[i4][1]==2){
              if(chargePlacesNew[i4][0] < 1600){
                chargeSumZones[0]++;
              }
              if(chargePlacesNew[i4][0] >= 1600 && chargePlacesNew[i4][0] < 3100){
                chargeSumZones[1]++;
              }
              if(chargePlacesNew[i4][0] >= 3100 && chargePlacesNew[i4][0] < 4600){
                chargeSumZones[2]++;
              }
              if(chargePlacesNew[i4][0] >= 4600 && chargePlacesNew[i4][0] < 6100){
                chargeSumZones[3]++;
              }
              if(chargePlacesNew[i4][0] >= 6100 && chargePlacesNew[i4][0] < 7600){
                chargeSumZones[4]++;
              }
              if(chargePlacesNew[i4][0] >= 7600 && chargePlacesNew[i4][0] < 9100){
                chargeSumZones[5]++;
              }
              if(chargePlacesNew[i4][0] >= 9100 && chargePlacesNew[i4][0] < 10600){
                chargeSumZones[6]++;
              }
              if(chargePlacesNew[i4][0] >= 10600){
                chargeSumZones[7]++;
              }
            }

            AtomElectron[chargePlacesNew[i4][0]]=chargePlacesNew[i4][1];
            if(chargePlacesNew[i4][2]>0){
              chargePlacesNew[i4][2]--;
            }
            blockRecombination[chargePlacesNew[i4][0]]=chargePlacesNew[i4][2];
          }
        }//if(isRunning)



//bufferImageSurface.drawString("chargeNumber = " + new DecimalFormat("000").format(chargeNumber), 80, 30);

        bufferImageSurface.setColor(new Color(0,0,0));
        bufferImageSurface.drawRect(0, 0, sceneWidth-1, sceneHeight-1);

        //paint bufferImage and make it visible
        g2.drawImage(bufferImage, 0, 0, this);
        g2.setComposite(origComposite);


      if(savePics == 1){
        picNumber++;
        picName="picsequence/drawing-" + new DecimalFormat("0000").format(picNumber) + ".png";
        try{
          ImageIO.write(bufferImage, "PNG", new File(picName));
        }
        catch (IOException e1) {
          e1.printStackTrace();
        }
      }

      crystalSize2=crystalSize;

    }
}

