Archive for the 'Web' Category

Creating a PHP Graphing Calculator

When I want to learn a new technology or technique, I like to think about little projects I've always wanted to try, and see if the project could be implemented using the new technology.

In this case, I've worked quite a bit with .NET's image and graphic related classes and methods, but had never really investigated the PHP equivalent. After reading up on PHP's GD library, I decided to give writing a simple graphing calculator a go.

This tutorial will get you started and allow your users to enter in equations to generate graphs like this:

Graph 1

Graph 2

It will also let users use standard PHP functions and code, giving them use of the full math library, modulus, random functions, etc. You can do some really neat things like this:

Graph 3

So let's get started. In this example, the webpage has a single text box that takes an equation, submits back to the page, and the PHP writes an image directly to the browser. Please note, this example doesn't do any error checking or sanitation of input - please include it in your final code, especially since this page makes use of the "eval" function which is a huge security risk.

 
< ? php
if ($_POST['tempBox'] != '')
{
   // width and height of graph.  These could also be user defined values.
   $GLOBALS['width'] = 500;
   $GLOBALS['height'] = 500;
 
   // drawGrid draws X and Y axis with markers every 50 points
   // We pass in the image resource to draw to
   function drawGrid($passGraphic)
   {
      // Grid is grey
      $gridColor = imagecolorallocate($passGraphic, 200, 200, 200);
 
      // Draw two lines, one vertical in the middle of the grid (width/2)
      // and another horizontal in the middle of the grid (height/2)
      imageline($passGraphic, $GLOBALS['width']/2, 0, $GLOBALS['width']/2, $GLOBALS['height'], $gridColor);
      imageline($passGraphic, 0, $GLOBALS['height']/2, $GLOBALS['width'], $GLOBALS['height']/2, $gridColor);
 
      // Draw a marker ever 50 points on the X axis.  We do this by starting at the center
      // of the board, then incrementing 50 with every iteration.  We also
      // draw a mark in the mirror location to cover the negative side of the axis
      for ($lcv = 0 ; $lcv < $GLOBALS['width'] / 2 ; $lcv += 50)
      {
           // Set tempX and tempY to the current marker location
           $tempX = $lcv;
           $tempY = 0;
 
           // Convert to image coordinates
           cartToPixels($tempX, $tempY);
 
           // Draw the line
           imageline($passGraphic, $tempX, $tempY - 10, $tempX, $tempY + 10, $gridColor); 
 
           // Now do the same for the negative side of the axis
           $tempX = $lcv * -1;
           $tempY = 0;
           cartToPixels($tempX, $tempY);
           imageline($passGraphic, $tempX, $tempY - 10, $tempX, $tempY + 10, $gridColor);
      } 
 
      // We use the same method for drawing markers on the Y axis.
      for ($lcv = 0 ; $lcv < $GLOBALS['height'] / 2 ; $lcv += 50)
      {
           $tempX = 0;
           $tempY = $lcv;
           cartToPixels($tempX, $tempY);
           imageline($passGraphic, $tempX - 10, $tempY, $tempX + 10, $tempY, $gridColor);
           $tempX = 0;
           $tempY = $lcv * -1;
           cartToPixels($tempX, $tempY);
           imageline($passGraphic, $tempX - 10, $tempY, $tempX + 10, $tempY, $gridColor);
      } 
 
   }
 
   // cartToPixels converts Cartesian coordinates to image coordinates.  In our program
   // they are 1 to 1, though you could easily put a coefficient in to scale it up/down.
   // The two gotchas are, coordinates start from the center, and the Y axis goes in the
   // opposite direction (image coordinates, increase in Y goes down.  Cartesian, increase
   // in Y goes up).
   function cartToPixels(&$passX, &$passY)
   {
      // Start from the middle, otherwise 1 to 1 for X
      $passX = $GLOBALS['width'] / 2 + $passX;
 
      // Start from the middle, also subtract to flip for Y
      $passY = $GLOBALS['height'] / 2 - $passY;
 
   }
 
   // pixelsToCart converts from image coordinates to
   // Cartesian coordinates.  Uses the same process as
   // above but reversed.
   function pixelsToCart(&$passX, &$passY)
   {
      $passX = $passX - $GLOBALS['width'] / 2;
      $passY = $passY + $GLOBALS['height'] / 2;
   }
 
   // The plot function simply takes Cartesian coordinates and
   // plots a dot on the screen
   function plot($passGraphic, $passCartX, $passCartY)
   {
      // We use green for our graphs
      $plotColor = imagecolorallocate($passGraphic, 0, 255, 0);
 
      // Convert Cartesian coordinates to image coordinates
      cartToPixels($passCartX, $passCartY);
 
      // Then draw a dot there
      imagesetpixel($passGraphic, $passCartX, $passCartY, $plotColor);
   }
 
   // Push out an image/png mime type to the browser so it knows
   // a PNG image is coming and not HTML
   header ("Content-type: image/png");
 
   // Create a new image resource with the dimensions dictated
   // by our width and height globals.  Starts off with a black
   // background automatically.
   $im = @imagecreatetruecolor($GLOBALS['width'], $GLOBALS['height'])
      or die("Cannot Initialize new GD image stream");
 
   // Draw the grid on our graph first (under everything)
   drawGrid($im);
 
   // We start a loop from 0 (First pixel in width of graph) to the width end,
   // converting image X coordinate to Cartesian X coordinate, then retrieving
   // the corresponding Y Cartesian coordinate (remember, our equations are
   // in 'y =' form.  We multiply it by 50 to give a better resolution - e.g. multiple dots on
   // the Y axis for each X.  This gives us a solid line instead of a bunch of dots for steep
   // functions.  The higher the number, the smoother the line (and the slower the program)
   for ($lcv = 0 ; $lcv < $GLOBALS['width'] * 50 ; $lcv++)
   {
      // Get the left most point on the graph in Cartesian coordinates
      $tempX = 0;
      $tempY = 0;
      pixelsToCart($tempX, $tempY);
 
      // Now get the current Cartesian X coordinate by adding our current loop
      // value divided by 50
      $tempX += $lcv/50;
 
      // Here's where the magic happens.  In a nutshell, we're setting the Y coordinate
      // ($tempY) in relation to the current X coordinate ($tempX)
      // by using the expression specified by the user.  We use PHP's eval function,
      // which allows us to take a string (the user's input box) and run it as if it
      // were PHP code.  We're also converting X to $tempX because that's the actual
      // name of our X variable in our code, not just
      // 'X' - but we don't want the user to have to type '$tempX', so we make it easy for
      // them.  This is the line of code to be especially careful of, as a user could insert
      // malicious PHP code and do bad things.
      @eval("\$tempY = " . str_replace('X', '$tempX', $_POST['tempBox']) . ";");
 
       // Now that we have both coordinates, we plot it!
      plot($im, $tempX, $tempY);
   }
 
   // We write the equation on the graph in red
   $textColor = imagecolorallocate($im, 255, 0, 0);
 
   // Now write the equation on the graph
   imagestring($im, 4, 10, $GLOBALS['height'] - 25, 'y = ' . $_POST['tempBox'], $textColor);
 
   // Output a PNG file now that it's all built
   imagepng($im);
 
   // Free up the resource
   imagedestroy($im);
}
else
{
? >
 
<html>
   <body>
<div style="margin-bottom:20px; font-weight:bold; font-size:16px">Simple Grapher</div>
<form id="graphForm" action="?< ? php echo rand(); ? >" method="post">
         Enter expression: y =
<input type="input" id="tempBox" name="tempBox">
<input type="submit" value="Graph!">
      </form>
<div style="margin-bottom:15px">Enter expression using variable X (PHP code accepted).  Both axes in range of -250 to 250.</div>
<div>Examples:
<ul>
<li><a href="javascript:populate('cos(X / 30) * 50')">cos(X / 30) * 50</a></li>
<li><a href="javascript:populate('pow(X / 30, 3) - X')">pow(X / 30, 3) - X</a></li>
<li><a href="javascript:populate('pow(X, 2) / 15 - 200')">pow(X, 2) / 15 - 200</a></li>
<li><a href="javascript:populate('2000 / X')">2000 / X</a></li>
<li><a href="javascript:populate('tan(X/ 25) * 20')">tan(X/ 25) * 20</a></li>
<li><a href="javascript:populate('-3 * X + 50')">-3 * X + 50</a></li>
<li><a href="javascript:populate('rand() % X')">rand() % X</a></li>
<li><a href="javascript:populate('pow(X,2) % X - X')">pow(X,2) % X - X</a></li>
<li><a href="javascript:populate('pow(X,2) % X - cos(X/20) * 40')">pow(X,2) % X - cos(X/20) * 40 (favorite)</a></li>
</ul></div>
 
   </body>
</html>
 
< script type="text/javascript">
 
   function populate(passExpression)
   {
      document.getElementById('tempBox').value = passExpression;
      document.getElementById('graphForm').submit();
   }
 
< /script>
 
< ? php
}
? >
 

