Sunday, 10 December 2017

Compressing files in a MVC environment

This article will present a way to compress files in a MVC environment. For compression, we will use the DotNetZip Nuget package, which is an open and free compression library hosted on Codeplex and supported also by Xceed. The DotNetZip produces of course Zip files. DotNetZip website We install this compression library by initiating the following Nuget command: Install-Package DotNetZip We then define a simple view in MVC that has got a file upload input and a submit button:

@model ZipAndMvc.Models.HomeViewModel
@{
    ViewBag.Title = "Home Page";
}

<div class="jumbotron">
    <h2>Test out zipping a file</h2>   
</div>

<div class="row">

    @using (Html.BeginForm("ZipIt", "Home", FormMethod.Post, new {  enctype = "multipart/form-data" }))
    {
        <div class="col-md-3">@Html.Label("Zip password") @Html.TextBoxFor(m => m.ZipPassword) </div>
        <div class="col-md-3"><input type="file" name="FileUpload" /> </div>
        <div>  <input type="submit" id="Submit" value="Upload and zip file" /> </div>
    }
</div>

This view allows the user to type in a password for the file to compress, where the user also selects the file to compress. The user then hits the submit button. The HomeViewModel is very simple with a simple property for setting the zip password. Then we define the following code in the MVC controller:

        public FileStreamResult ZipIt(HomeViewModel viewmodel)
        {
            if (Request.Files.Count > 0)
            {
                using (var zip = new ZipFile())
                {
                    zip.Encryption = EncryptionAlgorithm.PkzipWeak;
                    zip.Password = viewmodel.ZipPassword;
                    zip.CompressionLevel = Ionic.Zlib.CompressionLevel.Default; 
                    var memoryStream = new MemoryStream();
                    zip.AddFile(Request.Files[0].FileName, "");
                    zip.Save(memoryStream);
                    memoryStream.Position = 0;
                    return new FileStreamResult(memoryStream, contentType: "application/zip")
                    {
                        FileDownloadName = Path.ChangeExtension(Request.Files[0].FileName, "zip")
                    };
                }
            }
            return null;
        }


The client posts the file to compress. The controller then inspects the Request.Files collection and selects the first file if there is present any files there. Here we return a FileStreamResult where the compressed data inside the memorystream is returned to the client. We use DotNetZip to do the compression. The benefit of DotNetZip compared to .Net built-in support for compression is more functionality. The code above should be sufficient for basic compression scenario in MVC. Feel free to experiment with DotNetZip. As you can see, you can specify compression level. You can also choose to add directories and much more. The reason for the second argument in AddFile method is to ensure that the file to be added to the zip package is put in the root folder of the zipped file. Also, set the values of Encryption and Password before adding files or directories (Folders) in the ZipFile. You can actually use different passwords also in the Zip file.

Friday, 8 December 2017

Finding old Git Branches with WSL and Bash

Finding old branches in Git

I had to find out which branches in a Git repository was old and output it to a file. An old branch is defined to have no commits the last four months. Here is the bash script I ended up with.




#!/bin/bash

resolveOldBranches(){
branchfile="oldbranches.txt"
declare -i branchiteration=0
branchcount=$(git branch -a | wc -l)

if [ ! -e $branchfile ] ; then
 touch $branchfile
fi

#empty the oldbranch file
: > $branchfile

for k in $(git branch -a | sed /\*/d); do


 if [ -z "$(git log -1 --since='4 months ago' -s $k)" ]; then
  echo $k | cut -d/ -f3 >> $branchfile
 fi
 branchiteration=$branchiteration+1
 percentage= bc <<< "scale=2;($branchiteration/$branchcount)*100"


 read -n 1 -t 0.1 input                  # so read doesn't hang
   if [[ $input = "q" ]] || [[ $input = "Q" ]]
   then
      echo # to get a newline after 
echo -e "XXX\n$($percentage)\nAnalyzing $branchiteration of $branchcount $(bc <<< "scale=2;($branchiteration/$branchcount)*100") % done. \n(Exit: Q/q)... \nXXX"

done | whiptail --title "Resolving OpPlan 4 branch ages" --gauge "Analyzing.. (Press Q or q to exit)" 10 60 0


}

resolveOldBranches
cat $branchfile

Saturday, 21 October 2017

X11 Subsystem running WSL Windows 10 subsystem for Linux


This article will look at running graphical Linux applications in Windows 10. As many of you know, Windows 10 can offer a subsystem for Linux running inside Windows 10. We need to download and install a X11 server to be able to run graphical applications that need more than the console, that is a graphical user interface. First off, download XMing for Windows Server from here:

XMing X Server for Windows Server
After installation of Xming, start Xming from your start button.
Now we need to install the Linux Subsystem itself, if you have not done this yet.
Inside Powershell, enter the following command as an administrator:

Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux 

If you are not a Windows Insider yet, you must join this program. See this page for a description how to obtain WSL or Windows Subsystem for Linux and install it.
Installation guide of WSL - Windows Subsystem for Linux After you have downloaded WSL and installed it using Powershell, select Launch and in the console Window after WSL is further installed and setup, enter a user in Linux you will use as administrator (in Linux terms, this is the root user). You can add another user using the adduser command as root in the Bash console. You can access WSL Linux inside Windows 10 by selecting the menu item Bash on Ubuntu on Windows.


Getting started using WSL

You probably want to update your WSL to the latest version. WSL is a specialized Ubuntu Linux distribiton. Enter the following command to upgrade Linux Kernel and applications, note that it will take time to download package and upgrade to latest versions:

apt-get update && apt-get upgrade The version of Linux distro can be found using this command:


lsb_release -irc 

