Category Archives: Meta

Project List

The main purpose of this blog is to keep track of my various projects and to document them. The end result would be a long list of projects with all the associated metadata which can then be summarised to make browsing them easier. The Project List is a meta project that does just that. Each project on my website gets a file named project.php associated with it that contains the meta information. They are then gathered by the Project List and can be sorted, filtered, and displayed online. I’ve also added static pages, blogs, and talks to the lists of resources. You can view the Project List to read more it. This was tied in to a major update of my website, as I had to create new preview images and new styles to match the new way of showing the projects. This will probably evolve further over time, but for now it’s fine.

Screenshot

Project List, showing some projects
Project List, showing some projects

Meta: New styles

One of the criticisms of my site was that there were too many 90 degree corners. CSS has had rounded corners for a long time, so I decided to finally use this to make things look a little nicer. Now I have rounded corners on most of my website, which makes things look a little more inviting and soft.

At the same time I decided to update the style in general, taking as much of the project-specific styles out of the main style sheets as possible. This cut down the size of the main file by about 50%. The transition was not entirely smooth, and there are some parts that need to be fixed (most notably on the Project 365 page) but this does not affect the readibility of the website, so it’s a low priority.

In addition to the rounded corners and separation of main from project based styles, I also introduced some style related variables. Now a single style can define a set of colours that can be used to reskin the entire website. This uses PHP variables and several source files, which are then compiled into a single, relatively small stylesheet.

Screenshot

So many rounded corners!
So many rounded corners!

Meta: Variable width style

As part of the updates to my website I dediced to add some variable width CSS styles. This allows the width of the main container to change in a fluid manner as the user changes the window size. This meant making two major changes.

First the menu had to change in size and content to respond to the changing window size. This now supports window widths down to 610 px. I may add support for window sizes down to 320 px, although to be honest most of the content on this website is not designed to be viewed on such a small device and supporting such antiquated devices seems counterproductive.

The second major change was changing the size of the canvas elements dynamically. This proved to be a bit tricky, since I wanted to keep the dimensions of the canvas the same, while changing the size on screen. It turns out the way to do this is to allow the canvas containter to change, and set the width of the canvas in terms of %. This has the added advantage of allowing the developer to use a higher resolution for the canvas, leading to superior graphics (at the expense of some more CPU time and RAM.)

Screenshots

The wide menu
The wide menu
The narrow menu
The narrow menu

Meta: Social media plugins

For a while I have been meaning to add social media plugins to my website. I finally got around to adding Facebook Like and Share buttons, as well Tweet buttons. I will add more as time goes by, but for the moment I want to keep things simple while I overhaul the rest of my website. The overall design and layout will probably change as the website evolves. Adding these buttons caused some problems with validation, so I had to add some inline JavaScript to overcome these problems.

Screenshot

The social media buttons, fitting snugly between the style changer and the tagline.
The social media buttons, fitting snugly between the style changer and the tagline.

Project management

Here’s something special for the 100th post! It’s project I use to manage projects.

This project was made to manage other projects. Each project has its own metadata which is parsed and arranged to give a useful summary of what the project does, how it was developed, and what I learned during development.

Links

Blog
GitHub repository

Overview

Each project has a python script that summarises the metadata, including the GitHub repository, the relevant links, a short description of the project, and details of implementation. Summaries are made for the Projects WordPress blog, and special links made for the PHP pages for projects that are hosted on my website. The scripts are organised so that they iterate over all projects (currently 83 and growing) at once, allowing a summary of all projects to be formulated.

This project management will eventually be extended to include icons, images, and other useful information. So far this has mostly be used to allow me to catalogue my existing projects in my spare time, which has taken ~15 months, but now that is nearly complete my needs will change, so I will add new functionalities.

Challenges