That's it - a few things to note: We tack on a random value to the URL (check out the form code in the HTML section) to ensure our browser doesn't show a cached copy of the image. Since its always using the same filename, the browser would almost certainly show old copies unless a refresh was requested or cache was cleared (depending on the browser).

As you might have seen from the code, generating images is very easy in PHP. We can create a new image resource using the imagecreatetruecolor function. This creates a blank resource, we can also create from preexisting images. Once we have an image resource, we can perform basic graphic functions on it like lines, dots, fill, text, etc. Once we're done, the imagepng function can convert it into actual PNG binary code (in our case we dump it to the browser, but you can specify a filename too to save it to).

It's definitely fun to play around with the graphic library, you can do a lot of neat things and add some needed graphics to normally text-only server side output. Have fun!

Rotate, Flip, and Resize Images in .NET

Often times if you’re writing a web application that deals with user uploaded photographs and other images, a nice feature set to have is one that allows users to do basic manipulations on their photos, such as rotating, flipping, and resizing (the last being very important for download friendly images). Luckily, the .NET framework makes such functionality easy to implement.

Read the Image

Once the image has been uploaded and saved into a directory somewhere accessible by the web server, the first step common to all the procedures is to take the image data inside of the file and get it into memory usable by .NET. We do this by loading the image into a System.Drawing.Bitmap object. One of the constructors of Bitmap class can take the filename of an image, thereby automatically decoding the format and reading in the data into the Bitmap object. .NET supports the most common image formats, such as JPEG, GIF, BMP, and PNG.

 
        Dim PhotoBMP As Bitmap
 
        PhotoBMP = New Bitmap(“myfile.jpg)
 