As we see, I am running Ubuntu 16 Xenial. Next off, we are going to support X-Server. Download first the X11 apps.
root@tore# apt-cache search x11-apps x11-apps - X applications
Install X11-Apps. This will also download all required additional pacages. apt-get install X11-Apps
You need to do this as root or switching to a super user and use the sudo command. Next off, edit your ~/.bashrc file. This is similar to the $Profile file that Powershell uses. You are setting up your environment here to make sure you can use X-Server based Graphical User Interfaces. Such as Gimp, Firefox, Stellarium, Quake and so on - requiring a GUI. But we will start off with running a X11-app, such as Xeyes. Make sure your ~/.bashrc file got the following content. First download Nano if you do not want to use Vim or Vi. (apt-get install nano)
nano ~/.bashrc Next off, add the following line at least: export DISPLAY=:0 We can also add more nice colors and some additional information and offer to switch to a non-root user initally.
export DISPLAY=:0
LS_COLORS='rs=0:di=1;35:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lz=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.axa=00;36:*.oga=00;36:*.spx=00;36:*.xspf=00;36:';
export LS_COLORS
PS1='\e[37;1m\u@\e[35m\W\e[0m\$ '
echo "Welcome to Bash on Ubuntu on Windows. To switch to your user Tore :"
echo "su tore && cd /home/tore"
echo "Do this now? [y/n]"

read -rsn1 input
if [ "$input" = "y" ]; then
    cd /home/tore
    su tore
    ls -al
fi


echo "To adjust profile file edit the file ~\.bashrc with Nano"


Last, we can test out everything, running Xeyes. Note that you can test out more advanced Linux applications such as Gimp by running: apt-get install gimp This will install Gimp, the Gnu Image Manipulation Program. Then just enter gimp from the console. You can also run such programs in the background by adding the ampersand (&): gimp &

Sometimes, we need to force an exit of such a Linux app with a GUI running from the console entering Ctrl+C to force exit the process.

Tuesday, 3 October 2017

Setting up a Git alias with a shell function

This short article will show you how to search in the log of a Git repository with a shell function. This shell function can also receive a positional parameter to use in the shell function. First off, edit the .gitconfig file in your user's home folder. On Linux Mint for example, the folder should reside in the /home directory. You can use nano for example. (apt-get install nano)


.gitconfig : 

[alias]
        searchlog = "!f() { git log --all --decorate --graph -i --grep \"$1\";  }; f"




Note the positonal parameter $1 here, we escape the quote also. The function inside the alias is a shell function.


To use this alias command, type for example:
git searchlog test
This will search the Git log for the parameter passed in (test) and include some flags to decorate the log displayed. Using Git aliases, we can do lenghty Git commands with shorter aliased commands.

Friday, 22 September 2017

Displaying altitude with Google Maps





We will look at displaying additional positional information with Google Maps in this article, such as altitude. First off, we need to add a new object to Google Maps Api v3 - the MarkerWithLabel object. This allows us to add text labels to Google Maps. They are also draggable. I have updated a Plunk below so you can see the end result yourself:

Location in Google Maps with altitude - Plunk

The MarkerWithLabel.js contains the additional javascript code to add the MarkerWithLabel object. We add the marker inside the showPosition method with the following code:


  function showPosition(position){
        
         var mapCanvas = document.getElementById("demo");
  var myCenter = new google.maps.LatLng(position.coords.latitude, position.coords.longitude); 
  var mapOptions = {center: myCenter, zoom: 12};
  var map = new google.maps.Map(mapCanvas,mapOptions);
  var marker = new google.maps.Marker({
    position: myCenter,
    animation: google.maps.Animation.BOUNCE
  });
  marker.setMap(map);
  
  console.log(position.coords);
  
  
   var image = 'https://developers.google.com/maps/documentation/javascript/examples/full/images/beachflag.png';
 
  

     var altitudeMarker = new MarkerWithLabel({
       position: myCenter,
       draggable: true,
       raiseOnDrag: true,
       icon: image,
       map: map,
       labelContent: position.coords.altitude.toString(),
       labelClass: "labels", // the CSS class for the label
       labelStyle: {opacity: 0.75}
     });
     
     
        
      }

We add the altitude, using the position.coords.altitude object.

A good tip here is to add high accuracy of the GeoLocation.


  function getLocation() {
    

    var geo_options = {
     enableHighAccuracy: true, 
     maximumAge        : 30000, 
     timeout           : 27000
    };


         if (navigator.geolocation){
          var position = navigator.geolocation.getCurrentPosition(showPosition, null, geo_options);
        }
      }



Note that the client has got to have a positioning device supporting returning the altitude. Most smartphones today got GPS for example.
As a test - you can change the value displayed to position.coords.accuracy.toString() instead. Accuracy is always return in the Coords object.

Sunday, 17 September 2017

HTML 5 Geolocation introduction

This article will introduce you to HTML 5 Geolocation API.

First off, test the browser for support of HTML 5 Geolocation. The first call to get the location of the client / user will also prompt a dialog that the user must usually confirm to to allow getting the location.
The following Javascript is necessary to get the latitude and longitude of the client :

 function getLocation() {
  if (navigator.geolocation){
   var position = navigator.geolocation.getCurrentPosition(showPosition);
  }
 }
        
 function showPosition(position){
  console.log(position);
  $("#demo").html(position.coords.latitude + " " + position.coords.longitude);
 }








Note that you can retrieve additional information such as altitude, heading and speed if the client got positioning hardware supporting this.
Mozilla Developer Network - Coordinates object
In Firefox, you have to use HTTPS to use Geolocation in newer versions of this browser. The client must agree to share the current location. A demo of Geolocation is here:

HTML 5 Geolocation demo - Plunk

Let us also use a map to show the location of the user. We can use Google Maps API. To use this API, request a Google API Key from here:

Google API Key site