Challenge: This project needs to be versatile enough to handle a lot metadata about many projects.
Solution: In order to accommodate so many projects I had to create a new top level directory in my user space which is arranged hierarchically by category to contain all the projects. The choice to use python is motivated by the need to have a project management system that is versatile enough to handle a wide divrersity of projects with different metadata. (Resolved.)
This is how the sausage is made...
This is how the sausage is made…

Code update: Tetris

The first major Javascript project I took on was a clone of Tetris. Every time I look back at it I’m actually surprised an impressed at how many features it has. For a first project it has DOM manipulation, httpxml requests, timeout functions, cookies, audio, and a MySQL backend. After making the Tetris clone I then tried to make Pentris, which is the same game but with five blocks per piece. In going from Tetris to Pentris I made some interesting optimisations.

The Tetris clone
The Tetris clone

For example, here is the function tryRotate:

function tryRotate(){
  switch(color){
    case "red":
    switch(orientation){
      case 0:
      case 2:
        rotatedC[0] = C[0] + 1 ; rotatedR[0] = R[0] + 0 ;
        rotatedC[1] = C[1] + 0 ; rotatedR[1] = R[1] + 1 ;
        rotatedC[2] = C[2] - 1 ; rotatedR[2] = R[2] + 0 ;
        rotatedC[3] = C[3] - 2 ; rotatedR[3] = R[3] + 1 ;
      break ;
      case 1:
      case 3:
        rotatedC[0] = C[0] - 1 ; rotatedR[0] = R[0] + 0 ;
        rotatedC[1] = C[1] + 0 ; rotatedR[1] = R[1] - 1 ;
        rotatedC[2] = C[2] + 1 ; rotatedR[2] = R[2] + 0 ;
        rotatedC[3] = C[3] + 2 ; rotatedR[3] = R[3] - 1 ;
      break ;
    }
    break ;
    
    case "green":
    switch(orientation){
      case 0:
        rotatedC[0] = C[0] + 1 ; rotatedR[0] = R[0] + 1 ;
        rotatedC[1] = C[1] + 1 ; rotatedR[1] = R[1] - 1 ;
        rotatedC[2] = C[2] + 0 ; rotatedR[2] = R[2] + 0 ;
        rotatedC[3] = C[3] - 1 ; rotatedR[3] = R[3] + 1 ;
      break ;
      case 1:
        rotatedC[0] = C[0] - 1 ; rotatedR[0] = R[0] + 1 ;
        rotatedC[1] = C[1] + 1 ; rotatedR[1] = R[1] + 1 ;
        rotatedC[2] = C[2] + 0 ; rotatedR[2] = R[2] + 0 ;
        rotatedC[3] = C[3] - 1 ; rotatedR[3] = R[3] - 1 ;
      break ;
      case 2:
        rotatedC[0] = C[0] - 1 ; rotatedR[0] = R[0] - 1 ;
        rotatedC[1] = C[1] - 1 ; rotatedR[1] = R[1] + 1 ;
        rotatedC[2] = C[2] + 0 ; rotatedR[2] = R[2] + 0 ;
        rotatedC[3] = C[3] + 1 ; rotatedR[3] = R[3] - 1 ;
      break ;
      case 3:
        rotatedC[0] = C[0] + 1 ; rotatedR[0] = R[0] - 1 ;
        rotatedC[1] = C[1] - 1 ; rotatedR[1] = R[1] - 1 ;
        rotatedC[2] = C[2] + 0 ; rotatedR[2] = R[2] + 0 ;
        rotatedC[3] = C[3] + 1 ; rotatedR[3] = R[3] + 1 ;
      break ;
    }
    break ;
    
    case "blue":
    switch(orientation){
      case 0:
        rotatedC[0] = C[0] + 1 ; rotatedR[0] = R[0] + 0 ;
        rotatedC[1] = C[1] + 0 ; rotatedR[1] = R[1] + 1 ;
        rotatedC[2] = C[2] - 1 ; rotatedR[2] = R[2] + 2 ;
        rotatedC[3] = C[3] - 2 ; rotatedR[3] = R[3] + 1 ;
      break ;
      case 1:
        rotatedC[0] = C[0] + 1 ; rotatedR[0] = R[0] + 1 ;
        rotatedC[1] = C[1] + 0 ; rotatedR[1] = R[1] + 0 ;
        rotatedC[2] = C[2] - 1 ; rotatedR[2] = R[2] - 1 ;
        rotatedC[3] = C[3] + 0 ; rotatedR[3] = R[3] - 2 ;
      break ;
      case 2:
        rotatedC[0] = C[0] - 1 ; rotatedR[0] = R[0] + 1 ;
        rotatedC[1] = C[1] + 0 ; rotatedR[1] = R[1] + 0 ;
        rotatedC[2] = C[2] + 1 ; rotatedR[2] = R[2] - 1 ;
        rotatedC[3] = C[3] + 2 ; rotatedR[3] = R[3] + 0 ;
      break ;
      case 3:
        rotatedC[0] = C[0] - 1 ; rotatedR[0] = R[0] - 2 ;
        rotatedC[1] = C[1] + 0 ; rotatedR[1] = R[1] - 1 ;
        rotatedC[2] = C[2] + 1 ; rotatedR[2] = R[2] + 0 ;
        rotatedC[3] = C[3] + 0 ; rotatedR[3] = R[3] + 1 ;
      break ;
    }
    break ;
    
    case "cyan":
    switch(orientation){
      case 0:
        rotatedC[0] = C[0] - 1 ; rotatedR[0] = R[0] + 2 ;
        rotatedC[1] = C[1] + 0 ; rotatedR[1] = R[1] + 1 ;
        rotatedC[2] = C[2] + 1 ; rotatedR[2] = R[2] + 0 ;
        rotatedC[3] = C[3] + 0 ; rotatedR[3] = R[3] - 1 ;
      break ;
      case 1:
        rotatedC[0] = C[0] - 1 ; rotatedR[0] = R[0] - 1 ;
        rotatedC[1] = C[1] + 0 ; rotatedR[1] = R[1] + 0 ;
        rotatedC[2] = C[2] + 1 ; rotatedR[2] = R[2] + 1 ;
        rotatedC[3] = C[3] + 2 ; rotatedR[3] = R[3] + 0 ;
      break ;
      case 2:
        rotatedC[0] = C[0] + 1 ; rotatedR[0] = R[0] - 1 ;
        rotatedC[1] = C[1] + 0 ; rotatedR[1] = R[1] + 0 ;
        rotatedC[2] = C[2] - 1 ; rotatedR[2] = R[2] + 1 ;
        rotatedC[3] = C[3] + 0 ; rotatedR[3] = R[3] + 2 ;
      break ;
      case 3:
        rotatedC[0] = C[0] + 1 ; rotatedR[0] = R[0] + 0 ;
        rotatedC[1] = C[1] + 0 ; rotatedR[1] = R[1] - 1 ;
        rotatedC[2] = C[2] - 1 ; rotatedR[2] = R[2] - 2 ;
        rotatedC[3] = C[3] - 2 ; rotatedR[3] = R[3] - 1 ;
      break ;
    }
    break ;
    
    case "magenta":
    switch(orientation){
      case 0:
      case 2:
        rotatedC[0] = C[0] - 1 ; rotatedR[0] = R[0] + 0 ;
        rotatedC[1] = C[1] + 0 ; rotatedR[1] = R[1] + 1 ;
        rotatedC[2] = C[2] + 1 ; rotatedR[2] = R[2] + 0 ;
        rotatedC[3] = C[3] + 2 ; rotatedR[3] = R[3] + 1 ;
      break ;
      case 1:
      case 3:
        rotatedC[0] = C[0] + 1 ; rotatedR[0] = R[0] + 0 ;
        rotatedC[1] = C[1] + 0 ; rotatedR[1] = R[1] - 1 ;
        rotatedC[2] = C[2] - 1 ; rotatedR[2] = R[2] + 0 ;
        rotatedC[3] = C[3] - 2 ; rotatedR[3] = R[3] - 1 ;
      break ;
    }
    break ;
    
    case "yellow":
    switch(orientation){
      case 0:
      case 1:
      case 2:
      case 3:
        rotatedC[0] = C[0] + 0 ; rotatedR[0] = R[0] + 0 ;
        rotatedC[1] = C[1] + 0 ; rotatedR[1] = R[1] + 0 ;
        rotatedC[2] = C[2] + 0 ; rotatedR[2] = R[2] + 0 ;
        rotatedC[3] = C[3] + 0 ; rotatedR[3] = R[3] + 0 ;
      break ;
    }
    break ;
    
    case "white":
    switch(orientation){
      case 0:
      case 2:
        rotatedC[0] = C[0] + 1 ; rotatedR[0] = R[0] - 1 ;
        rotatedC[1] = C[1] + 0 ; rotatedR[1] = R[1] + 0 ;
        rotatedC[2] = C[2] - 1 ; rotatedR[2] = R[2] + 1 ;
        rotatedC[3] = C[3] - 2 ; rotatedR[3] = R[3] + 2 ;
      break ;
      case 1:
      case 3:
        rotatedC[0] = C[0] - 1 ; rotatedR[0] = R[0] + 1 ;
        rotatedC[1] = C[1] + 0 ; rotatedR[1] = R[1] + 0 ;
        rotatedC[2] = C[2] + 1 ; rotatedR[2] = R[2] - 1 ;
        rotatedC[3] = C[3] + 2 ; rotatedR[3] = R[3] - 2 ;
      break ;
    }
  break ;
  }
  for(var i=0 ; i<4 ; i++){
    if(rotatedR[i]>nRows) return false ;
    if(rotatedR[i]<1)     return false ;
    if(rotatedC[i]>nCols) return false ;
    if(rotatedC[i]<1)     return false ;
    var skip = 0 ;
    for(var j=0 ; j<4 ; j++){ if(rotatedR[i]==R[j] && rotatedC[i]==C[j]) skip = 1 ; }
    if(skip==0){
      var className = getClass(rotatedR[i],rotatedC[i]) ;
      if(className!="empty") return false ;
    }
  }
  for(var i=0 ; i<4 ; i++){
    set(R[i],C[i], "empty") ;
    R[i] = rotatedR[i] ;
    C[i] = rotatedC[i] ;
  }
  for(var i=0 ; i<4 ; i++){
    set(R[i],C[i], color) ;
  }
  orientation++ ;
  if(orientation==4) orientation = 0 ;
}