Above we have statically specified a filename, but you may, of course, dynamically pass in the filename saved on the webserver.

Flips and Rotations

Reorienting the image is incredibly easy. The Bitmap class contains a method called “RotateFlip” that does what you would think – rotates and/or flips. The RotateFlip method takes a single argument, one of RotateFlipType type. Intellisense will enumerate all the options, but each combination of rotation and flip is included: 90, 180, 270 degrees, and flips across X, Y or both axes.

 
        PhotoBMP.RotateFlip(RotateFlipType.Rotate270FlipNone)
 

Above we rotate our image clockwise 270 degrees (i.e. counter-clockwise 90 degrees). We do not perform a flip.

 
        PhotoBMP.RotateFlip(RotateFlipType.RotateNoneFlipY)
 

Above we flip our image across the Y axis but do not perform a rotation. You may also combine both flips and rotations simultaneously if desired.

Saving (and Converting) the Image

At this point, our reorientation has only been performed upon the bitmap data we have in memory, our file is still untouched. We can save our changes back to the original file, or to a new file if desired. The Bitmap class contains an easy to use Save method.

 
        PhotoBMP.Save(“myfile.jpg)
 

Save is overloaded in a number of ways, but you may also specify a filetype as the second argument. In our example, if we wanted to convert our jpg to a png:

 
        PhotoBMP.Save(“myfile.png”,ImageFormat.Png)
 