We plot the current location with some script below using the Google Maps API, adding a Marker that is bouncing on top of the current location.

  function getLocation() {
   if (navigator.geolocation){
    var position = navigator.geolocation.getCurrentPosition(showPosition);
   }
  }
        
  function showPosition(position){
        
   var mapCanvas = document.getElementById("demo");
   var myCenter = new google.maps.LatLng(position.coords.latitude, position.coords.longitude); 
   var mapOptions = {center: myCenter, zoom: 12};
   var map = new google.maps.Map(mapCanvas,mapOptions);
   var marker = new google.maps.Marker({
    position: myCenter,
    animation: google.maps.Animation.BOUNCE
   });
    marker.setMap(map);
        
  }
      



The current location is displayed using Google Maps as displayed here:



Support for Geolocation in Firefox is limited to HTTPS and recent version of Firefox may not function with Geolocation in Linux, newer than Firefox version 20. If so, downgrade to Firefox Version 20. I have tested the code above using Opera web browser in Linux Mint 15.

Note that we here added Google Maps Javascript source reference and an Google API key.





  <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyAeymq4mlrbKoBUhU3LdegaudQDnY7MFPo&callback=myMap"></script>
  



Sunday, 3 September 2017

Modernizr feature discovery demo

I just made a Modernizr feature discovery demo! It lists up the features Modernizr looks for and tests the browser you are running if it supports that feature! Plunk - Modernizr demo




<html class="no-js">

<head>
<meta charset="utf-8"/>
<title>Modernizr browser feature detection</title>
<script data-require="modernizr@*" data-semver="2.6.2" src="//cdnjs.cloudflare.com/ajax/libs/modernizr/2.6.2/modernizr.js"></script>
<script data-require="jquery@*" data-semver="3.1.1" src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script data-require="underscore.js@*" data-semver="1.8.3" src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
</head>

<style>

.greenlight {

}

.yellowlight {

}

.redlight {

}

.trafficlight {


}

.trafficlight:after {
    background-color: #10AF20;
 border-radius: 10px;
 padding-left: 5px;
 padding-right: 5px;
}

.redlight:after {
 content: "UNSUPPORTED ";
    color: #f0f0af;
    background-color: #AF1020;
}

.yellowlight:After {
 content: "PERHAPS SUPPORTED";
 color: #f0f0af;
    background-color: #AFAF10;
}

.greenlight:after{
 content: "SUPPORTED ";
 color: #f0f0af;
 background-color: #10AF20;
}

.underlight{
 margin-left:30px;
}

li {
 font-family: Trebuchet, Verdana;
 margin: 4px;
}

</style>

<body>
<h2>Modernizr browser feature detection</h2>


<ul id="ModernizrFeatureList">
<script>

function displayfeature(feature, isSubfeature){
   var isFeaturePartiallySupported = false;
   var isFeatureSupported = false; 
   if (eval("Modernizr." + feature) === true){
    isFeatureSupported = true;
   }
   if ((eval("Modernizr." + feature) === "probably") | (eval("Modernizr." + feature) === "maybe")){
    isFeaturePartiallySupported = true;
   }

   //debugger;

   var trafficlight = "trafficlight" + " ";
   if (isFeatureSupported)
    trafficlight += "greenlight"; 
   if (isFeaturePartiallySupported)
    trafficlight += "yellowlight"; 
   if (!isFeatureSupported && !isFeaturePartiallySupported){
    trafficlight += "redlight";
   }

   if (isSubfeature)
    trafficlight += " underlight";

   var featureToShow = "<li class='" + trafficlight + "'>" + feature + ": " + eval("Modernizr." + feature) + " </li>";

   return featureToShow;
}

var modernizrProps = _.sortBy(Object.keys(Modernizr), function(key){ return key; });


modernizrProps.forEach(function(feature, index){

  var modernizrFeatureType = eval("typeof Modernizr." + feature); 

   if (modernizrFeatureType == "boolean"){
     var f = displayfeature(feature, false);
     $("#ModernizrFeatureList").append(f);
   }
   else if (modernizrFeatureType === "object"){
    try {
     //debugger;
     for (var subfeature in  eval("Modernizr." + feature)){
     var subf = displayfeature(feature + "." + subfeature, true);
     $("#ModernizrFeatureList").append(subf);
    }

    }
    catch (Error){

    }
   }
 
});


</script>

</ul>
</body>
</html>


Thursday, 24 August 2017

Getting started with CSS3 Animations

CSS3 animations can give your web sites dynamic effects and visual queues for added user friendly navigation. Of course, such effects could quickly become noisy if exaggerated. I have added a sample demo of a Bookshelf with HTML and CSS3 below.

CSS3 Animated Bookshelf (Plunk)


The following HTML builds up the user interface - that is, the Bookshelf itself.


<!DOCTYPE html>
<html>

  <head>
    <link rel="stylesheet" href="style.css">
    <script src="script.js"></script>
  </head>

  <body>
    <h1>CSS3 Animations - Bookshelf</h1>
    
    <div id="box1" class="box">
      <p>HTML 2.0 for beginners</p>
    </div>
    
     <div id="box2" class="box">
      <p>Internet Relay Chat Powertips</p></p>
    </div>
    
     <div id="box3" class="box">
      <p>MS-DOS 5.0 Masterclass</p>
    </div>
    
     <div id="box4" class="box">
      <p>QBasic Game Coding</p>
    </div>
    
  </body>

</html>

To animate these "Books", that is the < div > elements, CSS3 rules are added. The following CSS3 style sheet was added:


  /* Styles go here */

@keyframes FocusBook {
  0% { 
    transform: scale(1.1);
  }
100% {
    transform: scale(1.8) rotate(90deg);
    box-shadow: 8px 8px 8px #8080af;
    text-shadow: 1px 1px #102030;
    top: 50%;
    left: 50%;
    position: fixed;
    color: white;
    background:linear-gradient(90deg, peru, brown);
  }
}