Compare that to the streamlined version I had for Pentris:

function tryRotate(){
  rotatePiece() ;
  for(var i=0 ; i<n ; i++){
    if(rotatedR[i]>nRows) return false ;
    if(rotatedR[i]<1)     return false ;
    if(rotatedC[i]>nCols) return false ;
    if(rotatedC[i]<1)     return false ;
    var skip = 0 ;
    for(var j=0 ; j<n ; j++){ if(rotatedR[i]==R[j] && rotatedC[i]==C[j]) skip = 1 ; }
    if(skip==0){
      var className = getClass(rotatedR[i],rotatedC[i]) ;
      if(className!="empty") return false ;
    }
  }
  for(var i=0 ; i<n ; i++){
    set(R[i],C[i], "empty") ;
    R[i] = rotatedR[i] ;
    C[i] = rotatedC[i] ;
  }
  for(var i=0 ; i<n ; i++){ set(R[i],C[i], color) ;}
}
function rotatePiece(){
  for(var i=0 ; i<n ; i++){
    rotatedR[i] =  (Y) + C[i] - (X) ;
    rotatedC[i] =  (X) - R[i] + (Y) ;
  }
}

The optimisation here is quite impressive, and very much needed when you consider that the move from Tetris to Pentris means moving from five pieces of four blocks each, to eighteeen pieces of five blocks each, with the space needed for ratotions scaling from sixteen to twenty five.

