Three dimensional chart added.

master
Ben Burlingham 10 years ago
parent 3a1d505d8b
commit f1a8e3c1ce
  1. 2
      .gitignore
  2. 2
      css/index.css
  3. 3
      index.html
  4. 3
      js/behaviors.js
  5. 6
      js/chart.js
  6. 220
      js/chart3.js
  7. 1
      js/init.js
  8. 35
      js/map.js
  9. 1
      sass/chart3.scss
  10. 1
      sass/index.scss
  11. 276
      vendor/FontUtils.js
  12. 62
      vendor/TextGeometry.js
  13. 1
      vendor/roboto.typeface.js
  14. 1532
      vendor/three.min.js

2
.gitignore vendored

@ -1,5 +1,7 @@
experiments
d3.min.js
node_modules
three.js-r73
.DS_Store
data/meteo/txt
data/stations/xml

@ -1,7 +1,6 @@
* {
box-sizing: border-box;
cursor: default;
font-family: 'open sans';
letter-spacing: 0.7px;
margin: 0;
padding: 0; }
@ -62,7 +61,6 @@
stroke: #f0f0f0; }
#chart3 {
border: 1px solid #f00;
height: 260px;
left: 20px;
position: absolute;

@ -39,9 +39,12 @@
<!-- <script src="//d3js.org/topojson.v1.min.js"></script> -->
<!-- https://cdnjs.cloudflare.com/ajax/libs/three.js/r74/three.min.js -->
<script src="vendor/three.min.js"></script>
<script src="vendor/TextGeometry.js"></script>
<script src="vendor/FontUtils.js"></script>
<script src="vendor/OrbitControls.js"></script>
<script src="vendor/d3.min.js"></script>
<script src="vendor/topojson.min.js"></script>
<script src="vendor/roboto.typeface.js"></script>
<script src="js/data.js"></script>
<script src="js/map.js"></script>