.box {
    width:50px;
    height:250px;
    border:1px solid black;
    box-shadow: 2px 2px 2px #808080;
    background:linear-gradient(peru, burlywood);
    transform: rotate(0deg);
    transition: all 1s;
    float: left;
    opacity:0.9;
    margin: 2px;
    user-select: none;
}

.box:hover {
  transform: translate(2px, 0px) rotate(2deg) scale(1.1);
  cursor: pointer;
  box-shadow: 2px 2px 2px yellow;
  color:black;
  background:linear-gradient(45deg, peru, brown);
  z-index:20;
}

.box p {
  font-family: Verdana;
  color: charcoal;
  font-size:10pt;
  white-space: nowrap;
  transform: rotate(-90deg) translate(-190px, -10px);
}

.box:active {
  animation: FocusBook 1.0s infinite alternate;
}


To support transitions with CSS, you add the CSS attribute transition with a comma-separated list of css attributes to allow transitioning and the time the transition should take. We add first all here to allow transition all attribute changes:

.box {
    width:50px;
    height:250px;
    border:1px solid black;
    box-shadow: 2px 2px 2px #808080;
    background:linear-gradient(peru, burlywood);
    transform: rotate(0deg);
    transition: all 1s;
    float: left;
    opacity:0.9;
    margin: 2px;
    user-select: none;
}

The transition is then in effect for the hover transition.

.box:hover {
  transform: translate(2px, 0px) rotate(2deg) scale(1.1);
  cursor: pointer;
  box-shadow: 2px 2px 2px yellow;
  color:black;
  background:linear-gradient(45deg, peru, brown);
  z-index:20;
}

Now, let's take a look at the animation effect when the user clicks on one "Book". We define key frames first.
@keyframes FocusBook {
  0% { 
    transform: scale(1.1);
  }
100% {
    transform: scale(1.8) rotate(90deg);
    box-shadow: 8px 8px 8px #8080af;
    text-shadow: 1px 1px #102030;
    top: 50%;
    left: 50%;
    position: fixed;
    color: white;
    background:linear-gradient(90deg, peru, brown);
  }
}

Then we play the animation after defining the key frames (note the percentage to specify keys at a relative elapsed time of the animation):

.box:active {
   animation: FocusBook 1.0s infinite alternate; 
}

Note that the CSS attribute animation now points to the @keyframes defined. In addition, the animation uses the keywords infinite and alternate.

The best use of CSS3 animations is most likely subtle changes in color and size, and using CSS transforms. You can define many keys in @keyframes definition for complex animations.

Wednesday, 23 August 2017

Getting started with web fonts in CSS

Now a simple topic in this article. We will look into how to use web fonts in CSS. Web fonts lets you add additional fonts that the users can view on your web site and offer a variation to the usual fonts that different systems support. The standard fonts or web safe fonts will become tedious and boring in the long run, web fonts will offer you variation on a grand scale! First off, we need a source for fonts where we can download fonts. You can for example download web fonts from the following site:

Font Squirrel (www.fontsquirrel.com)

Look for a web font to test out, for example Gooddog. Choose the pane Webfont kit and then hit the button Download @Font-Face kit after selecting formats to download. The format WOFF (.woff) is most compatible with different browsers. Then a .zip file is downloaded where you already can test out a demo page with the necessary CSS and HTML to get started.

First off, put the .woff file into a folder near the CSS of your web page. Now define the web font as a resource in CSS using @font-face like the following CSS rule:

@font-face {
    font-family: Good-dog;
    src: url(../fonts/GoodDog-webfont.woff) format('woff');
}

You have now defined the font resource and can use it with the friendly name you defined for font-family. For example, for the standard ASP.NET Mvc sample site for Visual Studio 2017, add this rule:
h1 {
    font-family: Good-dog;
}

.lead {
    font-family: Good-dog;
}


The result is then:
Note that you get excellent support in VS 2017 for defining such CSS rules! Also check out the license terms for the different web fonts you download. Some fonts is not free to use in commercial applications or other production. The WOFF format is not supported in IE browsers predating IE 8. Internet Explorer may also not work with uppercase letters in the url attribute of src. More info about @font-face on the following w3schools page:

@font-face CSS rule
Note that you should provide a fallback to the places in your CSS rules where you make use of the web font, such as fallback to Arial and so on. Mozilla Firefox might also deactivate web fonts intially as a security measure. Tip how to disable in Google Chrome and Mozilla Firefox:
Disable web fonts tip

Monday, 21 August 2017

Trigonometric functions and integrals with Js and Canvas

This article will look into displaying trigonometric functions with Javascript and HTML5 Canvas.

A plunk is availble so you can test out different trigonometric functions in Javascript yourself. The following link gives you access to the demo:

Plunk Integral of functions using HTML 5 Canvas and Js
The form input of the demo first asks for an equation to display. Supported here is the format that the Math.Js library supports. You can use for example f(x) = sin(x). I have tested it out with 2D functions supporting one variable x. The drop down lets you choose some prefilled equations for you to test out. The different trigonometric functions supported are the usual ones, also the hyperbolic and arc hyperbolic ones can be tested out, plus some polynomial functions and so on. The Math.Js library will build up a function delegate that can be used in the calculation of the integral and the calculation of the function curve itself.



Method for drawing the equations