In the first function the different scenarios are all checked explicitly, whereas in the second function the temporary piece is rotated and then collisions checked, before updating the main piece. The amount of code when from 82 to 27 lines, and in doing so became safer and more easily extendible.

In a similar vein, here's the updatePreview function, first in Tetris:

function updatePreview(){
  for(var i=1 ; i<3 ; i++){ for(var j=1 ; j<5 ; j++){ setPreview(i, j, "empty") ; } }
  switch(nextPiece){
    case 1:
      setPreview(1, 2, "red") ;
      setPreview(1, 3, "red") ;
      setPreview(2, 3, "red") ;
      setPreview(2, 4, "red") ;
      break ;
    case 2:
      setPreview(1, 3, "green") ;
      setPreview(2, 2, "green") ;
      setPreview(2, 3, "green") ;
      setPreview(2, 4, "green") ;
      break ;
    case 3:
      setPreview(1, 1, "blue") ;
      setPreview(1, 2, "blue") ;
      setPreview(1, 3, "blue") ;
      setPreview(2, 3, "blue") ;
      break ;
    case 4:
      setPreview(1, 3, "cyan") ;
      setPreview(1, 2, "cyan") ;
      setPreview(1, 1, "cyan") ;
      setPreview(2, 1, "cyan") ;
      break ;
    case 5:
      setPreview(1, 3, "magenta") ;
      setPreview(1, 2, "magenta") ;
      setPreview(2, 2, "magenta") ;
      setPreview(2, 1, "magenta") ;
      break ;
    case 6:
      setPreview(1, 2, "yellow") ;
      setPreview(1, 3, "yellow") ;
      setPreview(2, 3, "yellow") ;
      setPreview(2, 2, "yellow") ;
      break ;
    case 7:
      setPreview(2, 1, "white") ;
      setPreview(2, 2, "white") ;
      setPreview(2, 3, "white") ;
      setPreview(2, 4, "white") ;
      break ;
  }
}