@ -14,6 +14,7 @@ var BuoyAnalysisBehaviors = {
BuoyAnalysisMap.findStationsUnderReticle();
BuoyAnalysisChart.draw();
BuoyAnalysisChart3.draw();
},
/**
@ -33,6 +34,7 @@ var BuoyAnalysisBehaviors = {
BuoyAnalysisMap.findStationsUnderReticle();
BuoyAnalysisChart.draw();
BuoyAnalysisChart3.draw();
},
/**
@ -44,6 +46,7 @@ var BuoyAnalysisBehaviors = {
BuoyAnalysisMap.findStationsUnderReticle();
BuoyAnalysisChart.draw();
BuoyAnalysisChart3.draw();
},
/**

@ -80,7 +80,11 @@ var BuoyAnalysisChart = {
.append('div')
.classed('label', true)
.text(function(d) {
return d.year.toString().slice(-2);
if (d.year % 5) {
return '';
}
return d.year.toString()//.slice(-2);
})
},

@ -3,13 +3,17 @@
var BuoyAnalysisChart3 = {
h: 258,
w: 498,
components: {},
scene: null,
renderer: null,
camera: null,
/**
*
*/
buildCamera: function() {
getCamera: function() {
var viewAngle = 45;
var aspectRatio = BuoyAnalysisChart3.w / BuoyAnalysisChart3.h;
var near = 1;
@ -22,135 +26,167 @@ var BuoyAnalysisChart3 = {
*
*/
getLighting: function() {
var pointLight = new THREE.PointLight('#fff');
var light1 = new THREE.PointLight('#fff');
light1.position.set(220, 150, -450);
pointLight.position.x = 50;
pointLight.position.y = 40;
pointLight.position.z = 40;
var light2 = new THREE.PointLight('#fff');
light2.position.set(220, 150, 500);
return [pointLight];
return [light1, light2];
},
/**
*
* http://threejs.org/docs/#Reference/Extras.Geometries/TextGeometry
*/
draw: function() {
var renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize( BuoyAnalysisChart3.w, BuoyAnalysisChart3.h );
renderer.setClearColor('#fff');
drawLabel: function(msg, size, color) {
var options = {
height: 1,
font: 'roboto',
size: size
};
document.getElementById('chart3').appendChild( renderer.domElement );
var geo = new THREE.TextGeometry(msg, options);
var mat = new THREE.MeshBasicMaterial({ color: color });
var scene = new THREE.Scene();
return new THREE.Mesh(geo, mat);
},
var mat = new THREE.LineBasicMaterial({ linewidth: 3, color: '#f0f' });
/**
*
*/
drawAxes: function(len, rgbX, rgbY, rgbZ) {
var vertices = new Float32Array([
0, 0, 0, len, 0, 0,
0, 0, 0, 0, len, 0,
0, 0, 0, 0, 0, len
]);
var axisHelper = new THREE.AxisHelper(500);
scene.add(axisHelper);
var colorBuffer = [].concat(rgbX, rgbX, rgbY, rgbY, rgbZ, rgbZ);
var colors = new Float32Array(colorBuffer);
BuoyAnalysisChart3.getLighting().forEach(function(light) {
scene.add(light);
});
var geo = new THREE.BufferGeometry();
geo.addAttribute('position', new THREE.BufferAttribute(vertices, 3));
geo.addAttribute('color', new THREE.BufferAttribute(colors, 3));
var mat = new THREE.LineBasicMaterial({ vertexColors: THREE.VertexColors });
var obj = new THREE.Object3D();
return new THREE.LineSegments(geo, mat);
},
BuoyAnalysisMap.reticle.stations.forEach(function(station, index) {
if (index > 0) {
return;
}
/**
*
*/
init: function() {
BuoyAnalysisChart3.renderer = new THREE.WebGLRenderer({ antialias: true });
BuoyAnalysisChart3.renderer.setSize(BuoyAnalysisChart3.w, BuoyAnalysisChart3.h);
BuoyAnalysisChart3.renderer.setClearColor('#fff');
document.getElementById('chart3').appendChild(BuoyAnalysisChart3.renderer.domElement);
var h, geo, mat, mesh;
BuoyAnalysisChart3.scene = new THREE.Scene();
BuoyAnalysisChart3.components.axes =
BuoyAnalysisChart3.drawAxes(300, [0.8, 0.8, 0.8], [0.8, 0.8, 0.8], [0.8, 0.8, 0.8]);
BuoyAnalysisChart3.scene.add(BuoyAnalysisChart3.components.axes);
// Ribbons. Colorize stations.
var width = 40;
var height = 300;
var width_segments = 1;
var height_segments = BuoyAnalysisData.years.end - BuoyAnalysisData.years.start;
BuoyAnalysisChart3.components.lighting = BuoyAnalysisChart3.getLighting();
BuoyAnalysisChart3.components.lighting.forEach(function(light) {
BuoyAnalysisChart3.scene.add(light);
});
var geo = new THREE.PlaneGeometry(width, height, width_segments, height_segments);
BuoyAnalysisChart3.components.lbl2015 = BuoyAnalysisChart3.drawLabel('2015', 20, '#ccc');
BuoyAnalysisChart3.components.lbl2015.position.set(-80, 300, 0);
BuoyAnalysisChart3.scene.add(BuoyAnalysisChart3.components.lbl2015);
for (var year = BuoyAnalysisData.years.start, i = 0; year <= BuoyAnalysisData.years.end; year++, i++) {
h = BuoyAnalysisData.stationJson[station]['a' + year]['wt']['y'] * 10;
BuoyAnalysisChart3.components.lbl2000 = BuoyAnalysisChart3.drawLabel('2000', 20, '#ccc');
BuoyAnalysisChart3.components.lbl2000.position.set(-80, 150, 0);
BuoyAnalysisChart3.scene.add(BuoyAnalysisChart3.components.lbl2000);
// if (h === 0) {
// continue;
// }
BuoyAnalysisChart3.components.lbl1982 = BuoyAnalysisChart3.drawLabel('1982', 20, '#ccc');
BuoyAnalysisChart3.components.lbl1982.position.set(-80, -30, 0);
BuoyAnalysisChart3.scene.add(BuoyAnalysisChart3.components.lbl1982);
// geo = new THREE.PlaneGeometry(30, 30, 1);
// mat = new THREE.MeshLambertMaterial( {color: 'rgb(100, 100, 0)'} );
// mesh = new THREE.Mesh( geo, mat );
BuoyAnalysisChart3.camera = BuoyAnalysisChart3.getCamera();
BuoyAnalysisChart3.camera.up.set(0, 0, 1);
BuoyAnalysisChart3.camera.position.set(-207, -267, 367);
// geo.applyMatrix( new THREE.Matrix4().makeTranslation( 0, 0, 0 ) );
// mesh.position.set((year - BuoyAnalysisData.years.start) * 32, index * 32, h);
// obj.add(mesh);
BuoyAnalysisChart3.controls = new THREE.OrbitControls( BuoyAnalysisChart3.camera, BuoyAnalysisChart3.renderer.domElement );
BuoyAnalysisChart3.controls.target = new THREE.Vector3(200, 100, 0);
geo.vertices[2 * i].z = h;
geo.vertices[2 * i + 1].z = h;
}
// controls.minPolarAngle = 50 * Math.PI / 180; // radians
// controls.maxPolarAngle = 90 * Math.PI / 180; // radians
// for(var i = 0; i < plane.vertices.length / 2; i++) {
// plane.vertices[2 * i].z = Math.pow(2, i / 20);
// plane.vertices[2 * i + 1].z = Math.pow(2, i / 20);
// }
// controls.minAzimuthAngle = -90 * Math.PI / 180; // radians
// controls.maxAzimuthAngle = 0 * Math.PI / 180; // radians
},
var mesh = new THREE.Mesh(geo, new THREE.MeshLambertMaterial({color: 0xccffcc}));
mesh.material.side = THREE.DoubleSide;
// mesh.doubleSided = true;
/**
*
*/
draw: function() {
var maxZ = 0;
var obj = new THREE.Object3D();
// mesh.rotation.y = Math.PI/2-0.5;
mesh.position.y = index * 100 + height / 2;
obj.add(mesh);
});
BuoyAnalysisMap.reticle.stations.forEach(function(station, index) {
var h;
var geo = new THREE.PlaneGeometry(40, 300, 1, BuoyAnalysisData.years.end - BuoyAnalysisData.years.start);
obj.position.set(150, 0, 0);
scene.add(obj);
for (var year = BuoyAnalysisData.years.start, i = 0; year <= BuoyAnalysisData.years.end; year++, i++) {
h = BuoyAnalysisData.stationJson[station]['a' + year]['wt']['y'] * 10 || h;
var camX = -20;
var camY = -300;
var camZ = 150;
geo.vertices[2 * i].z = h;
geo.vertices[2 * i + 1].z = h;
var tgtX = 425;
var tgtY = 300;
var tgtZ = -20;
if (maxZ < h) {
maxZ = h;
}
if (year === BuoyAnalysisData.years.end) {
continue;
}
// var geoCam = new THREE.BoxGeometry(10, 10, 10);
// var matCam = new THREE.MeshLambertMaterial( {color: '#f0f'} );
// var boxCam = new THREE.Mesh( geoCam, matCam );
if (BuoyAnalysisData.stationJson[station]['a' + year]['wt']['y'] === 0) {
geo.faces[2 * i].materialIndex = 1;
geo.faces[2 * i + 1].materialIndex = 1;
}
}
// boxCam.position.set(camX, camY, camZ);
// scene.add(boxCam);
var color = BuoyAnalysisMap.stationColorScale(Math.round(BuoyAnalysisData.stationJson[station].lat));
var materials = [
new THREE.MeshLambertMaterial({ color: color, side: THREE.DoubleSide }),
new THREE.MeshLambertMaterial({ color: "#f00", transparent: true, opacity: 0, side: THREE.DoubleSide })
];
// var geoTgt = new THREE.BoxGeometry(10, 10, 10);
// var matTgt = new THREE.MeshLambertMaterial( {color: '#0ff'} );
// var boxTgt = new THREE.Mesh( geoTgt, matTgt );
var mesh = new THREE.Mesh(geo, new THREE.MeshFaceMaterial(materials));
// boxTgt.position.set(tgtX, tgtY, tgtZ);
// scene.add(boxTgt);
mesh.position.x = -1 * index * 100;
mesh.position.y = -150;
var camera = BuoyAnalysisChart3.buildCamera();
camera.up.set( 0, 0, 1 );
camera.position.set(camX, camY, camZ);
obj.add(mesh);
});
var controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.target = new THREE.Vector3(tgtX, tgtY, tgtZ);
var txt = "Max " + Math.round((maxZ / 10) * 9/5 + 32) + " °F (" + (maxZ / 10) + " °C)";
// // Range is 0 to Math.PI radians.
// controls.minPolarAngle = 50 * Math.PI / 180; // radians
// controls.maxPolarAngle = 90 * Math.PI / 180; // radians
BuoyAnalysisChart3.scene.remove(BuoyAnalysisChart3.components.lblMaxZC);
BuoyAnalysisChart3.components.lblMaxZC = BuoyAnalysisChart3.drawLabel(txt, 12, '#000');
BuoyAnalysisChart3.components.lblMaxZC.position.set(0, -10, maxZ);
BuoyAnalysisChart3.components.lblMaxZC.rotation.x = Math.PI / 2;
BuoyAnalysisChart3.components.lblMaxZC.rotation.y = -Math.PI / 2;
BuoyAnalysisChart3.scene.add(BuoyAnalysisChart3.components.lblMaxZC);
// // How far you can orbit horizontally, upper and lower limits.
// // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ].
// controls.minAzimuthAngle = -90 * Math.PI / 180; // radians
// controls.maxAzimuthAngle = 0 * Math.PI / 180; // radians
controls.update();
obj.rotation.z = Math.PI;
obj.position.x = 20;
(function animate() {
BuoyAnalysisChart3.scene.remove(BuoyAnalysisChart3.components.curves);
BuoyAnalysisChart3.components.curves = obj;
BuoyAnalysisChart3.scene.add(BuoyAnalysisChart3.components.curves);
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
controls.update();
})();
BuoyAnalysisChart3.renderer.render(BuoyAnalysisChart3.scene, BuoyAnalysisChart3.camera);
BuoyAnalysisChart3.controls.update();
};
BuoyAnalysisChart3.controls.update();
animate();
}
};

@ -18,5 +18,6 @@
.then(BuoyAnalysisChart.updateAxes)
.then(BuoyAnalysisChart.draw)
.then(BuoyAnalysisChart3.init)
.then(BuoyAnalysisChart3.draw)
})();

