Potential Energy field

Hello All!Electrical potential field of 5 charges simulation

As my first post, I will realize an ASP.NET web page which show an image with a potential energy field from different charges. This generates a very nice effect that can be translated to somewhat psychedelic pictures, and as well, what will be discused in next posts, can be used for example to know the most efficient path for go from a point to another avoiding charged obstacles. Using this code theme, this post will cover:

  • Generating online web images in ASP.NET
  • Partial web rendering
  • Use of MemoryStream to work with bitmaps

First of all, we are going to use codebehind: so, will define our web page schema and then we will work on it. We will need : some text boxs to indicate image size, a textbox that indicate parameter analog to Coulombs constant (K) and as well the maximum / 2 value of a charge can be. As well, we will put an asp:Button and a asp:Image objects in an UpdatePanel (in need of a ScriptManager) object, so this way any image reload, by a postback call by tbGenerate button, will not force all whole page to reload. This little trick will avoid flickering when generating new field images, and can be used for a lot of purposes without really lack of performance of the whole web page.

Here it is PotentialEnergy.aspx

 <%@ Page Language="C#" Inherits="OnlineRepository.PotentialEnergy" %>
<!DOCTYPE html>
<html>
<head runat="server">
    <title>PotentialEnergytitle>
    <style type="text/css">
        p{text-align:center;font-variants: small-caps; font-name=Lucida Console; color:white;}
    style>
head>
<body bgcolor="#00000F" >
    <form id="form1" runat="server">
    <asp:ScriptManager runat="server" id="ScriptManager1">
    asp:ScriptManager>
    <p>Please enter values for width, height, and number of chargesp>
    <table align="center" >
    <tr>
    <td><asp:Label runat="server" AssociatedControlID="tbWidth" Text="WIDTH" ForeColor="white" Font-size="small" />td>
    <td><asp:TextBox id="tbWidth" runat="server" Text="1000" Columns="6" Style="width:150pts;text-align: right" ToolTip="width" />td>
    tr>
    <tr>
    <td><asp:Label runat="server" AssociatedControlID="tbHeight" Text="HEIGHT" ForeColor="white" Font-size="small" />td>
    <td><asp:TextBox id="tbHeight" runat="server" Text="300" Columns="6" Style="width:120pts;text-align: right" ToolTip="height" />td>
    tr>
    <tr>
    <td><asp:Label runat="server" AssociatedControlID="tbCharges" Text="CHARGES" ForeColor="white" Font-size="small" />td>
    <td><asp:TextBox id="tbCharges"  runat="server" Text="5" Columns="6" Style="width:120pts;text-align: right" ToolTip="charges" />td>
    tr>
    <tr>
    <td><asp:Label runat="server" AssociatedControlID="tbK" Text="KONSTANT" ForeColor="white" Font-size="small" />td>
    <td><asp:TextBox id="tbK"  runat="server" Text="3000" Columns="6" Style="width:120pts;text-align: right" ToolTip="charges" />td>
    tr>
    <tr>
    <td><asp:Label runat="server" AssociatedControlID="tbMaxCharge" Text="MAXIMUM CHARGE" ForeColor="white" Font-size="small" />td>
    <td><asp:TextBox id="tbMaxCharge"  runat="server" Text="3000" Columns="6" Style="width:120pts;text-align: right" ToolTip="charges" />td>
    tr>
    table>
    <asp:UpdatePanel runat="server" id="UpdatePanel1">
        <ContentTemplate>
            <p>
            <asp:Button id="btGenerate" runat="server" Text="Generate field" OnClick="btGenerate_Click" /><br/><br/>
            <asp:Label runat="server" Text="Any image yet generated" id="InfoLabel" font-size="small" font-name="verdana" >asp:Label><br/>
            <asp:Image runat="server" id="UImage" ImageAlign="AbsMiddle"/>
            p>
        ContentTemplate>
    asp:UpdatePanel>
    form>
body>
html>

Once we got the page template, let’s going to fill it with code. The main purpose it’s very simple: a click of tbGenerate button will generate an image with the calculated field. First of all, we will abstract a charge with a class :

// Charge definition
protected class Charge
{
    public int x;
    public int y;
    public float v;
} 

Due each charge, with a value q, does a potential contribution to a point x,y by V = KQ/r², where r its module distance from that point to the location of the charge, then the function to get V potential at a point will be:

 // Potential of a specific point x,y derived from charges Q
 protected float V(int x, int y,Charge[] Q)
 {
     float v = 0;
 
     // So easy as run all charges and sum each q potential contribution
     foreach (Charge q in Q) 
     {
         // Vq(x,y) = q/r2
         try
         {
             v += K*q.v / ((q.x-x)*(q.x-x) + (q.y - y)*(q.y-y));
         }
         catch(Exception)
         {
             // when r2 = 0, so position of same charge    
         }
     }
     return v;
}