Second in Pentris:

function updatePreview(){
  for(var i=1 ; i<4 ; i++){ for(var j=1 ; j<6 ; j++){ setPreview(i, j, "empty") ; } }
  var x = new Array(n) ;
  var y = new Array(n) ;
  var piece = getPiece(nextPiece) ;
  var counter = 0 ;
  for(var i=0 ; i<n ; i++){
    for(var j=0 ; j<n ; j++){
      if(piece[i][j]==1){
        x[counter] = i   ;
        y[counter] = j+1 ;
        counter++ ;
      }
    }
  }
  for(var i=0 ; i<n ; i++){setPreview(x[i],y[i],colors[nextPiece-1]);}
}

Such an economy of code! One of my favourite parts of code is refactoring existing blocks of code into something more elegant and effiienct. The original code, as a first attempt, and a first adventure in serious Javascript was necessarily messy and in need of a good clean, so it's no surprise that so many improvements could be had.

I've been putting off resolving these differences for a very long time, partly because the games work as they are and there's no need to fix something that's not broken, and partly because I wanted to keep some legacy code around to remind myself of how it all started. I've archived the original code and now I'm bringing the Tetris, Pentris, and Tritris into line with each other to make all three games share a single code base.

Recycling old solutions

A few years ago I found myself in need of a simple program to make pixel art. At the time I had access to the PHP suite of graphical libraries, and the ability to manipulate the HTML DOM, so armed with these tools I put together a simple tool that would allow me to create small images online, which I called the Painter. It was not particularly efficient, but it got the job done, and did so for free. (Now that the HTML canvas is supported pretty much everywhere that tools needs to be rewritten for the latest generation of browser technology.) During the development of this tool I found myself wanting to solve the following problem: Given a two dimensional rectangular array of squares, how can I find all squares that reside within a given boundary? It’s a fairly straightforward problem, but one that requires keeping track of two lists of squares, and one that should scale, as far as possible, with \(n^2\) or lower. After some head scratching and experimenting with a few lines of code I found the solution I needed, tweaked it a bit, and was fairly impressed with its elegance.

