Categories
A piece of Art as big as India

Clicking on the Sculpture

In order to allow interaction with the sculpture, users are going to have to be able to click on it in some way.

Luckily, A-Frame provides the Cursor component to allow this. In order to familiarise myself with the component, I decided to follow the example provided in the docs, which allows the user to click to change the colour of entities that they click on.

To begin with, I created a new pug file (collidingWithTerrainModelComponent.pug) to create my demo html page, using the example from the docs and my previously developed component pug file:

doctype html
html
 head
 meta(charset='utf-8')
 title Colliding with the Terrain Model Component Demo
 meta(name='description', content='Colliding with the Terrain Model Component Demo')
 script(src='build.js')
 link(href="style.css", rel="stylesheet", media="all")
 body
 a-scene(antialias='true')
 //Assets
 a-assets
 img(id='sky' src='firstPanorama/2016_10_18_FirstPanorama.JPG')
 //Camera
 a-entity(position='0 0 0' rotation='0 0 0')
 a-entity(camera look-controls wasd-controls)
 a-entity(cursor='fuse: true; fuseTimeout: 500'
 position='0 0 -1'
 geometry='primitive: ring'
 material='color: black; shader: flat')
 //Terrain
 a-entity(id='landscape' position='0 100 0' terrain-model='DEM: url(aframe-terrain-model-component/docs/Olympic-Peninsula/data/clipped-envi.bin); texture: url(aframe-terrain-model-component/docs/Olympic-Peninsula/data/olympic-texture.jpg); planeWidth: 287; planeHeight: 151; segmentsWidth: 287; segmentsHeight: 151; zPosition: 50;' material-side-modifier cursor-listener)
 //Sky
 a-sky(src='#sky')
 //Light
 a-entity(light='type: ambient;')

I created a new js file (cursor-listener.js), with the example code from the Cursor docs:

// Component to change to random colour on click.
AFRAME.registerComponent('cursor-listener', {
 init: function () {
 var COLOURS = ['red', 'green', 'blue'];
 this.el.addEventListener('click', function () {
 var randomIndex = Math.floor(Math.random() * COLOURS.length);
 this.setAttribute('material', 'color', COLOURS[randomIndex]);
 console.log('I was clicked!');
 });
 }
});

I also had to add the cursor-listener.js file to my main.js as a requirement:

require('cursor-listener.js');

I found that the demo was reporting a click, but it wasn’t changing the colour correctly, so I edited the cursor-listener.js file as follows, drawing upon the material-side-modifier.js component that I had developed previously. I renamed the file to cursor-listener-terrain.js as it was apparent that this component wasn’t going to work well with the way that the Terrain Model component was set up:

// Component to change to random colour on click, not functioning presently
AFRAME.registerComponent('cursor-listener-terrain', {

init: function () {
 this.el.addEventListener('click', function () {
 console.log('I was clicked!');
 if(this.el.getObject3D('terrain')){
 var COLOURS = ['red', 'green', 'blue'];
 var randomIndex = Math.floor(Math.random() * COLOURS.length);
 //this.setAttribute('material', 'color', COLOURS[randomIndex]);
 var terrain = this.el.getObject3D('terrain');
 terrain.material.color = COLOURS[randomIndex];
 console.log('I changed the colour!');
 }
 });
 }
});

In order to keep making progress, I decided to work with the Mountain Component that I had previously been working with. I renamed my previously made components to:

material-side-modifier-terrain-model.js
cursor-listener-terrain.js

And updated both the intial component demo and the cursor interaction demo’s to reflect the name changes.

I could then continue developing my colliding with Mountain Component demo and the component that would allow the initial colour changing interaction demo to occur. Because of more standard way that the Mountain component mesh and materials were set up, I found I could use the example code for the cursor listener exactly as in the documentation:

// Component to change to random color on click.
AFRAME.registerComponent('cursor-listener', {
  init: function () {
    var COLORS = ['red', 'green', 'blue'];
    this.el.addEventListener('click', function () {
      var randomIndex = Math.floor(Math.random() * COLORS.length);
      this.setAttribute('material', 'color', COLORS[randomIndex]);
      console.log('I was clicked!');
    });
  }
});

For the sake of completeness I created a demo of a similar interaction but using the Ocean component – I had to create a new modifier component to do that, but tried reuse my cursor-listener that I had used successfully with the Mountain component, unfortunately the Ocean component doesn’t seem to be identifying clicks. I filled the problem as an issue on the Ocean Component Github, and decided to keep working with the Mountain component.

A-Frame also has the Raycaster Component, so I decided to follow it’s tutorial to create a collider-check component, with the mountain component as it was the only component that had proved to be compatible with the cursor-listener component.

I created a new pug file (mountainComponentWithColliderCheck.pug) to to demonstrate the the newly created collider-check component. I also added the collider-check component to my main.js to that Browserify could do it’s magic. The resulting collider-check with Mountain component functioned as expected, as it output to the Javascript Console successfully when the cursor was over the Mountain component.

As the collider-check component was attaching a new listener to the Mountain Component itself:

AFRAME.registerComponent('collider-check', {
 dependencies: ['raycaster'],
 init: function () {
 this.el.addEventListener('raycaster-intersected', function () {
 console.log('Player hit something!');
 });
 }
});

I wanted to see if it would be possible to trigger the update() method of the Mountain Component. I created a new pug file (mountainComponentWithColliderCheckAndUpdate.pug) and component (collider-check-and-update.js):

AFRAME.registerComponent('collider-check-and-update', {
 dependencies: ['raycaster'],
 init: function () {
 this.el.addEventListener('raycaster-intersected', function () {
 console.log('Player hit something!');
 //console.log(this);
 //trying too call the update() method of the Mountain component to which this event listener is now attached
 update(); //this errors with Uncaught ReferenceError: update is not defined
 this.update(); //this doesn't error, but doesn't do anything
 this.el.update(); //this errors with Cannot read property 'update' of undefined
 });
 }
});

As can be seen in the comments above, none of my three methods for calling the update method of the Mountain component are currently functioning. I received a tip from DonMcCurdy on the A-Frame Slack:

@jgl: `el.components['component-name'].update()` should do the trick.

I also had to store a reference to the element before calling the callback, the complete code for collider-check-and-update.js is now:

AFRAME.registerComponent('collider-check-and-update', {
 dependencies: ['raycaster'],
 init: function () {
 var el = this.el; //tip from Don McCurdy, needed to store the element that this was called from
 this.el.addEventListener('raycaster-intersected', function () {
 el.components['mountain'].update(); //tip from Don McCurdy
 });
 }
});

You can try the demo for yourself here.

The interaction was rather clumsy, so I created a new .pug file (mountainComponentWithMouseAndTouchInteraction.pug) and added Touch and Mouse interaction to the entire page, so that clicking or touching anywhere would generate a new terrain – as well as adding a random colour generator for the color and shadowColor attributes of the Mountain component, and finally making the sunPosition attribute random too.

You can try that demo for yourself here.

The frankly amazing Dietrich Ayala created my first ever pull request for the project and added his getUserMedia() work to the project, to make the background panorama live on compatible (i.e. Firefox on Android) devices.

Categories
A piece of Art as big as India

New Panoramas from India

r0010023

The fantastic Anne Siegel has just sent over some panoramas direct from the British Council location in Delhi.

In order to display them all in one convenient location I decided to use the A-Frame guide to building with components, which conveniently is a panorama browser.

Along the way, I had to install K-Frame:

npm install kframe --save

Unfortunately, I found some problems with the tutorial, so I posted a question to Stack Overflow.

Kevin Ngo, the author of the package helpfully got back to me, and bumped the version of his package. I edited my package.json to reflect his change:

    "kframe": "^0.3.2",

But this resulted in the following error when I tried to run:

npm update

To update K-Frame, the package in question:

ForceMacbookProRetina:APieceOfArtAsBigAsIndia joel$ npm update
npm ERR! Darwin 16.1.0
npm ERR! argv "/usr/local/Cellar/node/6.7.0/bin/node" "/usr/local/bin/npm" "update"
npm ERR! node v6.7.0
npm ERR! npm  v3.10.7
npm ERR! code ETARGET
npm ERR! notarget No compatible version found: kframe@'>=0.3.2 <0.4.0'
npm ERR! notarget Valid install targets:
npm ERR! notarget 0.3.0
npm ERR! notarget
npm ERR! notarget This is most likely not a problem with npm itself.
npm ERR! notarget In most cases you or one of your dependencies are requesting
npm ERR! notarget a package version that doesn't exist.
npm ERR! Please include the following file with any support request:
npm ERR!     /Users/joel/Documents/Projects/jgl/BritishCouncil/APieceOfArtAsBigAsIndia/npm-debug.log

I put the version number back to 0.3.0 for safety. Kevin got back in touch and suggested installing the Event Set component individually, so I ran:

npm install aframe-event-set-component --save

Which resulted in the following output:

a_piece_of_art_as_big_as_india@1.0.0 /Users/joel/Documents/Projects/jgl/BritishCouncil/APieceOfArtAsBigAsIndia
├── aframe-event-set-component@3.0.1 
└─┬ kframe@0.3.0
 └── aframe-event-set-component@0.1.2

Could this be a conflict? I added the following line to my main.js:

require('aframe-event-set-component');

When I tried to view the page on my local server, I got the following errors in my Javascript console:

Uncaught Error: The component `event-set` has been already registered. Check that you are not loading two versions of the same component or two different components of the same name.(…)

So after looking up how to remove a locally installed package via npm, I removed K-Frame by typing:

npm uninstall kframe --save

This removed all of K-Frame, as well as it’s dependencies. I could verify this by checking my package.json file. I now needed to add both the Template Component, the Layout Component and the Animation Component individually:

npm install aframe-template-component --save
npm install aframe-layout-component --save
npm install aframe-animation-component --save

And adding both of them as requirements to my main.js:

require('aframe-template-component');
require('aframe-layout-component');
require('aframe-animation-component');

Resulted in no errors on my Javascript console!

I continued through the rest of the tutorial, creating a pair of new components (set-image.js and update-raycaster.js), and including them in my main.js:

require('./update-raycaster.js');
require('./set-image.js');

I consulted the GitHub for the tutorial several times, but in the end I got the page working, allowing for the browsing of all of Anne’s images inside A-Frame!

 

Categories
A piece of Art as big as India

Adding the sculpture to a panoramic image

Today I’ve been working on adding many different A-Frame components to the panoramic image I shot of my studio. First, I created a new .pug file combining the terrain-model component by Ben Pyrik with the studio panorama:

doctype html
html
 head
 meta(charset='utf-8')
 title A-Frame Terrain Model Component with Panoramic background
 meta(name='description', content='A-Frame Terrain Model Component with Panoramic background')
 script(src='build.js')
 link(href="style.css", rel="stylesheet", media="all")
 body
 a-scene(antialias='true')
 //Assets
 a-assets
 img(id='sky' src='firstPanorama/2016_10_18_FirstPanorama.JPG')
 //Camera
 a-entity(position='0 0 0' rotation='0 0 0')
 a-entity(camera look-controls wasd-controls)
 //Terrain
 a-entity(position='0 100 0' terrain-model='DEM: url(aframe-terrain-model-component/docs/Olympic-Peninsula/data/clipped-envi.bin); texture: url(aframe-terrain-model-component/docs/Olympic-Peninsula/data/olympic-texture.jpg); planeWidth: 287; planeHeight: 151; segmentsWidth: 287; segmentsHeight: 151; zPosition: 50;')
 //Sky
 a-sky(src='#sky')
 //Light
 a-entity(light='type: ambient; color: #0xeeeeee')

I added the demo my Github pages and took the following screenshot:

2016_11_01_panoramaandlandscapenotpositioned

I knew that I wanted the landscape to float in the sky above user’s heads, so I used the A-Frame Inspector to reposition the terrain model in the sky. Unfortunately, this had the side-effect of adding some glitches to the drawing of the terrain:

2016_11_01_panoramaandlandscapepositionedbutnotdrawingproperly

I suspect this is because A-Frame is expecting the terrain 3D model to be viewed from the other side. I decided to keep going with adding the other components I had found that could be good candidates for the interactive demo that I want to test with users in India.

The next component to try was Ocean, part of A-Frame Extras by Don McCurdy. I installed it via NPM as normal:

npm install aframe-extras --save

Then I added the component to main.js:

require('aframe');
require('aframe-terrain-model-component');
require('aframe-extras');

I then created a new .pug file, aFrameOceanComponentWithPanorama.pug:

doctype html
html
 head
 meta(charset='utf-8')
 title A-Frame Ocean Component with Panoramic background
 meta(name='description', content='A-Frame Ocean Component with Panoramic background')
 script(src='build.js')
 link(href="style.css", rel="stylesheet", media="all")
 body
 a-scene(antialias='true')
 //Assets
 a-assets
 img(id='sky' src='firstPanorama/2016_10_18_FirstPanorama.JPG')
 //Camera
 a-entity(position='0 0 0' rotation='0 0 0')
 a-entity(camera look-controls wasd-controls)
 //Ocean
 a-ocean(width='50' depth='50' density='40')
 //Sky
a-sky(src='#sky')
 //Light
 a-entity(light='type: ambient; color: #0xeeeeee')

That didn’t display correctly, so I decided to build back up from the static demo’s that Don provided with the component, before re-adding them to the panoramic image. After downloading them, I managed to get them working on my local server by editing the following lines of his Water demo:

 <script src="https://aframe.io/releases/0.3.0/aframe.min.js"></script>
 <script src="https://cdn.rawgit.com/donmccurdy/aframe-extras/v2.6.0/dist/aframe-extras.js"></script>

But while isn’t my inclusion of the same component into main.js via Browserify working? I soon found the error, after re-reading Don’s readme.md. I had to add the following lines to register the component within main.js:

var extras = require('aframe-extras');
// Register everything.
extras.registerAll();

I added the demo to my GitHub, and took the following screengrab, with a similar glitch as before:

2016_11_01_panoramaandoceanpositionedbutnotdrawingproperly

Finally, I tried the Mountain component by Kevin Ngo. Again installing via NPM:

npm install aframe-mountain-component --save

First duplicated the demo to local system, which worked fine, so I created a new pug file:

doctype html
html
 head
 meta(charset='utf-8')
 title A-Frame Mountain Component with Panoramic background
 meta(name='description', content='A-Frame Mountain Component with Panoramic background')
 script(src='build.js')
 link(href="style.css", rel="stylesheet", media="all")
 body
 a-scene(antialias='true')
 //Assets
 a-assets
 img(id='sky' src='firstPanorama/2016_10_18_FirstPanorama.JPG')
 //Camera
 a-entity(position='0 0 0' rotation='0 0 0')
 a-entity(camera look-controls wasd-controls)
 //Mountain, seems to be on a large scale!
 a-mountain(position='0 2000 0')
 //Sky
 a-sky(src='#sky')
 //Light
 a-entity(light='type: ambient; color: #0xeeeeee')

And added the following line to main.js:

require(‘aframe-mountain-component’);

To make sure that Browserify included the necessary JS files to make the webpage work. This resulted in the following demo and screenshot:

2016_11_01_panoramaandmountainpositionedbutnotdrawingproperly

After discussing my bug(s) on the fantastic A-Frame Slack it became clear that I need to edit the properties of the A-Frame Material which makes each entity an appearance. Specifically, I need to make the side property equal to double to render both sides of the mesh.

Bryvik helpfully made a new branch for his terrain-model component, that exposed the side property of the material, via the side property of the original three.js material that it uses.

As well as asking about my problem on the A-Frame Slack, I also browsed Stackoverflow for questions tagged with A-Frame. There I found a post about how to use JavaScript directly with A-Frame. The answer was written by the very helpful Kevin Ngo, but was written for the 0.2.0 release of A-Frame, not the latest stable 0.3.0. After consulting the docs for the 0.3.0 Entity I realised I also needed to work out how to write inline Javascript with Pug. Stackoverflow came to the rescue again – (Jade was the previous name of Pug) with a link to a Pug example of inline Javascript.

doctype html
html
 head
 meta(charset='utf-8')
 title A-Frame Terrain Model Component with Panoramic background
 meta(name='description', content='A-Frame Terrain Model Component with Panoramic background')
 script(src='build.js')
 link(href="style.css", rel="stylesheet", media="all")
 body
 a-scene(antialias='true' stats)
 //Assets
 a-assets
 img(id='sky' src='firstPanorama/2016_10_18_FirstPanorama.JPG')
 //Camera
 a-entity(position='0 0 0' rotation='0 0 0')
 a-entity(camera look-controls wasd-controls)
 //Terrain
 a-entity(id='landscape' position='0 100 0' terrain-model='DEM: url(aframe-terrain-model-component/docs/Olympic-Peninsula/data/clipped-envi.bin); texture: url(aframe-terrain-model-component/docs/Olympic-Peninsula/data/olympic-texture.jpg); planeWidth: 287; planeHeight: 151; segmentsWidth: 287; segmentsHeight: 151; zPosition: 50;')
 //Sky
 a-sky(src='#sky')
 //Light
 a-entity(light='type: ambient; color: #0xeeeeee')
 script.
 console.log("Hello world");
 var landscapeEl = document.querySelector('#landscape');
 console.log(landscapeEl);
console.log(landscapeEl.getAttribute('terrain-model'));

By looking in the Javascript console I could see that I was successfully printing “Hello World”, and setting the Landscape component to the variable landscapeEl. I could even make the Landscape visible or not by typing:

landscapeEl.setAttribute('visible', false);

or

landscapeEl.setAttribute('visible', true);

Into the console. I could also see that I was successfully displaying the public attributes of the Landscape component:

<a-entity id="landscape" position="0 100 0" terrain-model="DEM: url(aframe-terrain-model-component/docs/Olympic-Peninsula/data/clipped-envi.bin); texture: url(aframe-terrain-model-component/docs/Olympic-Peninsula/data/olympic-texture.jpg); planeWidth: 287; planeHeight: 151; segmentsWidth: 287; segmentsHeight: 151; zPosition: 50;" rotation="" scale="" visible=""></a-entity>

As well as the attributes of terrain-model attribute itself:

DEM: url(aframe-terrain-model-component/docs/Olympic-Peninsula/data/clipped-envi.bin); texture: url(aframe-terrain-model-component/docs/Olympic-Peninsula/data/olympic-texture.jpg); planeWidth: 287; planeHeight: 151; segmentsWidth: 287; segmentsHeight: 151; zPosition: 50;

– and side wasn’t one of them (I wasn’t using the branch of the Component that bryvik had made as I was trying to find a general method for changing the side property of any material in A-Frame. I realised that I needed to manipulate the three.js that underlies a-frame directly, which I could do by accessing Entity’s object3D property – which is a native three.js object. By adding the following

 var landscapeObject3D = landscapeEl.object3D;
 console.log(landscapeObject3D.parent);
 console.log(landscapeObject3D.children);

Via the console, I could then manipulate the THREE.MeshLambertMaterial side property (via THREE.mesh) directly, to get the mesh to draw correctly:

2016_11_03_partialsidewin

The next step is to do this programatically, but I’m running into a bug where I can type the command on the console to see the result, even trying different methods:

var landscapeEl = document.querySelector('#landscape');
 // Gaining access to the internal three.js object that the landscape component contains
 var landscapeObject3D = landscapeEl.object3D;
 console.log(landscapeObject3D.parent);
 console.log(landscapeObject3D.children);
 for(var i in landscapeObject3D.children) {
 //for all the children of the landscapeObject3D, change the material side property to THREE.DoubleSide aka 2
 landscapeObject3D.children[i].material.side = THREE.DoubleSide;
 }
 //this works when typed into the console, but not here programmatically, found here: http://stackoverflow.com/questions/18613295/how-to-set-a-texture-on-a-object3d-child-mesh
 landscapeObject3D.children[0].material.side = THREE.DoubleSide; 
 //this works when typed into console, but not here programmatically, found here: https://threejs.org/docs/#Reference/Materials/Material
 landscapeObject3D.traverse( function( node ) {
 if( node.material ) { 
 node.material.side = THREE.DoubleSide;
 }
 });
 //this also works when typed into the console, but not programatically, found here: http://stackoverflow.com/questions/16027131/three-js-how-to-make-double-sided-object

The A-Frame Slack came to the rescue, with pookage pointing out that I needed to call the code above only after the rest of the page had loaded, via the global JavaScript event handler: onLoad(). He also recommended the following the FunFunFunction YouTube Channel for general JavaScript learning.

 function changeMaterialSide(){
 //testing that I can print to the console
 console.log("A-Frame and the rest have loaded");
 //Gaining access to the landscape element via it's ID
 var landscapeEl = document.querySelector('#landscape');
 // Gaining access to the internal three.js object that the landscape component contains
 var landscapeObject3D = landscapeEl.object3D;
 //console.log(landscapeObject3D.parent);
 //console.log(landscapeObject3D.children);
 for(var i in landscapeObject3D.children) {
 //for all the children of the landscapeObject3D, change the material side property to THREE.DoubleSide aka 2
 landscapeObject3D.children[i].material.side = THREE.DoubleSide;
 }
 //this works when typed into the console, but not here programmatically, found here: http://stackoverflow.com/questions/18613295/how-to-set-a-texture-on-a-object3d-child-mesh
 landscapeObject3D.children[0].material.side = THREE.DoubleSide; 
 //this works when typed into console, but not here programmatically, found here: https://threejs.org/docs/#Reference/Materials/Material
 landscapeObject3D.traverse( function( node ) {
 if( node.material ) { 
 node.material.side = THREE.DoubleSide;
 }
 });
 //this also works when typed into the console, but not programatically, found here: http://stackoverflow.com/questions/16027131/three-js-how-to-make-double-sided-object
 }
 //Thanks to @pookage (http://www.beardeddevelopment.co.uk/) on the A-Frame Slack for pointing me in the correct direction:https://developer.mozilla.org/en/docs/Web/API/GlobalEventHandlers/onload
 window.onload = changeMaterialSide;

With this new knowledge, I added the material changing functionality to the other two component demo’s I had made before, making a total of new three pug files, which compiled to the following HTML/A-Frame pages:

  1. Terrain Model Component Demo
  2. Ocean Component Demo
  3. Mountain Component Demo

While 3. worked fine, after uploading, I tested 1. and 2. and found that the meshes would not render properly – even though 2. was rendering properly via my local server. I tried adding a call to Material.needsUpdate() after finding a Stackoverflow page alluding to it, but to no avail, even after changing the Material properties to just BackSide sides.

Finally, I posted a Stack Overflow question about it, where the ever helpful Kevin Ngo advised writing a component for the functionality, which I’m going to do next.

Categories
A piece of Art as big as India

Adding Browserify to the project

When looking at both the HeightGrid Component by andreasplesch and the Terrain Model Component by bryik I noticed that both referenced a project called Browserify.

Browserify lets you require(‘modules’) in the browser by bundling up all of your dependencies.

In other words, you can use Browserify to include lots of different JavaScript components and package them up into one JavaScript file that can then be easily included in your projects.

To start with, I decided to try to re-make the A-Frame Terrain Model Component – Olympic Peninsula that I had previously duplicated from bryik.

First of all, I had to install Browserify via npm globally:

npm install browserify --global

Second, install both A Frame and A-Frame Terrain Model Component via npm locally:

npm install aframe --save
npm install aframe-terrain-model-component --save

Third, I created a new JavaScript file (main.js) in a newly created js folder inside my previously created src folder:

cd src
mkdir js
cd js
touch main.js

Fourth, I added the following content to my new main.js file:

require('aframe');
require('aframe-terrain-model-component');

Fifth, I created a new .pug file (aFrameTerrainModelComponentOlympicPeninsulaGeneratedViaLessAndPugAndBrowserify.pug) in my previously created pug folder inside my previously created src folder, with the following content:

doctype html
html
 head
 meta(charset='utf-8')
 title A-Frame Terrain Model Component - Olympic Peninsula made via Less and Pug and Browserify
 meta(name='description', content='A-Frame Terrain Model Component - Olympic Peninsula made via Less and Pug and Browserify')
 script(src='build.js')
 link(href="style.css", rel="stylesheet", media="all")
 body
 a-scene(antialias='true')
 //Is this assets line even necessary?
 a-assets
 //Camera
 a-entity(position='100 40 70' rotation='0 30 0')
 a-entity(camera look-controls wasd-controls)
 //Terrain
 a-entity(terrain-model='DEM: url(aframe-terrain-model-component/docs/Olympic-Peninsula/data/clipped-envi.bin); texture: url(aframe-terrain-model-component/docs/Olympic-Peninsula/data/olympic-texture.jpg); planeWidth: 287; planeHeight: 151; segmentsWidth: 287; segmentsHeight: 151; zPosition: 50;')
 //Sky
 a-sky(color='grey')
 //Light
 a-entity(light='type: ambient; color: #0xeeeeee')

Which was created by converting the original HTML source code, liberal use of the JavaScript console and the Pug API reference, particulary the attributes section.

Sixth, I could now check that my new .pug file was correctly formatted by running the following command in the root of my project:

gulp pug

Unfortunately, the newly generated html file wasn’t loading yet as build.js didn’t exist, so it was time to add a new task to my gulpfile.js, so that Browserify could create it:

// Modules
var gulp = require('gulp');
var pug = require('gulp-pug');
var less = require('gulp-less');
var browserSync = require('browser-sync').create();
var source = require('vinyl-source-stream');
var browserify = require('browserify');

// Tasks
gulp.task('default', ['pug', 'less', 'browserify']);

gulp.task('pug', function(){
 return gulp.src( './src/pug/**/*.pug')
 .pipe( pug( {pretty: true}))
 .pipe( gulp.dest('./docs/'));
});

gulp.task('less', function(){
 return gulp.src( './src/less/**/*.less')
 .pipe( less())
 .pipe( gulp.dest('./docs/'));
});

gulp.task('browserify', function() {
 return browserify('./src/js/main.js')
 .bundle()
 //Pass desired output filename to vinyl-source-stream
 .pipe(source('build.js'))
 // Start piping stream to tasks!
 .pipe(gulp.dest('./docs/'));
});

// Watching
gulp.task('watch', function(){
 browserSync.init({
 port: 4000, //where is browser sync
 proxy: 'http://localhost:3000/', //what are we proxying?
 ui: {port: 4001}, //where is the UI
 browser: [] //empty array of browsers
 });

 gulp.watch('./src/pug/**/*.pug', [ 'pug'])
 .on('change', browserSync.reload);

 gulp.watch('./src/less/**/*.less', [ 'less'])
 .on('change', browserSync.reload);

 gulp.watch('./src/js/main.js', [ 'browserify'])
 .on('change', browserSync.reload);
});

In order to make this new task work, I also had to install vinyl-source-stream via npm locally, as well as browserify locally:

npm install vinyl-source-stream --save
npm install browserify --save

I could see the files running locally by running commands that I first used in a previous post:

nodemon app.server.js
gulp && gulp watch

I’ve pushed all these changes to my GitHub for the project, and you can see the generated html file here, it’s identical to the file I duplicated manually before.

The following tutorials were useful as reference for this post:

Categories
A piece of Art as big as India

Google Pixel, Making a Screen Recording of Dietrich Ayala’s AR Demo, Google Cardboard partial win

Today my Google Pixel arrived in the post, along with a pair of Google Cardboard. I’ve also got a Google Daydream on order, but it looks like it will arrive too late to be useful for user testing.

First impressions were great, it’s certainly a very capable smartphone. After successfully running the A-Frame AR demo by Dietrich Ayala on Firefox for Android, I wanted to be able to screen record it so that the A-Frame community could see the framework’s performance on Google’s flagship phone.

I downloaded Android File Transfer as well as Android Studio to enable me to connect directly to the phone on my Apple MacBook Pro. Helpfully, Google have provided a command line utility called screencap that allows for direct screen recording. Screencap runs within Android Debug Bridge (ADB) which needs to be enabled in the following way:

To access these settings, open the Developer options in the system Settings. On Android 4.2 and higher, the Developer options screen is hidden by default. To make it visible, go toSettings > About phone and tap Build number seven times. Return to the previous screen to find Developer options at the bottom.

After pressing Build number seven times(!) I was able to enable debugging mode on the new Pixel phone. This tutorial was handy to describe how to actually run ADB on my computer, I had to start a terminal in the following folder: /Users/<user>/Library/Android/sdk/ and then run the following command:

./adb devices

I actually had to run it twice, one to get permission to connect to the Pixel, and once again to actually get it to connect and report the serial number of the Pixel.

After playing around in the command line for some time, I was finding it very difficult to actually save a file to the phone’s local filesystem:

ForceMacbookProRetina:platform-tools joel$ pwd
/Users/joel/Library/Android/sdk/platform-tools
ForceMacbookProRetina:platform-tools joel$ ./adb devices
List of devices attached
FA69Y******* device
ForceMacbookProRetina:platform-tools joel$ ./adb shell screenrecord demo.mp4
Unable to open 'demo.mp4': Read-only file system

After doing some more searching I found a great app called AndroidTool by Morten Just that enabled me to record screen captures (and even convert them to animated Gifs) with just one click. The results can be seen below:

The final step was to try running the demo with Google Cardboard. I’m pleased to say that it worked great in terms of a 3D effect – but unfortunately, Cardboard physically covers the forward facing camera as well as the VR mode disabling the live video. I managed to get Firefox running fullscreen in non VR mode by using an addon, but it would be great to have VR mode not disable the camera.

2016_10_24_firstcardboard

Below is a screenshot of the demo working in Firefox for Android:

2016_11_01_aframearonfirefoxonandroid

But when you enable VR mode by pressing the icon in the bottom right hand corner of the screen, the live camera background is no longer displayed:

2016_11_01_aframearonfirefoxonandroidinvrmodecamerafail

 

Categories
A piece of Art as big as India

Streamlining development of the project with Express, nodemon, Pug, Less, Gulp and Browsersync

In order to be able to develop efficiently, I’ve realised that I need a local web server running on my own computer, instead of having to constantly upload code to my GitHub Pages server.

As I’d already selected node.js as my backend, it made sense to use that on my local machine too. I found The Art of Node by Max Ogden a great introduction to what node.js is and what is it useful for, namely:

Node.js is an open source project designed to help you write JavaScript programs that talk to networks, file systems or other I/O (input/output, reading/writing) sources. That’s it! It is just a simple and stable I/O platform that you are encouraged to build modules on top of.

Quoting further:

Node isn’t either of the following:

  • A web framework (like Rails or Django, though it can be used to make such things)
  • A programming language (it uses JavaScript but node isn’t its own language)

Instead, node is somewhere in the middle. It is:

  • Designed to be simple and therefore relatively easy to understand and use
  • Useful for I/O based programs that need to be fast and/or handle lots of connections

This is exactly what I want to do – I need something simple that is going to be fast and handle lots of connections – potentially up to 300,000,000 at once!

I installed node.js on my laptop via Homebrew.

In order not to have to write HTML and CSS completely manually, I asked my friend Ross Cairns for some tips on what would be useful, and he gave me a rapid tutorial in the following platforms:

  • Express – a web framework for node that enables you to write web applications – which is what I’ll need to enable users to load my sculpture and alter it themselves within a mobile webpage.
  • nodemon – tool that reloads your node server automatically when it detects any changes in your code.
  • Pug – a templating engine for node that enables you to write HTML in a simpler way, without having to worry about closing tags and other complications. I also found a Pug template (formally known at Jade) that used A-Frame, which was very encouraging.
  • Less – a pre-processor for CSS that makes it much easier to use.
  • Gulp – a tool for automation that enables the automatic use of tools like Pug, Less and many others.
  • Browsersync – a tool that automatically reloads your web browser when it detects changes in your source code.

By default, Node.js also installs Node Package Manager (npm) which can be used to install further node programs.

I want to be able to install lots of node programs for this project, and doing it by hand can get unwieldy, so on Ross’s advice, I used the:

npm init

Command in order to create a package.json file in my project directory to list all the node programs I install, in order to make the project easier to manage and share in the future.

After that I installed nodemon and gulp-cli globally:

npm install nodemon --global
npm install gulp-cli --global

And then Express, Pug, Less, Gulp and BrowserSync locally:

npm install express --save
npm install gulp --save
npm install gulp-pug --save
npm install gulp-less --save
npm install browser-sync --save

Then I had to create the most simple Express app possible – a completely static one, by creating an “app.server.js” in the root of my project, with the following content:

// Modules
var express = require('express');

// Express
var app = express();

// our middleware
app.use(express.static('docs')); //Also GitHub Pages root, everything is going to be static to begin with

//Binding to a port...
app.listen(3000, function () {
 console.log('A piece of Art as big as India Express app listening on port 3000.');
});

I could then test the Express app by running the following command:

node app.server.js

and accessing http://localhost:3000 to test my new node server. Everything worked as if I was accessing the GitHub pages I had previously been working with.

In order to have something for Gulp to automate, I then created a .less file and .pug file in a newly created src folder (with less and pug folders within) that would duplicate the Pug template I had found earlier:

body {
   background: white;
}

style.less

doctype html
html
 head
 meta(charset='utf-8')
 title Hello, World! &bull; A-Frame, made via Pug and Less and Gulp
 meta(name='description', content='Hello, World! • A-Frame')
 script(src='https://aframe.io/releases/0.3.2/aframe.min.js')
 link( href="style.css", rel="stylesheet", media="all")
 body
 a-scene
 a-box(position='-1 0.5 -3' rotation='0 45 0' color='#4CC3D9')
 a-sphere(position='0 1.25 -5' radius='1.25' color='#EF2D5E')
 a-cylinder(position='1 0.75 -3' radius='0.5' height='1.5' color='#FFC65D')
 a-plane(position='0 0 -4' rotation='-90 0 0' width='4' height='4' color='#7BC8A4')
 a-sky(color='#ECECEC')

aFrameBoilerPlateGeneratedViaLessAndPug.pug

Now that I had some files to generate from, I could create a gulpfile.js in the root of my project in order to automate the process.

// Modules
var gulp = require('gulp');
var pug = require('gulp-pug');
var less = require('gulp-less');
var browserSync = require('browser-sync').create();

// Tasks
gulp.task('default', ['pug', 'less']);

gulp.task('pug', function(){
 return gulp.src( './src/pug/**/*.pug')
 .pipe( pug( {pretty: true}))
 .pipe( gulp.dest('./docs/'));
});

gulp.task('less', function(){
 return gulp.src( './src/less/**/*.less')
 .pipe( less())
 .pipe( gulp.dest('./docs/'));
});

// Watching
gulp.task('watch', function(){
 browserSync.init({
 port: 4000, //where is browser sync
 proxy: 'http://localhost:3000/', //what are we proxying?
 ui: {port: 4001}, //where is the UI
 browser: [] //empty array of browsers
 });

gulp.watch('./src/pug/**/*.pug', [ 'pug'])
 .on('change', browserSync.reload);

gulp.watch('./src/less/**/*.less', [ 'less'])
 .on('change', browserSync.reload);
});

By running the following commands in two Terminal windows, I can write code locally and see the changes instantaneously in a browser running on my own computer.

nodemon app.server.js
gulp && gulp watch

I’ve pushed all these changes to my GitHub for the project, and you can see the generated html file here, it’s identical to the file I created manually before.

Categories
A piece of Art as big as India

Making a equirectangular panoramic image

Right at the beginning of the project, I requested a panoramic image from the team at the British Council in Delhi, so that I could construct a demonstration app for user testing in India that didn’t require real time camera data. They sent me back the following image:

20160926_150409

I dropped it a simple A-Frame scene, using the Sky component. This was the result:

Panorama fail.
Panorama fail.

You can try the broken demo for yourself here.

It was obvious that there was something wrong with the translation between an iPhone panorama and what the Sky component required, especially at the top and bottom of the panorama. Exploring the documentation around the Sky component further, I found this detail:

In order to be seamless, images should be equirectangular.

After reading Kevin’s 360-degree Photography Guide, I realised I needed to invest in a device that would make it possible for me to take equirectangular images – the iPhone just wasn’t going to cut it. Luckily, the article recommends a great camera, the Ricoh Theta S.

I snapped the following image today:

2016_10_18_firstpanorama

I dropped it into the same A-Frame scene that I used for the Delhi panorama and it worked perfectly:

Panorama win!
Panorama win!

You can try the successful demo for yourself here. I’m going to send the camera out to Delhi soon so that I can capture more usable imagery.

Categories
A piece of Art as big as India

Building a static landscape

After selecting the technical components and doing some research into the form of the sculpture, the next step is to start building the sculpture.

Keeping in mind the MVP principles that I set out at the beginning of the project, the first step was to duplicate some static demonstrations of A-Frame and publish them to the web.

As I’ve already started a GitHub repository to be able to share my code research and the eventual final project, it made sense to use GitHub Pages to be able to create static webpages to begin my experimentation with A-Frame.

Following the Github Page tutorial for creating a Project Site from scratch, I created a new file called ‘index.html’, and copied the index.html code from the A-Frame Boilerplate project, and then enabled the GitHub pages option in the settings of the project.

Success!
Success!

Now that I have a demo file going, the next step is to get a static landscape building in A-Frame. I have previously selected two components that looked like they would be a good fit:

  1. HeightGrid Component by andreasplesch
  2. Terrain Model Component by bryik

Starting with the HeightGrid component I had to create more than one GitHub page, so made the root of my GitHub Pages the docs/ folder, and made a new index.html file as well as an attempt at the basic HeightGrid demo.

After downloading both complete projects, I copied them into my docs/ folder. You can view the list of demos here. Unfortunately, both the HeightGrid demos are currently broke, but all the Terrain Model Component demos work fine:

Olympic Peninsula Terrain Model Success
Olympic Peninsula Terrain Model Success
Categories
A piece of Art as big as India

Khadi, Christo, Tess Jaray and the Atlas of Novel Tectonics

As well as working on the technical details of how I’m going to make a piece of Art as big as India, I’ve been doing research on what the form of the augmented sculpture should be, as well as thinking about how users should interact with it.

Lost Rivers of London by Lorain Rutt
Lost Rivers of London by Lorain Rutt

I love the idea of being able to see the land below our feet above our heads – especially in urban areas where the topology of the land is often obscured by the built environment. This is something that I think about in London all the time – especially compared to where I grew up in Wales.

whereigrewup
Where I grew up in Wales

I’m interested in the sculpture being a time based metaphor for humanity’s effect on the world around it – pushing and pulling it away from its original form. I want to start with the sculpture being an exact copy of the land below it, but the act of interaction and observation changing it in real time.

Should the sculpture resemble a crystalline structure (fixed, hard, unmoving) or a material (billowing, supple, constantly flowing)? Jon Harris suggested looking at Khadi, both a material and a movement.

I wanted to see what other people had been making around making sculpture, especially on larger scales.

On a visit to the Serpentine Gallery‘s branch of Koenig Books I found three interesting references:

  1. Christo – Big Air Package
  2. Desire Lines: The public art of Tess Jaray
  3. Atlas of Novel Tectonics
Drawing 2012 in two parts 96 x 28" and 96 x 42" (244 x 71 cm and 244 x 106.6 cm) Pencil, charcoal, pastel, wax crayon, wash and architectural plans Photo: André Grossmann © 2012 Christo Ref. # 3-2012
Drawing 2012 in two parts
96 x 28″ and 96 x 42″ (244 x 71 cm and 244 x 106.6 cm)
Pencil, charcoal, pastel, wax crayon, wash and architectural plans
Photo: André Grossmann
© 2012 Christo
Big Air Package, Gasometer Oberhausen, Germany, 2010-13 Photo: Wolfgang Volz © 2013 Christo
Big Air Package, Gasometer Oberhausen, Germany, 2010-13
Photo: Wolfgang Volz
© 2013 Christo
Big Air Package, Gasometer Oberhausen, Germany, 2010-13 Photo: Wolfgang Volz © 2013 Christo
Big Air Package, Gasometer Oberhausen, Germany, 2010-13
Photo: Wolfgang Volz
© 2013 Christo

Christo – Big Air Package is both the name of a installation by Christo and the title of a catalogue of projects from 1961-2013. More background information on Christo can be found on Wikipedia.

The most recent realised project by Christo was titled “The Floating Piers“:

The Floating Piers, Lake Iseo, Italy, 2014-16 Photo: Wolfgang Volz © 2016 Christo
The Floating Piers, Lake Iseo, Italy, 2014-16
Photo: Wolfgang Volz
© 2016 Christo

I was also excited to discover their current in progress project, “Over the River“:

Over the River (Project for Arkansas River, State of Colorado) Drawing 2010 in two parts 15 x 96" and 42 x 96" (38 x 244 cm and 106.6 x 244 cm) Pencil, pastel, charcoal, wax crayon, enamel paint, wash, fabric sample, hand-drawn topographic map and technical data Photo: André Grossmann © 2010 Christo
Over the River (Project for Arkansas River, State of Colorado)
Drawing 2010 in two parts
15 x 96″ and 42 x 96″ (38 x 244 cm and 106.6 x 244 cm)
Pencil, pastel, charcoal, wax crayon, enamel paint, wash, fabric sample, hand-drawn topographic map and technical data
Photo: André Grossmann
© 2010 Christo

I found the following quote particularly interesting:

What was involved for Christo and Jeanne-Claude in their multifarious temporary wrappings was the physical experience of enveloping, protecting and caring, – as they themselves put it – “the quality of love and care that humans show for things which are not made for eternity.”

I want to be able to enable users to be able to express their care for India in a similar way, but in an augmented rather than purely physical space. The success of the project will rest upon if I can engage users as successfully as Christo.

Centenary Square Birmingham 1992 Tess Jaray
Centenary Square Birmingham
© 1992 Tess Jaray

Desire Lines: The public art of Tess Jaray catalogues the public installations of the painter and printmaker, Tess Jaray. These works in brick and stone are on a different scale to her studio-based output, but are concerned with the same things – repetition, balance and order – or the disruption of those things. Her use of materials is something that I want to echo in this project – perhaps I could use the geology of India as a starting point for a colouring or textural scheme?

Desire Line by Alan Stanton.
Desire Line by Alan Stanton.

I also found the title of the book inspirational. Once you know what a Desire Line (or path) is, you can’t help finding them everywhere. I hope they will emerge in my sculpture too.

Atlas of Novel Tectonics is by Reiser + Umemoto, a design practice based in New York. It’s a beautifuly designed book, with inserted colour prints that fold away elegantly to reveal captions. The written content is equally elegant, with several concepts or definitions jumping out at me as being particularly relevant to this project.

In the foreword by Sanford Kwinter the philosophical interest around diagrams – both external and internal ones. I’m hoping that users will create many local diagrams of both kinds with this project, starting from the initial geological facsimile.

Their idea of Fineness:

Fineness breaks down the gross fabric of buildings into finer and finer parts such that it can register small differences while maintaining an overall coherence. The fineness argument is encapsulated in the denstities of a sponge: too fine and it acts like a homogenous solid; too course and it becomes constrained by its members.

The Fineness of this sculpture is going to be critical aesthetically and technically.

Intensive and Extensive differences, drawing from a quote from Manuel DeLanda’s “Intensive Sciences and Virtual Philosophy”:

If we divide a volume of matter into two equal halves we end up with two volumes, each half the extent of the original one. Intensive properties on the other hand are properties such as temperature or pressure, which cannot be divided.

This sculpture will clearly have extensive properties of area and volume, but what are it’s intensive (or gradient) properties? Colour? Pressure? Density? Speed? Elasticity? Duration?

Classical Body/Impersonal Individuation. In this part of the book, the authors use the example of a skateboard ramp to rail against Anthropocentrism:

[A skateboard ramp] is an intervening technology that belongs to a totally different pattern of order upon which the human works. The ramp augments the body; it is an extension of the body via the vehicle of the skate, but it does not represent it.

Such an extension of performance belongs to a larger class of singularities know as impersonal individuations. Like the sunset or a time of day, these intense and unique conditions  emerge out of the material world. They have manifold meanings projected onto them, but they are not the product of meaning.

I want this sculpture to be like the sunset in this way.

Matter/force relationships – here the authors discuss how to make the relationship between matter and force visible in varying scales. As part of this, they discuss using Voronoi patterns to express forces on a variety of scales:

from Structure to Space to Program.

This sparked a thought in my mind, that I could make the interaction around the sculpture such that each time someone touches it makes a new singularity – however, this would be incompatible with the initial landscape – which is based around a cartesian grid of heights.

I still have the second half of the Atlas to read, but that will have to wait for a later post.

Categories
Reactickles 3

Starting out with p5.js

I’ve used Processing for many years, but never the more recent Javascript version created by Lauren McCarthy, p5.js.

The tutorials seem like a logical place to start, so lets begin with “Hello p5.js”.

The video is super fun! Lauren and Dan make a great team. Most exciting of all is that the video is interactive – allowing you to click and interact with the tutorial as it is playing. Starting with shape drawing and quickly moving onto flocking behaviours as well as connecting to web services (such as the wind direction in New York) to control those said flocking circles the tutorial gives you a great quick overview of the platform – including a great sound generation via mouse demo.

Being open, the source code for the interactive video is even available here.

The next on the was “Get Started”. The tutorial starts with an instruction to download p5.js complete. After doing that I added the files to my Git repository for the project in a folder called “p5_js_GetStarted” and pushed them to GitHub using the following commands:

git pull
git add .
git commit -m "Adding p5.js Get Started tutorial"
git push

After some circle drawing code, I added the following code to add interaction:

function setup() {
 createCanvas(640, 480);
}

function draw() {
 if (mouseIsPressed) {
 fill(0);
 } else {
 fill(255);
 }
 ellipse(mouseX, mouseY, 80, 80);
}

Using Sublime Text‘s Reindent function kept everything tidy. Below is a screen grab of the result, or you can try it for yourself.

2016_10_11_getstarted

The third tutorial in the list was “p5.js overview“.

This tutorial was hosted on GitHub and deals with a basic “Hello World” program, creating a canvas to draw upon, drawing into different HTML containers, working with native HTML5 canvas functionality, mouse and touch interaction, asynchronous calls and file loading – where my code started to break. After doing some digging I realised that I needed to run a local server in order to load my lovely “cat.jpg” file.

As I knew I’d be using it for another project, I decided to try Node.js. After some more digging, I realised that it would be best to install it via Homebrew, which I already had on my computer. As I’ve just updated to OS 10.12 aka “macOS Sierra” I had to do some updating of Homebrew, and found a nasty issue. After following the proscribed fix (which won’t happen if you just do a new install of Homebrew), I was ready to install node.js, by typing:

brew install node

In my terminal. I could then run the following two commands to verify that everything had installed correctly:

node -v
npm -v

The versions that I had installed were 6.7.0 and 3.10.7 respectively. NPM stands for the Node Package Manager – NPM helps you with installing other Node.js code to your local filesystem, or wherever you happen to have Node.js installed.

After installing Node http-server, I ran it in my HelloWorld folder, pointed my web browser to http://localhost:8080/ and saw:

2016_10_11_successfulcat

Hurrah! I uploaded it to my GitHub Pages account, so you can try it for yourself.

After trying making a Loading Screen and Instantiation / namespace experiments, I’m ready to move on to the next tutorial, “p5.js and Processing“.