<!DOCTYPE html>
<html>

  <head>
    <link rel="stylesheet" href="style.css">
    
    <script src="//cdnjs.cloudflare.com/ajax/libs/three.js/r73/three.min.js"></script>
    <script src="particle-label.js"></script>
    <script src="script.js"></script>
  </head>

  <body>
    
  </body>

</html>
// change this Boolean to swap between Mesh and Sprite

var USE_SPRITE = true;

var theta = 0;
var radiusCamera = 4000;
var particleLabel;
var hoverSprite;
var scene;
var renderer;
var raycaster;
var mouse;
var userMouse;
var spriteGroup;


function create3d() {
  
  mouse = new THREE.Vector2();
  userMouse = new THREE.Vector2();
  
  scene = new THREE.Scene();
  renderer = new THREE.WebGLRenderer();
  raycaster = new THREE.Raycaster();
  
  renderer.autoClear = true;
  renderer.setClearColor( 0x111111 );
  renderer.setSize( window.innerWidth, window.innerHeight );
  renderer.setPixelRatio( window.devicePixelRatio );
  document.body.appendChild( renderer.domElement );
  
  camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 10000 );
  camera.position.z = 4000;
  camera.position.y = 1000;
  camera.lookAt( new THREE.Vector3( 0, 0, 0 ) );
  
}


function createSprites() {
  
  var o;
  var m;
  var radius = 500;
  var a = 250 / ( Math.PI * 2 );
  var pi2 = Math.PI * 2;
  var r;
  var total;
  var scale;
  
  spriteGroup = new THREE.Object3D();
  
  for ( var i = 0; i < 250; i++ ) {
  
    total = Math.floor(Math.random() * 1000);
    
    if ( USE_SPRITE ) {
    
      // using SpriteMaterial / Sprite
      m = new THREE.SpriteMaterial( { color: 0xff0000 } );
      o = new THREE.Sprite( m );
      
    } else {
     
      // using MeshBasicMaterial / Material
      m = new THREE.MeshBasicMaterial( { color: 0xff0000 } );
      o = new THREE.Mesh(new THREE.PlaneGeometry( 1, 1, 1 ),  m );
      
    }
    
    r = ( radius * Math.random() );
    a = Math.random() * pi2;
    o.position.x = Math.sin( a ) * r * 2;
    o.position.z = Math.cos( a ) * r; // z
    o.position.y = Math.sqrt( total ) * 25 + Math.random() * 200 + 50; // y
    o.position.round();
    o.userData.title = '#' + i;
    scale = total / 10;  
    o.scale.set( scale, scale, 1 );
    spriteGroup.add( o );
  
  }
  
  scene.add( spriteGroup );
}


function render() {

  requestAnimationFrame(render);
  
  theta += 0.05;
	camera.position.x = radiusCamera * Math.sin( THREE.Math.degToRad( theta ) );
	camera.position.z = radiusCamera * Math.cos( THREE.Math.degToRad( theta ) );
	camera.lookAt( scene.position );
	camera.updateMatrixWorld();
  
  raycastSprites();
  renderer.render( scene, camera );
  
}


function raycastSprites() {

  raycaster.setFromCamera( mouse, camera );

  intersects = raycaster.intersectObjects( spriteGroup.children );
  
  if ( intersects.length > 0 ) {

    // get the first object.
    showLabel( intersects[ 0 ].object, userMouse.x, userMouse.y );

  } else {

    particleLabel.hide();
    hoverSprite = null;
  
  }

}


function onMouseMove( event ) {

  event.preventDefault();

  userMouse.x = event.clientX;
  userMouse.y = event.clientY;

  // normalize between -1 and +1
  mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
  mouse.y = -( event.clientY / window.innerHeight ) * 2 + 1;

  isMouseMoving = true;

}


function showLabel( object, x, y ) {

  if (!object) return;
  
  if ( hoverSprite !== object && object.visible ) {
    hoverSprite = object;
    particleLabel.setText(hoverSprite.userData.title);
    particleLabel.show();
  }
  
  particleLabel.setPos( x, y );

}


window.onload = function () {
  
  create3d();
  createSprites();
  particleLabel = new ParticleLabel();
  renderer.domElement.addEventListener('mousemove', onMouseMove, false);
  render();
  
};
/* Styles go here */

html,
body,
canvas,
div {
  margin: 0;
  padding: 0;
  border: 0;
}

.label {
  font-size: 1.25em;
  z-index: 100;
  position: absolute;
  background: #333;
  color: #fff;
  padding: 10px;
     -moz-transform: translatez(1px);
      -ms-transform: translatez(1px);
       -o-transform: translatez(1px);
  -webkit-transform: translatez(1px);
  transform: translatez(1px);
  -khtml-user-select: none;
  -webkit-user-select: none;
  -o-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}

.label .label-number {
  font-size: 24px;
  margin-right: 10px;
}
(function (){
  'use strict';
  
  var ParticleLabel = function () {
  
    this.container = document.createElement( 'div' );
    this.container.className = 'label';
    this.container.style.display = 'none';
    document.body.appendChild( this.container );
  
  };
  
  ParticleLabel.prototype = {
    
    setText: function ( text, countArea ) {
  
      this.container.innerHTML = '<span class="label-number">' + text + '</span>';
    
    },
    
    setPos: function ( x, y ) {
    
      this.container.style.top = ( y - 80 ) + 'px';
      this.container.style.left = x + 'px';
    
    },
    
    hide: function () {
    
      this.container.style.display = 'none';
    
    },
    
    show: function () {
    
      this.container.style.display = 'block';
    
    }
  
  };
  
  window.ParticleLabel = ParticleLabel;

}());