Skip to content

tedkmburu/E-FieldSim-v2

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

89 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation


Logo

E field Simulator

Documentation on how the simulation works so that you can add to it.
View Simulation »

Table of Contents
  1. About The Project
  2. Design
  3. Code
  4. Physics
  5. Contributing
  6. Contact
  7. Acknowledgements

About The Project

Product Name Screen Shot

Because electric fields cannot be touched or seen, simulations are often utilized to build students' understanding of them by providing them with a visual experience of electric fields and the motion of test charges through them. The objective of the simulation is to improve students’ qualitative understanding of how electric fields are impacted by the charges around them by creating a dynamic representation of the electric field lines, field vectors, equipotential lines and the voltage created by the charges on screen. After creating a charge configuration, students can observe the motion of test charges through the electric field. The simulation was built in JavaScript so it will run on most browsers on a computer or mobile device. The simulation is intended to be used by college students taking introductory physics courses. The core principles of the simulation will then be used to create a game. Although the Learning objectives of the game and the simulation will be almost identical, we hope that the game will motivate students to build their intuition on how electric fields work outside of class in their own time. The effectiveness of the simulation and game will be analyzed after students test both in an introductory physics class.

Learning Objectives

Students will be able to:

  • Predict the general shape and direction of electric field lines for a system of charges
  • Relate the shape of equipotential lines to the shape of electric field lines
  • Compare the curve of the electric field lines to the trajectory of a test charge

Languages and Libraries

The entire simulation runs inside 2 canvases in a webpage, so it is accessible from both mobile phones and computers. HTML and a little CSS are used to set up the page then p5.js creates the canvas with the game loop and everything else. Everything else is done in Javascript.

Design

The simulation is primarily designed to target the learning goals listed above. There are different mods that display different inforamtion about the configuration of charges shown on then screen.

Code

Object-oriented programming is used throughout the simulation. The Button, Charge, FieldLine, CheckBox, TestCharge, fieldVector, PointCharge and other classes can be found in own their self-titled files. Functions that primarily only use that one class can also be found in that classes self-titled file.

The p5.js library is inside the file titled p5.min.js. It should not be tampered with. The library creates the game loop and has useful Vector math functions.

There is a file called variables.js that has all global variables in it. They can technically be declared anywhere but this is a little more organized.

JavaScript has inbuild functions that can be done to arrays that are used throughout this game. Learn more about them here: Read more on that here. The 2 canvases are layered on top of eachother and the background gradient is always blured with css. This is the canvas that the voltage is displayed on. Everything else is in the foreground canvas. Anytime you want to call an inbuilt p5 function, you have to specify which canvas you will call it from because p5 is no longer in a global mode.

p5.js

Because the simulation uses 2 canvases, they each run on their own instance of p5.js. This is called Instance Mode https://p5js.org/examples/instance-mode-instantiation.html

When the page is first loaded, each canvas will look for its preload(), setup() and draw() functions. They are run in that order and are all indepentant from eachother.

The preload() function is used to handle asynchronous loading of external files in a blocking way. If a preload function is defined, setup() will wait until any load calls within have finished. This is where all of the images and fonts are moved to the user's RAM for later usage.

The setup() function is called once when the program starts. It's used to create the canvas tag which is not included in the HTML file wih the rest of the HTML tags. There can only be one setup() function for each program and it shouldn't be called again after its initial execution.

Called directly after setup(), the draw() function continuously executes the lines of code contained inside its block until the program is stopped. The number of times draw() executes in each second may be controlled with the frameRate() function.

All of these functions can be found a the top of the main.js file.

Buttons

Buttons are used in the side panel and the right click menu.

This is how a button is created:

new Button({
   position: canvas.createVector(100, 255),        //  x y position of the button
   width: 200,                                     // integer
   height: 200,                                    // integer
   text: "Single",                                 // String
   image: icons[1]                                 // optional - will replace the text with an image 
   onClick: function(){ createPreset('single') }   // function will run when button is clicked
   })

CheckBoxes

CheckBoxes are used in the side panel.