Very easy as can be seen, no need to lookup file formats and encodings. You may also want to read in the current extension to direct what actions are taken. I use the following regular expression to parse the ending extension of the file.

 
        Regex.Match(FileName, ".[^.]+$")
 

Resizing Images

Resizing images is a little more difficult, but not too bad once you know the procedure. You start by loading bitmap data in the same fashion we did for rotating and flipping. Assuming you want to keep the image in proportion when changing the size, as well as resize with a maximum resolution in mind, we’ll need to calculate the aspect ratio of the image and our conversion coefficient. This may sound complicated, but fear not.

Calculate Aspect Ratio

There are a few methods of doing the math, but in general we know the maximum dimension we want. E.g. 1024x768, 800x600, 640x480, etc. We calculate the aspect ratio of our maximum desired size by dividing the width by the height. In these examples it is 1.333.

Then we must calculate the aspect ratio of the current image. We can obtain the width and height of our bitmap with the width and height properties of the Bitmap class. We know if we have a greater aspect ratio than our target max size’s aspect ratio, then our width is larger and is the confining dimension. If our aspect ratio is less, our height is larger and is the confining dimension. Once we know our confining dimension, to get the conversion coefficient, we divide our current image confining dimension size by the target confining dimension size.

E.g. If we’re converting 2048x1800 to a maximum of 1024x768, we calculate our target image aspect ratio, which we already know is 1.333. Then we calculate our original image aspect ratio, which is 1.138. This is less than 1.333, so we know height is our confining dimension. So we divide 1800 by 768 to get our conversion coefficient, or 2.344. Now we can calculate our target width size by dividing our current width by 2.344, which is 874. Our new image will by 874x768.

Confused? It’s not really as bad as it sounds. First you’re figuring out which dimension will match the largest dimension of your target size, then you’re decreasing the other dimension by the same amount. Here it is in code.

 
        OrigAspectRatio = PhotoBMP.Width / PhotoBMP.Height
 
        TargetAspectRatio = 1024 / 768
 
        If OrigAspectRatio < TargetAspectRatio
   	     ‘Height is confining
	     NewHeight = 768
	     NewWidth = PhotoBMP.Width / (PhotoBMP.Height / 768)
        ElseWidth is confining
	     NewWidth = 1024
  	     NewHeight = PhotoBMP.Height / (PhotoBMP.Width / 1024)
        End If
 

Perform the Resize

Now we have our target width and height via good old fashioned math, now the technical bits for .NET to perform its magic. .NET can perform resizing via the Graphics class, by resizing data from the original bitmap data into a new, smaller bitmap. First we make a new, blank Bitmap of the correct new size.

 
        NewBMP = New Bitmap(newWidth, newHeight)
 

Then we create a new Graphics image using the reference of this new Bitmap object.

 
        ConvertGraphics = Graphics.FromImage(NewBMP)
 

Then we specify the method of scaling the image. Different methods work better for different amounts of scaling, but High Quality Bicubic is the one I tend to use.

 
        ConvertGraphics.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic
 

Now comes the actual conversion, we use the DrawImage method to draw our original bitmap into the new bitmap at the new size (at the starting coordinates of 0,0 in the new bitmap

 
        ConvertGraphics.DrawImage(PhotoBMP, 0, 0, NewBMP.Width, NewBMP.Height)
 

Dispose our old bitmap

 
        PhotoBMP.Dispose()
 

Save our new

 
        NewBMP.Save(new.jpg”, ImageFormat.Jpeg)
 

And dispose

 
        NewBMP.Dispose()
 

And we’re done! You can also use the Graphics object to perform a number of paint-oriented operations on your image, such as lines and drawing text, but that you can have fun experimenting with.

With the exception of resizing which requires a few calculations, .NET makes image manipulation exteremly easy, and when combined into a web application, offers users some useful tools at virtually no cost to you.