The Journal

This journal is a development blog for my Final Year Project at the University of Kent. The project is to create an interactive 3D map of the University of Kent campus, using Three.js and WebGL. The application will allow users to explore a scale model of the campus while interacting with elements to reveal lots of different information.

I will use this journal to keep track of my research, design ideas and technical progress throughout the project. This will help me to organise my thoughts and get feedback from possible users of the application.

The final completed project can be seen at 3d.eda.kent.ac.uk

Final Map UI

final

The final user interface for the map has come a long way from initial concepts. The most important aspect of the design was usability. This had to be an application that is intuitive to use and above all easy to control. This guided my design decisions and helped me to refine the choices I made.

final2

Every detail has been carefully thought through and a consistent style flows throughout the application. Light, dark and colour have all been used for very specific purposes in order to split up content and prompt users to make decisions.

final3

Home Page Final Design

After a week of design and coding, the project home page has been completed. The idea behind the design was to display a large amount of information coherently in a single page. Splitting the content up into panels helps to separate the ideas and notify the user that the subject matter has changed.

The main purpose of the page is to direct the user to the main application. The first elements that are visible help to quickly describe the project and then guide the user to click a large call-to-action button.

The rest of the page content is then left available to people who wish to learn more about the project or to persuade those who have not already viewed the main application to do so. Animating the visibility of elements helps to keep the users attention and add fluidity to the scrolling process.

homepage

 

This page was created using a great little plugin called scrollReveal.js

Admin Page Final Design

The admin and content management section of the website is now fully complete which allows an admin user to edit much of the content displayed on the map.

admin

The design uses many similar elements to the main map application in order to unify the two areas of the site. This can be seen in the navigation bar and left hand navigation. The overall design is focussed on the content first meaning that I have taken extra care with spacing and legibility throughout.

admin

Help Pages Final Design

help

The help and information pages were quite a simple design as the focus is on the content rather than design. Using the same navigation bar as the rest of the application helps to unify the pages and stops the user becoming confused or lost. Using a simple left-aligned secondary navigation again helps give the user context and specifically sets out the content of these pages.

I chose to use a dark background colour to separate the content from the navigation bar and also to give good contrast against the text. The dark colour also adds to the formality of the pages.

pages

Social Page Final Design

The social page is a major part of the application and so needed to fit in seamlessly with the design of the main map page. The same top navigation bar has been used to keep this consistency and familiarity, along with the left hand navigation providing secondary functions.

The layout of individual tweets and Instagram photos was the first major design decision. I chose to use a masonry style layout to allow the user to see as much content as possible at once, while still keeping a structure that could flow to fit any screen size. This layout also allowed me to easily add sorting and filtering to these items, which gives a massive improvement to usability.

social

The dark background gives depth to the page and also helps to separate each individual item making the content more easy to read. Once again the same colour scheme was used to style the tweets, using the blue for links and as a background colour for the Instagram photo hoverable captions.

Gathering Event Data from UKC Website

As a secondary feature on the main map page I wanted to add an event feed showing the latest events from the University of Kent. To do this I had to scrape the events page on the university website for the data I wanted and display it nicely styled on the map page. Originally I wanted to have access to the Kent database but I was not allowed to do this for security reasons.

The PHP function below loads in the HTML page from the university website and stores it as a document. This document can then be traversed using Xpath just like an XML document.

 

$html = file_get_contents('http://www.kent.ac.uk/calendar/'); //get the html returned from the following url

$kent_doc = new DOMDocument();

libxml_use_internal_errors(TRUE);