This is how a CheckBox is created:

new CheckBox({
   position: canvas.createVector(100, 255),        //  x y position of the CheckBox
   width: 200,                                     // integer
   height: 200,                                    // integer
   text: "Show Grid",                                 // String
   value: true,                                    // boolean
   onClick: function(){createGrid = this.value;}   // function will run when checkbox is clicked
   })

Point Charges

This is how a charge is created.

class Charge
 {
 constructor(x, y, charge)
 {
     this.x = x;
     this.y = y;
     this.position = createVector(x,y);
     this.charge = charge || 0;
     this.selected = true;
     this.dragging = false;
 }

 display()
 {
     let charge = this;
     
 
     push();
     strokeWeight(2);
     if (charge.selected)
     {
         stroke(255);
         charge.charge = slider.value();
     }
     else
     {
         noStroke();
     }

     if (charge.charge > 0){ fill(chargeColor.positive); }
     else if (charge.charge == 0){ fill(chargeColor.neutral); }
     else { fill(chargeColor.negative); }
     ellipse(charge.x, charge.y, chargeDiameter, chargeDiameter);

     textSize(16);
     if (charge.charge > 0){ fill(textColor.positive); }
     else if (charge.charge == 0){ fill(textColor.neutral); }
     else { fill(textColor.negative); }
     noStroke();
     if (charge.charge > 0)
     {
         text(`+${charge.charge}`, charge.x, charge.y + 7);
     }
     else
     {
         text(charge.charge, charge.x, charge.y + 7);
     }
     pop();
 }
 
 }

Collision Detection

Physics

Physics concepts related to

and superimposed electric fields given by

are used to run the game.

Test Charge Movement

There is a netForceAtPoint() function in game.js that is given an x-y position vector as an input and outputs a vector with x and y components. It does this using Coulomb's Law and trig.

function netForceAtPoint(position) // the position comes in in a createVector(x,y) format
{
 let finalVector = createVector(0,0);        // starts with a 0 vector
 
 charges.forEach(charge =>       // calculates force from each charge and adds them to the finalVector variable
 {
     let chargePosition = createVector(charge.x, charge.y);      //position of charge object

     //F = KQ / (r^2)                                    // k is a constant that is used to fine tune the size of the force vector
     let kq = charge.charge * k;                         // q is the magnitude and sign of the charges charge
     let r = p5.Vector.dist(position, chargePosition);   // gets the distance from the charge to the point in pixels
     if (r < 10)
     {                                                   // this prevents the radius from being too small
         r = 10;                                         // and keeps the net force capped at a resonable size. 
     }                                                   // also prevents dividing by zero    
     let rSquared = Math.pow(r,2);

     //F = KQ / (r^2)                                    // coulombs law
     let force = kq / rSquared;                          // magnitude of force

     let theta = chargePosition.sub(position).heading();     // angle from point to charge
     let forceX = force * cos(theta);
     let forceY = force * sin(theta);

     let forceVector = createVector(forceX, forceY).mult(-1);    // force from the one charge
     
     finalVector.add(forceVector);                               // adds the one force to the net force
 });

 return finalVector;         // returns net force in a vector format
}

This function is used to move test charges by calculating the net force on the charge every frame and using euler's method to translate the net force to an acceleration to a velocity to an x-y position on the screen. This is how Euler's method is implimented with code

let force = netForceAtPoint(testCharge.position);

if (force.mag() != Infinity && testCharge.moving)       // if the distance between two points is zero, the magnitude of the 
{                                                       // force would be infinity. testCharge.moving is true when the gamemode
    // F  = qE                                          // is "Play"
    // ma = qE
    // a  = (qE)/m
    // m = 1
    // a = qE
    testCharge.acceleration = force.mult(testCharge.charge);    // E = force and q = testCharge.charge
    testCharge.velocity.add(testCharge.acceleration);           // the next two lines are eulers method
    testCharge.position.add(testCharge.velocity);
}

Field Lines