Finally, there’s no more than, in btGenerate click event, realize a bitmap, calculating value at each x,y pixel, and mapping it to a color. For simplicity, we get the value of field V(x,y) to a byte (that way there’s an automatic 0xff modulus) and then, translate that value to an RGB gray color. When got the bitmap generated, we simply save it to a memory stream bitmap, and then, encoded as a Base64 string, we add it to the asp:Image url we got in our UpdatePanel object:

 public void btGenerate_Click (object sender, EventArgs args)
 {
     // Counter
     Stopwatch sw = Stopwatch.StartNew();
     Random r = new Random ();
 
     // get parameters
     int w = Int32.Parse (tbWidth.Text),
         h = Int32.Parse (tbHeight.Text);
 
     K = Int32.Parse (tbK.Text);
     MAXQ = Int32.Parse (tbMaxCharge.Text);
 
     // Create charges and fill them. Values must be negatives & positives
     Charge[] Q = new Charge[Int32.Parse (tbCharges.Text)];
 
     for (int i = 0; i < Q.Length; i++)
     {
         Q[i] = new Charge ();
         Q[i].x = r.Next () % w;
         Q[i].y = r.Next () % h;
         Q[i].v = (r.Next () % MAXQ) - MAXQ / 2;
     }
 
     // Create bitmap, and fill it 
     Bitmap B = new Bitmap (w, h,System.Drawing.Imaging.PixelFormat.Format32bppArgb);
 
     int x, y;
     byte c; // using byte so this way got automatic modulus 0xff for color
 
     for (x = 0; x < w; x++)
         for (y = 0; y < h; y++) 
         {
             c = (byte)V (x, y, Q) ;
             B.SetPixel (x, y, Color.FromArgb (c,c,c));
         }
     
     sw.Stop ();
     InfoLabel.Text = "Bitmap generated on " + sw.ElapsedMilliseconds.ToString () + "ms";
 
     // Lets copy it in a stream and make page to load it encoded as base64
     sw.Start ();
     using (MemoryStream m = new MemoryStream ()) 
     {
         // Saving to .ImageFormat.Gif does a nice 90's effect :)
         B.Save (m, System.Drawing.Imaging.ImageFormat.Jpeg);
         UImage.ImageUrl = "data:image/png;base64," + Convert.ToBase64String (m.ToArray ());
     }
     sw.Stop ();
     InfoLabel.Text += " / total ellapsed time : " + sw.ElapsedMilliseconds.ToString() + "ms";
} 

So that’s all! Got a random, beauty, and consistent image from physics of each day. Don’t hesitate to play with values and of course, with the mapping to a color (try for example different FromARgb(r,g,b) percentages). As well, you can play with K and Max Charges and so on, and you will found some different (and real) behaviours of the potential field.

Here it is some examples. You can experiment with this code at http://dvtrsc.somee.com/PotentialEnergy.aspx  (NOTE: from 20190924, with new servers, ASPX/.NET code will not be kept)

V1v2 v3

Hope it had been nice to you as for me!

Best,

Here complete PotentialEnergy.aspx.cs

using System.Web.UI;
using System.IO;
using System.Drawing;
using System.Diagnostics;
using System.Web.UI.WebControls;

namespace OnlineRepository
{
    public partial class PotentialEnergy : System.Web.UI.Page
    {
        protected float K; // Multiplicative factor
        protected int MAXQ; // Maximum value of a charge * 2

        // Charge definition
        protected class Charge
        {
            public int x;
            public int y;
            public float v;
        }

        // Potential of a specific point x,y derived from charges Q
        protected float V(int x, int y,Charge[] Q)
        {
            float v = 0;

            // So easy as run all charges and sum each q potential contribution
            foreach (Charge q in Q) 
            {
                // Vq(x,y) = q/r2
                try
                {
                    v += K*q.v / ((q.x-x)*(q.x-x) + (q.y - y)*(q.y-y));
                }
                catch(Exception)
                {
                    // when r2 = 0, so position of same charge    
                }
            }
            return v;
        }

        public void btGenerate_Click (object sender, EventArgs args)
        {
            // Counter
            Stopwatch sw = Stopwatch.StartNew();
            Random r = new Random ();

            // get parameters
            int w = Int32.Parse (tbWidth.Text),
                h = Int32.Parse (tbHeight.Text);

            K = Int32.Parse (tbK.Text);
            MAXQ = Int32.Parse (tbMaxCharge.Text);

            // Create charges and fill them. Values must be negatives & positives
            Charge[] Q = new Charge[Int32.Parse (tbCharges.Text)];

            for (int i = 0; i < Q.Length; i++)
            {
                Q[i] = new Charge ();
                Q[i].x = r.Next () % w;
                Q[i].y = r.Next () % h;
                Q[i].v = (r.Next () % MAXQ) - MAXQ / 2;
            }

            // Create bitmap, and fill it 
            Bitmap B = new Bitmap (w, h,System.Drawing.Imaging.PixelFormat.Format32bppArgb);

            int x, y;
            byte c; // using byte so this way got automatic modulus 0xff for color

            for (x = 0; x < w; x++)
                for (y = 0; y < h; y++) 
                {
                    c = (byte)V (x, y, Q) ;
                    B.SetPixel (x, y, Color.FromArgb (c,c,c));
                }
            
            sw.Stop ();
            InfoLabel.Text = "Bitmap generated on " + sw.ElapsedMilliseconds.ToString () + "ms";

            // Lets copy it in a stream and make page to load it encoded as base64
            sw.Start ();
            using (MemoryStream m = new MemoryStream ()) 
            {
                // Saving to .ImageFormat.Gif does a nice 90's effect :)
                B.Save (m, System.Drawing.Imaging.ImageFormat.Jpeg);
                UImage.ImageUrl = "data:image/png;base64," + Convert.ToBase64String (m.ToArray ());
            }
            sw.Stop ();
            InfoLabel.Text += " / total ellapsed time : " + sw.ElapsedMilliseconds.ToString() + "ms";
        }
    }
}

Leave a comment