Filling complex shapes with the Painter
Filling complex shapes with the Painter

Then a few years later I was talking with my current boss and she said something along the lines of “You shouldn’t take on these extra projects, they take up your time and distract you.” It’s a fair point that with over 50 coding projects, and about a dozen more nebulous and time consuming projects, I have a lot on my plate, but on the other hand they serve some very useful purposes that I can’t get elsewhere. One of the most useful contributions they give is that they are often the first testing ground for solving problems. The lessons I learn in Javascript, PHP, and python are often directly applicable to my physics work.

The CMS Preshower in real life (Image: Anna Pantelia/CERN)
The CMS Preshower in real life (Image: Anna Pantelia/CERN)

A few months ago I found myself having to deal with the CMS Preshower system. I wanted to make a map of the system to that I could visualise how it looked in three dimensions. The only things I really knew as that it was arranged in a grid of square components (each square component subsequently arranged in strips) and that I had a tool where if I could access a single square I could access all of its neighbours. Since the physical extent of the preshower system gave me an obvious boundary I found myself with a very familiar sounding problem… Without even needing to look up the code from the Painter tool I wrote down the solution in a few lines and in about 10 minutes. What may have taken me a few hours of framing the question, determining how to find the solution, and subsequently implement it was reduced to a trivially solved problem with a minimal footprint of CPU time and memory. This later lead on to the development of the \(\eta-\phi\) map, which is still under development and may eventually lead to more breakthroughs for the CMS experiment.

The end result, a preshower map!
The end result, a Preshower map!

I make it a point to not invest much time in a project unless it’s going to teach me a new skill or show me how a new technology or feature works. By exploring what was possible with a few rudimentary tools I was able to unwittingly give myself the solution to a real world problem that saved a lot of headaches and time. Whether the extra projects are worth the extra time is a different discussion entirely (I personally think they are, and also think that what I learn in physics programming today I can use in private enterprise tomorrow) and one that deserves some serious thought. For now I am happy that I have a huge pool of experience to draw on that only keeps growing in time.

HTML validation problems

One of the most frustrating parts of keeping my website up to date is keeping up with the latest technology at the same time as keeping the HTML valid. Recently I’ve started to add Facebook preview images to some of my pages, but in doing so I had to use the OpenGraph meta tags which invalidated my HTML. Finding the solution wasn’t easy, as I had to mess with the doctype tag, which introduced conflicts with my script tags. I had previously dropped the type="text/javascript attribute, which was optional in HTML 5, but had to reinstate them in order to validate with the OpenGraph tags.

No matter how much we try to keep these changes under control there is no denying that HTML standards are for the most part an accumulation of incremental bug fixes designed to keep up with the latest capabilities without breaking things too much. I might start to add automatic validation to my website to catch these problems as soon as they arise.

My MySQL is installed!

Last week I was involved in a rather delicate project that required AJAX requests with a MySQL backend. The problem I faced was that I would be on the Eurostar for two hours without any internet connection, and needed to develop the project in that time. With minutes to spare I managed to complete a MySQL isntallation and get PHP to successfully talk to it, meaning that I now have a functioning server installed on my laptop and it can do everything my webspace can do!

PHP and MySQL, playing nicely together
PHP and MySQL, playing nicely together

This came shortly before a scare related to the Yosemite upgrade. Overnight I allowed my laptop to upgrade, and the following morning, a couple of hours before I was due to present my latest project, I found that apache was down, PHP was down, my Sites folder was not accessible, and MySQL stopped working again. After a slighlty panicked 30 minutes I managed to have everything under control and all was as it should be. It’s stressful having all the pieces fall into place, and then fall apart again within a matter of hours. In spite of that, my laptop based web server is more versatile than it was before and this has come at just the right time. I may need to migrate my webspace to a new server soon, and being able to test on my laptop will help ease that process considerably.