@ -21,6 +21,11 @@ var BuoyAnalysisMap = {
.scale(2600)
.translate([BuoyAnalysisData.mapW / 2, BuoyAnalysisData.mapH / 2]),
/**
*
*/
stationColorScale: d3.scale.linear().domain([42, 37.5, 33]).range(['blue', 'orange', 'purple']),
/**
*
*/
@ -41,24 +46,6 @@ var BuoyAnalysisMap = {
var station;
var stations = [];
var colors = [
'#f00',
'#d00',
'#b00',
'#900',
'#700',
'#0f0',
'#0d0',
'#0b0',
'#090',
'#070',
'#00f',
'#00d',
'#00b',
'#009',
'#007',
];
for (var prop in BuoyAnalysisData.stationJson) {
station = BuoyAnalysisData.stationJson[prop];
@ -66,6 +53,8 @@ var BuoyAnalysisMap = {
station.x = tmp[0];
station.y = tmp[1];
station.color = BuoyAnalysisMap.stationColorScale(Math.round(station.lat));
stations.push(station);
}
@ -74,11 +63,9 @@ var BuoyAnalysisMap = {
.enter().append('circle')
.attr('cx', function(d) { return d.x; })
.attr('cy', function(d) { return d.y; })
.attr('r', 3)
.attr('r', 5)
.attr('id', function(d) { return 's' + d.id; })
.attr('stroke', function(d, i) { return colors[i]; })
// .attr('stroke', '#f00')
.attr('stroke-width', 5)
.attr('fill', function(d) { return d.color; });
},
/**
@ -117,6 +104,10 @@ var BuoyAnalysisMap = {
}
};
stations.sort(function(a, b) {
return BuoyAnalysisData.stationJson[a].lat < BuoyAnalysisData.stationJson[b].lat;
});
BuoyAnalysisMap.reticle.stations = stations;
}
};

@ -1,5 +1,4 @@
#chart3 {
border:1px solid #f00;
height:260px;
left:20px;
position:absolute;

@ -6,7 +6,6 @@ $mainW: 800px;
* {
box-sizing:border-box;
cursor:default;
font-family:'open sans';
letter-spacing:0.7px;
margin: 0;
padding: 0;

276
vendor/FontUtils.js vendored

@ -0,0 +1,276 @@
/**
* @author zz85 / http://www.lab4games.net/zz85/blog
* @author alteredq / http://alteredqualia.com/
*
* For Text operations in three.js (See TextGeometry)
*
* It uses techniques used in:
*
* Triangulation ported from AS3
* Simple Polygon Triangulation
* http://actionsnippet.com/?p=1462
*
* A Method to triangulate shapes with holes
* http://www.sakri.net/blog/2009/06/12/an-approach-to-triangulating-polygons-with-holes/
*
*/
THREE.FontUtils = {
faces: {},
// Just for now. face[weight][style]
face: 'helvetiker',
weight: 'normal',
style: 'normal',
size: 150,
divisions: 10,
getFace: function () {
try {
return this.faces[ this.face.toLowerCase() ][ this.weight ][ this.style ];
} catch ( e ) {
throw "The font " + this.face + " with " + this.weight + " weight and " + this.style + " style is missing."
}
},
loadFace: function ( data ) {
var family = data.familyName.toLowerCase();
var ThreeFont = this;
ThreeFont.faces[ family ] = ThreeFont.faces[ family ] || {};
ThreeFont.faces[ family ][ data.cssFontWeight ] = ThreeFont.faces[ family ][ data.cssFontWeight ] || {};
ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data;
ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data;
return data;
},
drawText: function ( text ) {
// RenderText
var i,
face = this.getFace(),
scale = this.size / face.resolution,
offset = 0,
chars = String( text ).split( '' ),
length = chars.length;
var fontPaths = [];
for ( i = 0; i < length; i ++ ) {
var path = new THREE.Path();
var ret = this.extractGlyphPoints( chars[ i ], face, scale, offset, path );
offset += ret.offset;
fontPaths.push( ret.path );
}
// get the width
var width = offset / 2;
//
// for ( p = 0; p < allPts.length; p++ ) {
//
// allPts[ p ].x -= width;
//
// }
//var extract = this.extractPoints( allPts, characterPts );
//extract.contour = allPts;
//extract.paths = fontPaths;
//extract.offset = width;
return { paths: fontPaths, offset: width };
},
extractGlyphPoints: function ( c, face, scale, offset, path ) {
var pts = [];
var b2 = THREE.ShapeUtils.b2;
var b3 = THREE.ShapeUtils.b3;
var i, i2, divisions,
outline, action, length,
scaleX, scaleY,
x, y, cpx, cpy, cpx0, cpy0, cpx1, cpy1, cpx2, cpy2,
laste,
glyph = face.glyphs[ c ] || face.glyphs[ '?' ];
if ( ! glyph ) return;
if ( glyph.o ) {
outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) );
length = outline.length;
scaleX = scale;
scaleY = scale;
for ( i = 0; i < length; ) {
action = outline[ i ++ ];
//console.log( action );
switch ( action ) {
case 'm':
// Move To
x = outline[ i ++ ] * scaleX + offset;
y = outline[ i ++ ] * scaleY;
path.moveTo( x, y );
break;
case 'l':
// Line To
x = outline[ i ++ ] * scaleX + offset;
y = outline[ i ++ ] * scaleY;
path.lineTo( x, y );
break;
case 'q':
// QuadraticCurveTo
cpx = outline[ i ++ ] * scaleX + offset;
cpy = outline[ i ++ ] * scaleY;
cpx1 = outline[ i ++ ] * scaleX + offset;
cpy1 = outline[ i ++ ] * scaleY;
path.quadraticCurveTo( cpx1, cpy1, cpx, cpy );
laste = pts[ pts.length - 1 ];
if ( laste ) {
cpx0 = laste.x;
cpy0 = laste.y;
for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2 ++ ) {
var t = i2 / divisions;
b2( t, cpx0, cpx1, cpx );
b2( t, cpy0, cpy1, cpy );
}
}
break;
case 'b':
// Cubic Bezier Curve
cpx = outline[ i ++ ] * scaleX + offset;
cpy = outline[ i ++ ] * scaleY;
cpx1 = outline[ i ++ ] * scaleX + offset;
cpy1 = outline[ i ++ ] * scaleY;
cpx2 = outline[ i ++ ] * scaleX + offset;
cpy2 = outline[ i ++ ] * scaleY;
path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy );
laste = pts[ pts.length - 1 ];
if ( laste ) {
cpx0 = laste.x;
cpy0 = laste.y;
for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2 ++ ) {
var t = i2 / divisions;
b3( t, cpx0, cpx1, cpx2, cpx );
b3( t, cpy0, cpy1, cpy2, cpy );
}
}
break;
}
}
}
return { offset: glyph.ha * scale, path: path };
}
};
THREE.FontUtils.generateShapes = function ( text, parameters ) {
// Parameters
parameters = parameters || {};
var size = parameters.size !== undefined ? parameters.size : 100;
var curveSegments = parameters.curveSegments !== undefined ? parameters.curveSegments : 4;
var font = parameters.font !== undefined ? parameters.font : 'helvetiker';
var weight = parameters.weight !== undefined ? parameters.weight : 'normal';
var style = parameters.style !== undefined ? parameters.style : 'normal';
THREE.FontUtils.size = size;
THREE.FontUtils.divisions = curveSegments;
THREE.FontUtils.face = font;
THREE.FontUtils.weight = weight;
THREE.FontUtils.style = style;
// Get a Font data json object
var data = THREE.FontUtils.drawText( text );
var paths = data.paths;
var shapes = [];
for ( var p = 0, pl = paths.length; p < pl; p ++ ) {
Array.prototype.push.apply( shapes, paths[ p ].toShapes() );
}
return shapes;
};
// To use the typeface.js face files, hook up the API
THREE.typeface_js = { faces: THREE.FontUtils.faces, loadFace: THREE.FontUtils.loadFace };
if ( typeof self !== 'undefined' ) self._typeface_js = THREE.typeface_js;