if(!empty($html)){ //if any html is actually returned

  $kent_doc->loadHTML($html);
  libxml_clear_errors(); //remove errors for yucky html

  $kent_xpath = new DOMXPath($kent_doc);

  $event = $kent_xpath->query('//div[contains(concat(" ", normalize-space(@class), " "), " event ")]');

I then loop through the returned data and add it into HTML containers. I then output the styled result to the map page wherever it is needed.

if($event->length > 0){
      foreach($event as $row){
		  $titlepath = $kent_xpath->query('.//div[@class="title"]', $row);
		  $title = $titlepath->item(0)->nodeValue;
		  $timepath = $kent_xpath->query('.//div[@class="time"]', $row);
		  $time = $timepath->item(0)->nodeValue;
		  $linkpath = $kent_xpath->query('.//div[@class="title"]/a/@href', $row);
		  $link = $linkpath->item(0)->nodeValue;
		  $linktitlepath = $kent_xpath->query('.//div[@class="categories"]/a', $row);
		  $linktitle = $linktitlepath->item(0)->nodeValue;

I have also added a check to stop the HTML displaying if there was an error retrieving the data from the university website. This is to stop errors showing if the way the data is displayed or the URL of the webpage is changed at the university’s end.

dribbble

Render of UKC model

📷

Search Function and Auto-Complete

A search function is a very important feature within the project. Allowing the user to easily find information is at the core of what the project is about.

In order to setup this function I first loaded in the XML file which contains all of the building data used on the map. When the load is complete I call a new function called parseXML() which places each of the building titles and room names into an array ready for the autocomplete function.

$.ajax({
	type: "GET",
	url: "assets/buildings.xml",
	dataType: "xml",
	success: parseXml,
	complete: setupAC,
	failure: function(data) {
		alert("XML File could not be found");
		}
});

function parseXml(xml){
	$(xml).find("building").each(function(){
		var spaces = $(this).attr("label").replace(/_/g, ' ');
		buildings.push(spaces);
		$(this).find('rooms').children().each(function(){
			var room = $(this).text();
			buildings.push(room);
		});
	});	
}

The function setupAC() then uses the JQuery UI autocomplete functionality to match what the user is typing and submit the search query when the user selects one of the options.

function setupAC() {
	$("input#searchBox").autocomplete({
		source: function( request, response ) {
			var matcher = new RegExp( "^" + $.ui.autocomplete.escapeRegex( request.term ), "i" );
			response( $.grep( buildings, function( item ){
				return matcher.test( item );
			}) );
		},
		minLength: 1,
		select: function(event, ui) {
		        $("input#searchBox").val(ui.item.value);
			$("#searchForm").submit();
		}
	});
}

Once the form is submitted the data is sent to the function below which animates the 3D camera to the location the user has searched for and reveals the popup information for that building. If a user searches for a room then they are taken to the building which houses that room instead.

function searchResult(searchvalue) {

	var foo = searchvalue.searchString.value;
	if(jQuery.inArray( foo, buildings ) != -1){
		$('#searchBox').css('color','#666');
		$('.search-error').hide();
		var dashes = foo.replace(/\s/g, '_');

		var room = $(xml).find('room:contains('+foo+')');

		if( !room.length){
			$('#modalfront, #modalback').empty();
			$("#modalpanel").removeClass('fadeOutUp fadeInDown opaque');
			$(".card").removeClass('flipped');
			closeTweet();
			modal = scene.getObjectByName( dashes, true );
			placeMarker(modal);
		}else{
			var rooms = $(room).parent()
			var building = rooms.parent().attr('label');
			$('#modalfront, #modalback').empty();
			$("#modalpanel").removeClass('fadeOutUp fadeInDown opaque');
			$(".card").removeClass('flipped');
			closeTweet();
			modal = scene.getObjectByName( building, true );
			placeMarker(modal);
		}
	}else{
		$('#searchBox').css('color','#e83333');
		$('.search-error a').css('color','#e83333');
	}

	return false;
}

Preloader GIF Design

Instead of simply displaying a loading percentage or a 2D graphic while the main application was loading, I wanted to reinforce that the map is 3D by using a small 3D GIF animation.

splash

 

I rendered out a small portion of the map including the library as a main landmark. This then loops through a full rotation to give the sense of an endless animation. Below the GIF I will display a loading percentage to notify the user of current progress.

Creating Transport Routes Using Cylinder Geometry

Originally I used LineGeometry to draw lines between an array of points to make up the routes of buses around the campus. However this caused problems with Windows computers as I could not control the line width, meaning that lines would only be 1px wide and much harder to see.

To combat this I decided to create a function to draw a cylinder between the points of the array instead of using the Three.js LineGeometry. This meant that I could control the width of the cylinder and it would look the same on both Windows and Mac.

I simply loop through the array of points and feed the previous and next points into the new function. The function then draws a cylinder between the two points with a specified colour and adds the cylinder to an array to be displayed later. I have added a check to change the width of the cylinder for footpaths (coloured #333333) to differentiate between them and the bus and cycle routes.

function drawCylinder(vstart, vend, name, colour){
    var HALF_PI = Math.PI * .5;
    var distance = vstart.distanceTo(vend);
    var position  = vend.clone().add(vstart).divideScalar(2);

    var material = new THREE.MeshBasicMaterial({ color: colour, transparent: true, opacity: 0.8 });
	if(colour == 0x333333){
    	var cylinder = new THREE.CylinderGeometry(0.5,0.5,distance,6,6,false);
	}else{
		var cylinder = new THREE.CylinderGeometry(1,1,distance,6,6,false);
	}

    var orientation = new THREE.Matrix4();
    var offsetRotation = new THREE.Matrix4();
    var offsetPosition = new THREE.Matrix4();
    orientation.lookAt(vstart,vend,new THREE.Vector3(0,1,0));
    offsetRotation.makeRotationX(HALF_PI);
    orientation.multiply(offsetRotation);
    cylinder.applyMatrix(orientation)

    var mesh = new THREE.Mesh(cylinder,material);
    mesh.position=position;
	mesh.visible = false;
    name.push(mesh);
	group.add(mesh);
}

Restyling and Recreating the Map Image

After a lot of thought I have decided to create my own version of the map image that I will be using. The previous map is taken from Open Street Map and was very useful for the initial purpose of positioning the building models and getting a sense of scale. However, the style of the map image is not in-keeping with the overall style of the project and also contains information that I do not want to be included

oldmap

 

These problems have led me to create my own version of the map in Photoshop where I have adjusted the colours and smoothed out the colour contrast. Doing this has also let me have control over exactly what is displayed on the map. For example, I have removed road names as I plan to add them in later as a 3D sprite.

map

Colour Swatches

colours

I have now finalised the four main colours that I will be using throughout the design of this project. The idea is to have two base colours to separate content, which in this case are #f6f6f6 and #242b31. Along with two highlight colours which indicate links, titles or other important information.

The blue has been chosen to link the project to the University of Kent. Although the shade is much lighter than the blue used in the Kent logo, it gives a hint of the Kent colour scheme while at the same time being different enough to show the project as a unique experience.

To contrast against the blue I chose a pale shade of orange to use for call-to-action buttons and other links. The contrast makes these pieces of information stand out against both the background colours and the blue highlight colour.

Screen shot 2014-04-05 at 18.23.58

Google maps button inspiration

📷

UI progress prototype

I have lightened up the user interface of the map page significantly to make the information much more readable

recent-posts

Map popup inspiration

📷

Position tracking for HTML elements on top of a 3D canvas

Firstly I set up variables for each type of modal box I would like to position on the map.

var modal;

These variables will hold the position of a clicked object, which is brought in from a separate ‘onClick’ function. The positionTrackingOverlay function is then constructed as below:

function positionTrackingOverlay()
{
    var visibleWidth, visibleHeight, p, v, percX, percY, left, top, 
        widthPercentage, heightPercentage;

    if(modal != null){

	modal.geometry.computeBoundingBox();
	var boundingBox = modal.geometry.boundingBox;
	var position = new THREE.Vector3();
	position.subVectors( boundingBox.max, boundingBox.min );
	position.multiplyScalar( 0.5 );
	position.add( boundingBox.min );
	position.applyMatrix4( modal.matrixWorld );

After checking that ‘modal’ is not empty, the function computes the bounding box of the selected object in order to find its position. This is then used below to project the 3D coordinates onto the 2D screen.

p = position.clone();

// projectVector will translate position to 2d
v = projector.projectVector(p, camera);

// translate our vector so that percX=0 represents
// the left edge, percX=1 is the right edge,
// percY=0 is the top edge, and percY=1 is the bottom edge.
percX = (v.x + 1) / 2;
percY = (-v.y + 1) / 2;

// scale these values to our viewport size
left = percX * WIDTH;
top = percY * HEIGHT;

I can then use the ‘WIDTH’ and ‘HEIGHT’ variables to calculate the position of the modal box as a percentage (this is so that its position will change on window resize).

	
widthPercentage = (left - $("#modalpanel").width() / 2) / WIDTH * 100;
heightPercentage = (top - $("#modalpanel").height()) / HEIGHT * 100;

Finally I use JQuery to update the css of the HTML element according to the position of the 3D object.

$("#modalpanel")
	.css('left', widthPercentage + '%')
	.css('top', heightPercentage + '%');

The function ‘positionTrackingOverlay()’ is called in the render loop so that it is constantly updated at runtime.

nav

Side navigation Inspiration from Dribbble

📷

Progress update of UI

A quick update on the Ui front so far. I’ve added a navigation bar at the top of the page for when I expand the application.

Thank you for taking the time to look over my most recent work. Please feel free to contact me for any further information, to hire me for a project or to simply have a good old chat.

Click here to hire me