The netForceAtPoint() function seen above is also used to draw field lines by creating a starting point inside a charge and converting the net force at that point into a unit vector of length 5 pixels. Recursion is then used to keep adding a new force unit vector to the tip of the previous one until one end of the vector has collided with a charge. Each starting point begins at a new position around the charge.

The getFieldLinePoints() function in fieldLines.js has 3 inputs. The first input is an x position and the second input is a y position. The third input is the position of the charge in the charges array that the field line will come out of. When the field line either hits a charge with a different index in the charges array or is very far from it's original starting point, then field line is finished. The function will return an array of points that can be connected together in order to make a field line.

The createFieldLines() funcion will create all of the field lines necessary for the configuration of charges on screen.

charges.forEach((charge, i) =>      // loops through each charge in the charges array and gives it an index i
{
fieldLines[i] = [];


let radius = chargeRadius + 1;      // the starting point for field lines is one pixel outside the circle that makes up a charge
                                    // this is so the field line is not colliding with itself and stopping the recursion
let times = Math.abs(charge.charge) * fieldLinesPerCoulomb;     // the number of field lines coming out of a charge grows with the charges magnitude
let origin = charge.position;       //the place the field lines originae from


let point = createVector(0, radius);
for (let a = 0; a < times; a++)
{
    getFieldLinePoints(point.x + origin.x, point.y + origin.y, i);      // this function gets an array of points that make up the field like starting at the field lines origin
    point.rotate(360/times);        // this rotates the starting point of the field line around the charge in accordance to the charges magnitude
}
});
function getFieldLinePoints(x, y, baseCharge)
{
  let position = createVector(x,y);
  let forceVector = netForceAtPoint(position);
  forceVector.setMag(chargeRadius);

  let forceVectorFinalPosition = p5.Vector.add(forceVector, position);
  let vectorToChargeDistance = p5.Vector.dist(forceVectorFinalPosition, charges[0].position);

  let startingPointIsInsideCharge = false;
  let i = 0;
  let chargesLength = charges.length;
  for (i; i < chargesLength; i++)
  {
    let distanceFromEndOfVectorToCharge = p5.Vector.dist(position, charges[i].position);
    if (distanceFromEndOfVectorToCharge < (chargeRadius) && charges[i].charge != 0)
    {
      startingPointIsInsideCharge = true;
    }
  }

  if (!startingPointIsInsideCharge && vectorToChargeDistance < windowSize)
  {
    try
    {
      points.push(position);
      getFieldLinePoints(forceVectorFinalPosition.x, forceVectorFinalPosition.y, baseCharge);
    }
    catch (e)
    {
      //console.log(e);
    }
  }
  else
  {
    points.unshift(charges[baseCharge].position);

    let chargeDistances = [];
    for (let i = 0; i < charges.length; i++)
    {
      chargeDistances.push(charges[i].position.dist(points[points.length - 1]));
    }
    let closestChargeDistance = Math.min(...chargeDistances);

    for (let i = 0; i < chargeDistances.length; i++)
    {
      if (chargeDistances[i] == closestChargeDistance && closestChargeDistance < 100)
      {
        let halfWayPoint = points[points.length - 1].add(charges[i].position).div(2);
        points.push(halfWayPoint);

        halfWayPoint = points[points.length - 1].add(charges[i].position).div(2);
        points.push(halfWayPoint);

        points.push(p5.Vector.add(charges[i].position, createVector(1, 0)));
      }
    }

    fieldLines.push(new FieldLine(points));
    points = [];
  }
}

Contributing

  1. Fork the Project
  2. Create your Feature Branch
  3. Commit your Changes
  4. Push to the Branch
  5. Open a Pull Request

Contact

Dr. Colleen Countryman - Assistant Professor - [email protected]

Project Link: https://github.com/tedkmburu/E-FieldSim-v2

Acknowledgements

  • Dr. Colleen Countryman

  • Ted Mburu

  • Ithaca College Physics & Astronomy Department

  • Ithaca College IT

  • P5.js (p5js.org)

  • Daniel Shiffman (The Coding Train)

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published