The javascript code below is used to draw the equations. We use Math.Js to draw the function, since Math.Js got an excellent parser to build functions that allow different plots of trigonometric functions. But also polynomial equations and so on is supported. Note the math.eval(..) line!

    Graph.prototype.drawEquation = function(polynomialequation, increment, 
     isIntegral, color, thickness, startx, endx){

        var totalArea = 0.0;

        var context = this.context;
        context.save();
        context.save();
        this.transformContext();
        
        //debugger;
        
        var parsedFunc = math.eval(polynomialequation);
        var cury = 0;

        context.beginPath();
     
        cury = parsedFunc(this.minX);
        
        context.moveTo(this.minX, cury);
        
        for(var x = this.minX + this.iteration; x <= this.maxX; x += this.iteration) {
   
          cury = parsedFunc(x);
          
          context.lineTo(x, cury);
        }

        context.restore();
        context.lineJoin = 'round';
        context.lineWidth = thickness;
        context.strokeStyle = color;
        context.stroke();
        context.restore();

        if (isIntegral){
            var context = this.context;
            context.save();
            this.transformContext();

            var currentY = 0.0;

            context.lineWidth = 0.1;
            context.strokeStyle = 'red';

            for(var x = startx; x < endx; x += increment) {
              context.beginPath();
              currentY = parsedFunc(x+increment/2.0);
              context.rect(x, 0, increment, currentY);
              totalArea += Math.abs(increment * currentY);
              context.stroke();
            }           
      
        }

        return totalArea.toFixed(2);

      };


Further issues around asymptotic behavior

One thing I have seen is that the calculation of the integral fails to detect asymptotic boundary conditions. For example, the tangens function $$ f(x) = tan(x) $$ has got several asymptotes vertically. These asymptotes occur at the following x-values: $$ \frac{\pi}{2} * (2n+1) $$ for the natural numbers $$ \mathbb{N} $$. Detecting such asymptotes can be very hard, since you have to first decide when a function becomes asymptotic, and you also have to test the function at specific intervals and narrow it down. The Riemann sum as an approximation of the integral will fail for such as asymptotic functions. I use the Midpoint rule in an easy manner here by using the average or middle point of the incrment and look at the function value right there. You could also calculate $$ f(x) $$ at the minimum and maximum part of the increment and average the two function values instead of calculating the function value at the middle point. One other way is to set the increment at a very low value.
You can also test this out yourself! Try setting the increment to a very low value, like 0.00001! You will see that the integral keeps growing as you lower the increment value. This is because small increments for the Riemann sum will more and more find the true integral of the tagens function in this case.
If you are a math student and have good tips here, I would be happy to know more about the strategy around integrals and Riemann sums to deal with asymptotes!

Sunday, 20 August 2017

Integrals in math with Javascript

This article will describe a demo how to calculate and display definite integrals in a web browser using the HTML 5 <Canvas> element and demonstrate an approximation of integrals using the method of summing up the rectangles between a function curve and the x-axis, that is the equation under consideration to do the calculation of the definite integral on. The integral is defined as the area between the function curve and the x-axis.
Note that we in this demo will consider equations on the form of polynomial curves.
The picture reads integration, it should be integrals! Lets first look some math of how to do the integral of our example, we consider polynomials here, which will be defined as:
$$ y = f(x) = ax^3 + bx^2 + cx + d $$ The integral will be the areal between the function f(x) between startx and endx. Consider the following definition of the indefinite integral: $$\int f(x)\ dx $$ The definite area is then between startx s and endx e is then: $$\int_s^e f(x)dx $$ Some math ensues of to check the calculation of the integral (area), consider this following example :
$$ a = 0.05, b = 0.05, c = 0.05, d = 1 $$. We consider x to be between 1 and 4. Manually calculating the area results in the calculated area of 7.614 (the image shows 7.62, but a check showed it to be 7.614).
Calculation of the integral uses in the hand written calculation the start and end x-values and subtracting the end value of the integral formula with the start value. We consider x here to be between [1..4]. As the screen shot of the demo displays, we approximate this integral to the value 7.60 by calculating the integral as the sum of the midpoint rectangles.

The difference in the specific example between the hand written calculation of 7.62 (exact integral) and approximation of 7.60 is because of the fact that we approximate the integral by summing up the rectangles below the curve. Actually in some cases part of the rectangles are above the curve, there is some discrepancy between the approximate rectangle shape and the true shape of the integral. To get an even more exact value, reducing the increment from 0.5 to 0.25 will get a closer value to the exact one. Reducing the increments will result in smaller rectangles (thinner) following the curve more exact.



Now that we see that our math is sound, we can look at a Plunk with a demo displaying this.
Math integral demo - Plunk
In the sample the graph is displayed with Canvas in HTML 5. The following function in Javascript is used to display the integral and the midpoint rectangles:



   Graph.prototype.drawPolynomial = function(polynomialequation, a, b, c, d,
 increment, isIntegral, color, thickness, startx, endx){


        var totalArea = 0.0;

        var context = this.context;
        context.save();
        context.save();
        this.transformContext();

        context.beginPath();
        context.moveTo(this.minX, polynomialequation(a, b, c, d, this.minX));

        for(var x = this.minX + this.iteration; x <= this.maxX; x += this.iteration) {
          context.lineTo(x, polynomialequation(a, b, c, d, x));
        }

        context.restore();
        context.lineJoin = 'round';
        context.lineWidth = thickness;
        context.strokeStyle = color;
        context.stroke();
        context.restore();

        if (isIntegral){
            var context = this.context;
            context.save();
            this.transformContext();

            var currentY = 0.0;

            context.lineWidth = 0.1;
            context.strokeStyle = 'red';

            for(var x = startx; x < endx; x += increment) {
              context.beginPath();
              currentY = polynomialequation(a, b, c, d, (x+increment/2.0));
              context.rect(x, 0, increment, currentY);
              totalArea += increment * currentY;
              context.stroke();
            }           
      
        }

        return totalArea.toFixed(2);

      }