@ -0,0 +1,62 @@
/**
* @author zz85 / http://www.lab4games.net/zz85/blog
* @author alteredq / http://alteredqualia.com/
*
* For creating 3D text geometry in three.js
*
* Text = 3D Text
*
* parameters = {
* size: <float>, // size of the text
* height: <float>, // thickness to extrude text
* curveSegments: <int>, // number of points on the curves
*
* font: <string>, // font name
* weight: <string>, // font weight (normal, bold)
* style: <string>, // font style (normal, italics)
*
* bevelEnabled: <bool>, // turn on bevel
* bevelThickness: <float>, // how deep into text bevel goes
* bevelSize: <float>, // how far from text outline is bevel
* }
*
*/
/* Usage Examples
// TextGeometry wrapper
var text3d = new TextGeometry( text, options );
// Complete manner
var textShapes = THREE.FontUtils.generateShapes( text, options );
var text3d = new ExtrudeGeometry( textShapes, options );
*/
THREE.TextGeometry = function ( text, parameters ) {
parameters = parameters || {};
var textShapes = THREE.FontUtils.generateShapes( text, parameters );
// translate parameters to ExtrudeGeometry API
parameters.amount = parameters.height !== undefined ? parameters.height : 50;
// defaults
if ( parameters.bevelThickness === undefined ) parameters.bevelThickness = 10;
if ( parameters.bevelSize === undefined ) parameters.bevelSize = 8;
if ( parameters.bevelEnabled === undefined ) parameters.bevelEnabled = false;
THREE.ExtrudeGeometry.call( this, textShapes, parameters );
this.type = 'TextGeometry';
};
THREE.TextGeometry.prototype = Object.create( THREE.ExtrudeGeometry.prototype );
THREE.TextGeometry.prototype.constructor = THREE.TextGeometry;

File diff suppressed because one or more lines are too long

1532
vendor/three.min.js vendored

File diff suppressed because one or more lines are too long
Loading…
Cancel
Save