The following code is used to get the values for the coefficients of the polynomial equation and draw the polynomial function and the corresponding approximation rectangles for the integral.


  
     $(document).ready(function(){

      $("#btnDemo").click(function(){

        $("#a").val("0.05");
        $("#b").val("0.05");
        $("#c").val("0.05");
        $("#d").val("1");
        $("#increment").val("0.25"); 
        $("#startx").val("1.0"); 
        $("#endx").val("4.0"); 


        $("#btnGraph").click();

      });
     
      $("#btnGraph").click(function(){

        var a,b,c,d,increment,startx,endx = 0;

        a = parseFloat($("#a").val());
        b = parseFloat($("#b").val());
        c = parseFloat($("#c").val());
        d = parseFloat($("#d").val());
        increment = parseFloat($("#increment").val());
        startx = parseFloat($("#startx").val());
        endx = parseFloat($("#endx").val());
       
        var myGraph = new Graph({
         canvasId: 'Graph',
         minX: -10,
         minY: -10,
         maxX: 10,
         maxY: 10,
         unitsPerTick: 1
        });  

          var totalArea = myGraph.drawPolynomial(function(a,b,c,d,x){ 
           return ((a*x*x*x) + (b * x*x) +  c*x + d * 1.0).toFixed(2); 
           }, a, b, c, d, increment, true, 'blue', 1.0,  startx, endx);   

         $("#detailsInfo").append("Total area under graph: " + totalArea);

      

     
      });
     
     });
     


There is a lot of code to digest here, see through the code in the Plunk the article points to. The key points here is that we pass in a function as a delegate to be our Polynomial equation. We also approximate the integral (area) by summing up the rectangles below it.

Note that this demo also supports calculating the integral when it dips below the positive y axis!


Fix of reloading functionality

One issue with Canvas and a central one is the fact that the Canvas in HTML 5 is a rasterized grid. It is more like a single layer Photoshop image than a vectorized canvas like Illustrator. With that fact in mind, clearing the Canvas for a new redrawing functionality proved hard. In addition, I also use a transformation here from View-Coordinates to Object-Coordinates. A brute force redrawing is therefore preferred. The following ReloadCanvas method will fix this!

     function ReloadCanvas(canvasId){
        //debugger;

        var oldCanvas = document.getElementById(canvasId);
        console.log("Old canvas: " + oldCanvas);
        var newCanvas = document.createElement('canvas');
        var oldWidth = 500;
        var oldHeight = 500;
        if (oldCanvas){
          oldWidth = oldCanvas.width;
          oldHeight = oldCanvas.height;
          oldCanvas.parentNode.removeChild(oldCanvas);
        }
        newCanvas.id = 'Graph';
        newCanvas.width = oldHeight;
        newCanvas.height = oldWidth;
        document.body.appendChild(newCanvas);
      }



We detach the old canvas and insert a new one into the DOM (Document Object Model). I use here old api methods, you could also resort to more use of jQuery of course. Note that I try to get the old Canvas width and height and copy this to the new blank Canvas that I insert. You must use parentNode here to to the appendChild and removeChild methods to do the replacement of Canvas. This way, we can paint the Canvas again with a fresh new Canvas and have a simpler demo! I have updated the Plunk with a fixed Fork below:
Plunk - Integral math demo app

This demo also supports not only a convenient reload method to make the demo easier to test out, but also supports "negative integrals", i.e. sections where the integral dips below the x-axis as shown in the following image:

Note about accuracy

The total area calculated uses some rounding here. I used the .toFixed(2) to stick to a precision of 0.01. You can try out .toFixed(3) to test out a better precision. That will calculate even more precise the calculated area. The increments should of course be small and you will also see that some small values are still imprecise. We calculate the y value to be the midpoint of the polynomial f(x) and the midpoint should follow the curve as good as possible.

Actually this integral demo app can support other equations that just polynomials. We could support for example trigonometric functions and so on also. I will look at this in a future demo+article!
So there you have it, Canvas and Js can tutor kids and students math concepts in Calculus quite elegant in a web browser. We are seeing that Javascript and HTML 5 are now just as powerful as Java applets in the good old days to describe different concepts and prove that once again Math is fun, especially when computers also come into play!

Note for students


What we have used in this demo is the calculation of definite integrals using the Midpoint Rule and Riemann sum. You can read more about it here. This is standard 1st year Calculus syllabus.
Riemann Sums (Wikipedia)

Monday, 7 August 2017

Finding intersection between two lines with HTML5 and Javascript

This article will look at detecting intersection of two lines using Javascript and using HTML 5 Canvas to display the two lines.

A plunk is available here:
Plunk - Intersection of two lines

















We continue from the last article and add a LineIntersection function and add prototype functions. This "class" will help us find the intersection points. The code is as follows:

   function LineIntersection (config){
        this.m1 = config.m1;
        this.b1 = config.b1;
        this.m2 = config.m2;    
        this.b2 = config.b2;

        this.error = 0;
        this.marginOfError = 0.01;
        this.iterations = 0;
        this.maxIterations = 200;
   
      }

      LineIntersection.prototype.PrintData = function() {
        console.clear();
        console.log("m1: " + this.m1 + " b1: " + this.b1 + " m2: " + this.m2 + " b2: " + this.b2);
      }

      LineIntersection.prototype.GetGuess = function(guess){
        var newguess = ((this.b2 - this.b1) / (this.m1 - this.m2) + guess) / 2;
        return newguess;
      }

      LineIntersection.prototype.Y1 = function (guess){
        return this.m1 * guess + this.b1;
      }

      LineIntersection.prototype.Y2 = function (guess){
        return this.m2 * guess + this.b2;
      }

      LineIntersection.prototype.DeltaY = function (guess){
        return Math.abs(this.Y1(guess) - this.Y2(guess));
      }

      LineIntersection.prototype.FindIntersection = function(){

        this.iterations = 0;
       
        if (this.m1 == this.m2)
        {
          alert("The two lines are parallel!");
          return { x: "Infinity", y: "Infinity"};
        }

        guess = Math.floor(Math.random() * 10 + 1);

        do {
         guess = this.GetGuess(guess);

         this.error = this.DeltaY(guess);

         if (this.iterations > this.maxIterations){
          break;

          this.iterations = this.iterations + 1;
         }

        }
        while (this.error > this.marginOfError);

        return { x: guess.toFixed(2), y: this.Y1(guess).toFixed(2) }

      } //function LineIntersection 



The intersection point is then calculated using the code in the following jQuery button click event handler:

   $("#btnIntercept").click(function(){
          var m1,b1,m2,b2 = 0;

        m1 = parseFloat($("#m1").val());
        b1 = parseFloat($("#b1").val());
        m2 = parseFloat($("#m2").val());
        b2 = parseFloat($("#b2").val());
       
        var myGraph = new Graph({
         canvasId: 'Graph',
         minX: -10,
         minY: -10,
         maxX: 10,
         maxY: 10,
         unitsPerTick: 1
        });      

        myGraph.drawLine(m1, b1, 'blue', 3);

        myGraph.drawLine(m2, b2, 'red', 4);

        var lineIntersect = new LineIntersection({
          m1: m1,
          b1: b1,
          m2: m2,
          b2: b2
        });

        lineIntersect.PrintData();

        var intersect = lineIntersect.FindIntersection();
        console.log(intersect);
        console.log(intersect.x);

        myGraph.drawRect(intersect.x, intersect.y, 0.5, 0.5);

        $("#detailsInfo").append("Intersection point: " + " X: " + intersect.x + " Y: " + intersect.y);


To plot a dot where the calculated intersection point the following code is used:
        Graph.prototype.drawRect = function (x, y, width, height){
        var context = this.context; 
        this.transformContext();
        context.strokeStyle = 'green';
        context.fillRect(x - (width/2), y - (height/2), width, height);
      }
To move from object space coordinates to display coordinates we make use of the following helper function:

        Graph.prototype.transformContext = function() {
        var context = this.context;

        // move context to center of canvas
        this.context.translate(this.centerX, this.centerY);

        /*
         * stretch grid to fit the canvas window, and
         * invert the y scale so that that increments
         * as you move upwards
         */
        context.scale(this.scaleX, -this.scaleY);
      };

Sunday, 6 August 2017

Drawing intercepting lines with HTML 5 and Js - Part One

I am working with a sample demo that will draw two lines and calculate the intersection. The demo will use Js and HTML 5 Canvas. I have made a Plunk available here:

Canvas line drawing Plunk

The image belows shows a rendering of two lines on the form : y = f(x) = ax + b, y1 = x + 1, y2 = -x + 3

The HTML and Js code is below. I will in the later articles focus on calculating the interception with an iterative approximation method for the intercept. In a future article I will clean up the code and I will look on the code bits and look at Canvas functionality. Here is the code:




















<!DOCTYPE html>
<html>

  <head>
    <meta charset="UTF-8" />
    <script data-require="jquery@*" data-semver="3.1.1" src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
    <link data-require="bootstrap-css@*" data-semver="4.0.0-alpha.4" rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.4/css/bootstrap.min.css" />
    <link rel="stylesheet" href="style.css" />
    <script src="script.js"></script>
  </head>

  <body>
    <h1>Intersection of two lines with Canvas and Js </h1>
    
    <fieldset>
      <label>y = mx + b</label> <br /><br />
      
      <table>
        <tr>
        
          <td> <label for="m1">m1:</label> <input id="m1" type="text" style="width:50px" /></td>
          <td> <label for="b1">b1:</label> <input id="b1" type="text" style="width:50px" /></td>

          <td> <label for="slope2">m2:</label> <input id="m2" type="text" style="width:50px" /></td>
          <td> <label for="b2">b2:</label> <input id="b2" type="text" style="width:50px" /></td>

        </tr>
        <tr>
          <td><input id="btnGraph" type="button" value="Draw line" /></td>
        </tr>
      </table>
      
      <br />
      <h3>Graph</h3>
      
      <canvas id="Graph" style="background:aliceblue;border:1px solid #AFAFAF" width="600" height="600"></canvas>
     
    </fieldset>
    
      <script>
      function Graph(config) {
        // user defined properties
        this.canvas = document.getElementById(config.canvasId);
        this.minX = config.minX;
        this.minY = config.minY;
        this.maxX = config.maxX;
        this.maxY = config.maxY;
        this.unitsPerTick = config.unitsPerTick;

        // constants
        this.axisColor = '#aaa';
        this.font = '8pt Calibri';
        this.tickSize = 20;

        // relationships
        this.context = this.canvas.getContext('2d');
        this.rangeX = this.maxX - this.minX;
        this.rangeY = this.maxY - this.minY;
        this.unitX = this.canvas.width / this.rangeX;
        this.unitY = this.canvas.height / this.rangeY;
        this.centerY = Math.round(Math.abs(this.minY / this.rangeY) * this.canvas.height);
        this.centerX = Math.round(Math.abs(this.minX / this.rangeX) * this.canvas.width);
        this.iteration = (this.maxX - this.minX) / 1000;
        this.scaleX = this.canvas.width / this.rangeX;
        this.scaleY = this.canvas.height / this.rangeY;

        // draw x and y axis
        this.drawXAxis();
        this.drawYAxis();
      }

      Graph.prototype.drawXAxis = function() {
        var context = this.context;
        context.save();
        context.beginPath();
        context.moveTo(0, this.centerY);
        context.lineTo(this.canvas.width, this.centerY);
        context.strokeStyle = this.axisColor;
        context.lineWidth = 2;
        context.stroke();

        // draw tick marks
        var xPosIncrement = this.unitsPerTick * this.unitX;
        var xPos, unit;
        context.font = this.font;
        context.textAlign = 'center';
        context.textBaseline = 'top';

        // draw left tick marks
        xPos = this.centerX - xPosIncrement;
        unit = -1 * this.unitsPerTick;
        while(xPos > 0) {
          context.moveTo(xPos, this.centerY - this.tickSize / 2);
          context.lineTo(xPos, this.centerY + this.tickSize / 2);
          context.stroke();
          context.fillText(unit, xPos, this.centerY + this.tickSize / 2 + 3);
          unit -= this.unitsPerTick;
          xPos = Math.round(xPos - xPosIncrement);
        }

        // draw right tick marks
        xPos = this.centerX + xPosIncrement;
        unit = this.unitsPerTick;
        while(xPos < this.canvas.width) {
          context.moveTo(xPos, this.centerY - this.tickSize / 2);
          context.lineTo(xPos, this.centerY + this.tickSize / 2);
          context.stroke();
          context.fillText(unit, xPos, this.centerY + this.tickSize / 2 + 3);
          unit += this.unitsPerTick;
          xPos = Math.round(xPos + xPosIncrement);
        }
        context.restore();
      };

      Graph.prototype.drawYAxis = function() {
        var context = this.context;
        context.save();
        context.beginPath();
        context.moveTo(this.centerX, 0);
        context.lineTo(this.centerX, this.canvas.height);
        context.strokeStyle = this.axisColor;
        context.lineWidth = 2;
        context.stroke();

        // draw tick marks
        var yPosIncrement = this.unitsPerTick * this.unitY;
        var yPos, unit;
        context.font = this.font;
        context.textAlign = 'right';
        context.textBaseline = 'middle';

        // draw top tick marks
        yPos = this.centerY - yPosIncrement;
        unit = this.unitsPerTick;
        while(yPos > 0) {
          context.moveTo(this.centerX - this.tickSize / 2, yPos);
          context.lineTo(this.centerX + this.tickSize / 2, yPos);
          context.stroke();
          context.fillText(unit, this.centerX - this.tickSize / 2 - 3, yPos);
          unit += this.unitsPerTick;
          yPos = Math.round(yPos - yPosIncrement);
        }

        // draw bottom tick marks
        yPos = this.centerY + yPosIncrement;
        unit = -1 * this.unitsPerTick;
        while(yPos < this.canvas.height) {
          context.moveTo(this.centerX - this.tickSize / 2, yPos);
          context.lineTo(this.centerX + this.tickSize / 2, yPos);
          context.stroke();
          context.fillText(unit, this.centerX - this.tickSize / 2 - 3, yPos);
          unit -= this.unitsPerTick;
          yPos = Math.round(yPos + yPosIncrement);
        }
        context.restore();
      };

      Graph.prototype.drawEquation = function(equation, color, thickness) {


        var context = this.context;
        context.save();
        context.save();
        this.transformContext();

        context.beginPath();
        context.moveTo(this.minX, equation(this.minX));

        for(var x = this.minX + this.iteration; x <= this.maxX; x += this.iteration) {
          context.lineTo(x, equation(x));
        }

        context.restore();
        context.lineJoin = 'round';
        context.lineWidth = thickness;
        context.strokeStyle = color;
        context.stroke();
        context.restore();
      };

       Graph.prototype.drawLine = function(slope, yintercept, color, thickness) {

        console.log("Inside drawline");

        console.log("this.maxX: " + this.maxX + " this.maxY: " + this.maxY);

        var context = this.context;

        // draw x and y axis
        this.drawXAxis();
        this.drawYAxis();

        //context.clearRect(0, 0, this.canvas.width, this.canvas.height);

        context.save();
        context.save();
        this.transformContext();

        console.log("this.minX: " + this.minX);
        console.log("this.iteration: " + this.iteration);
        console.log("yintercept: " + yintercept);
        console.log("slope:" + slope);

        context.beginPath();
        context.moveTo(this.minX, slope * this.minX + yintercept);

        for(var x = this.minX + this.iteration; x <= this.maxX; x += this.iteration) {
          if (this.iteration % 200 == 0){
           console.log("x: " + x + " y: " + (slope * x + yintercept));
          }
          context.lineTo(x, slope * x + yintercept);
        }

        context.restore();
        context.lineJoin = 'round';
        context.lineWidth = thickness;
        context.strokeStyle = color;
        context.stroke();
        context.restore();
      };

      Graph.prototype.transformContext = function() {
        var context = this.context;

        // move context to center of canvas
        this.context.translate(this.centerX, this.centerY);

        /*
         * stretch grid to fit the canvas window, and
         * invert the y scale so that that increments
         * as you move upwards
         */
        context.scale(this.scaleX, -this.scaleY);
      };
    
    </script>
    
    <script>
     $(document).ready(function(){
     
      $("#btnGraph").click(function(){

        var m1,b1,m2,b2 = 0;

        m1 = parseFloat($("#m1").val());
        b1 = parseFloat($("#b1").val());
        m2 = parseFloat($("#m2").val());
        b2 = parseFloat($("#b2").val());



       
        var myGraph = new Graph({
         canvasId: 'Graph',
         minX: -10,
         minY: -10,
         maxX: 10,
         maxY: 10,
         unitsPerTick: 1
        });      

        myGraph.drawLine(m1, b1, 'blue', 3);

        myGraph.drawLine(m2, b2, 'red', 4);

        //myGraph.drawEquation(function(x) {
         //return 1 * x;
        //}, 'red', 3);

     
      });
     
     });
     
     </script>
    
  </body>

</html>