2D Scatter Plot in Three.js - three.js

Using Three.js, I created a 3D scatter plot. Here is an example as a fiddle...
http://jsfiddle.net/jmg157/ynFzw/19/
I generate some random points (x, y, z) and add them to the plot. Here is an example of the for loop I use to do this:
for (var i = 0; i < 50; i++) {
colors[i] = new THREE.Color(1, 1, 1);
colors[i].setHSL(1000 / 2000, 1, 0.5);
var material = new THREE.PointCloudMaterial({
size: 5,
vertexColors: THREE.VertexColors,
transparent: true,
useScreenCoordinates: false
});
material.color.setHSL(1.0, 0.2, 0.7);
var vertex = new THREE.Vector3();
var max = 50;
var min = -50;
vertex.x = Math.random() * (max - min) + min;
vertex.y = Math.random() * (max - min) + min;
vertex.z = Math.random() * (max - min) + min;
geometry.vertices.push(vertex);
}
In practice, the points are plotted based on values in an array. So let's say I have a dataset that contains only x and y values (2D). I'd still like to be able to show a plot using Three.js.
I've created a fiddle that sort of does this, by only creating an XY grid, and commenting out the z value. But unfortunately, this seems to put the points a distance away from the grid, not directly on the grid like you would see in a typical 2D plot. Here is my fiddle for this...
http://jsfiddle.net/jmg157/exr6xc42/1/
I know it might sound a little silly to use Three.js for a 2D plot, but I want to be able to have the option in case a dataset only has x and y values as plottable.
Based on the fiddle above, how can I get the points to appear on the grid, like a standard 2D plot? Do I need to set the z coordinate to something specific?
Thanks in advance!

Isn't it enough to change this line (line 43) from
gridXY.position.set(0, 0, -50);
to
gridXY.position.set(0, 0, 0);
?

Related

How to rotate cartesian coordinates relative to a vector?

I'm building a fractal tree in three dimensions. I need to draw each generation of branches at an angle relative to the previous generation. The branches are currently drawn at the same angle and are growing "straight up". I know I need to do a rotation of some kind, but not sure if it's quaternions or if I need to take a completely different approach.
Here's a jsfiddle of the fractal tree with the branches growing "straight up".
https://jsfiddle.net/degraeve/xa8m5Lcj/59/
Here's a 2D image of what I'm trying to achieve with the branch angles: https://i.imgur.com/uVK4Dx6.png
code that appears in the jsfiddle:
function draw_tree_branch(x, y, z, phi, theta, radius) {
// use sperical coordinate system
// https://en.wikipedia.org/wiki/Spherical_coordinate_system
var phi_in_degrees = phi * (180 / Math.PI);
var material = new THREE.LineBasicMaterial({
color: 0x00ffff,
linewidth: 1
});
// draw 3 lines at 120 degrees to each other
var angle_between_branches = 120;
var num_branches = 360 / angle_between_branches;
for (var temp_count = 1; temp_count <= num_branches; temp_count++) {
phi_in_degrees += angle_between_branches;
phi = (phi_in_degrees) * Math.PI / 180;
// compute Cartesian coordinates
var x2 = x + (radius * Math.sin(theta) * Math.sin(phi));
var y2 = y + (radius * Math.cos(theta));
var z2 = z + (radius * Math.sin(theta) * Math.cos(phi));
// ????????
// How do I rotate this line so the angles are "relative" to the parent line instead of growing "straight up?"
// Quaternion ???
// example of what I'm trying to achieve, but in 3D:
// https://www.codheadz.com/2019/06/30/Trees-with-Turtle-in-Python/simple_tree.png
// ????????
var points = [];
var vector_1 = new THREE.Vector3(x, y, z);
points.push(vector_1);
var vector_2 = new THREE.Vector3(x2, y2, z2);
points.push(vector_2);
var geometry = new THREE.BufferGeometry().setFromPoints(points);
var line = new THREE.Line(geometry, material);
scene.add(line);
// keep drawing branches until the branch is "too short"
if (radius > 2) {
draw_tree_branch(x2, y2, z2, phi, theta, radius * 0.5);
}
}
}
I may not even be asking the right question. Any pointers in the right direction are appreciated.
You're very close. The only problem is that theta is the same on each iteration, so you'll always get a sub-branch that's 30º from vertical. A simple way to solve this is by keeping track of the iteration you're in, and multiply that by tree_theta so you get an increasing number of degrees: 30, 60, 90, 120, etc...
function draw_tree_branch(x, y, z, phi, tree_theta, radius, iteration) {
var theta = tree_theta * iteration;
// ... perform all calculations
// Draw next branch with iteration + 1
if (radius > 2) {
draw_tree_branch(x2, y2, z2, phi, tree_theta, radius * 0.5, iteration + 1);
}
}
Here's an updated version of your JSFiddle: https://jsfiddle.net/marquizzo/r2w7oz6x/

D3 Donut chart projected to sphere/globe

I want to use d3 for the next task:
display rotating globe with donut chart in center of every country. It should be possible to interact with globe (select country, zoom, rotate).
Seems d3 provide an easy way to implement every part of it but I can not get donuts part working as I need.
There is an easy way draw donut chart with the help of d3.arc:
var arc = d3.arc();
var data = [3, 23, 17, 35, 4];
var radius = 15/scale;
var _arc = arc.innerRadius(radius - 7/scale)
.outerRadius(radius).context(donutsContext);
var pieData = pie(data);
for (var i = 0; i < pieData.length; i++) {
donutsContext.beginPath();
donutsContext.fillStyle = color(i);
_arc(pieData[i]);
}
by with code as it is donuts are displayed on a plane on top of the globe, like:
globe with donut
​
while I want them to be 'wrapped' around the globe
There is d3.geoCircle method that can be projected to globe correctly. I got 'ring' projected correctly to the globe with the help of two circles:
var circle = d3.geoCircle()
.center(centroid)
.radius(2);
var outerCircle = circle();
var circle = d3.geoCircle()
.center(centroid)
.radius(1);
var innerCircle = circle();
var interCircleCoordinates = [];
for (var i = innerCircle.coordinates[0].length - 1; i >= 0; i--) {
interCircleCoordinates.push(innerCircle.coordinates[0][i]);
}
outerCircle.coordinates.push(interCircleCoordinates);
​globe with rings
but I really need to get a donut.
The other way I tried is getting image from donuts and wrapping this image around globe with the help of pixels manipulation:
var image = new Image;
image.onload = onload;
image.src = img;
function onload() {
window.dx = image.width;
window.dy = image.height;
context.drawImage(image, 0, 0, dx, dy);
sourceData = context.getImageData(0, 0, dx, dy).data;
target = context.createImageData(width, height);
targetData = target.data;
for (var y = 0, i = -1; y < height; ++y) {
for (var x = 0; x < width; ++x) {
var p = projection.invert([x, y]), λ = p[0], φ = p[1];
if (λ > 180 || λ < -180 || φ > 90 || φ < -90) { i += 4; continue; }
var q = ((90 - φ) / 180 * dy | 0) * dx + ((180 + λ) / 360 * dx | 0) << 2;
var r = sourceData[q];
var g = sourceData[++q];
var b = sourceData[++q];
targetData[++i] = r;
targetData[++i] = g;
targetData[++i] = b;
targetData[++i] = 125;//
}
}
context.clearRect(0,0, width, height);
context.putImageData(target, 0, 0);
};
by this way I get extremely slow rotating and interaction with a globe for a globe size I need (1000px)
So my questions are:
Is there is some way to project donuts that are generated with the help of d3.arc to a sphere (globe, orthographic projection)?
Is there is some way to get a donut from geoCircle?
Maybe there is some other way to achieve my goal I do not see
There is one way that comes to mind to display donuts on a globe. The key challenge is that d3 doesn't project three dimensional objects very well - with one exception, geographic features. Consequently, an "easy" solution is to convert your pie charts into geographic features and project them with the rest of your features.
To do this you need to:
Use a pie/donut generator as you normally would
Go along the paths generated to get points approximating the pie shape.
Convert the points to long/lat points
Assemble those points into geojson
Project them onto the map.
The first point is easy enough, just make a pie chart with an inner radius.
Now you have to select each path and find points along its perimeter using path.getPointAtLength(), this will be dependent on path length, so path.getTotalLength() will be handy (and corners are important, so you might want to incorporate a little bit of complexity for these corner cases to ensure you get them)).
Once you have the points, you need the use of a second projection, azimuthal equidistant would be best. If the pie chart is centered on [0,0] in svg coordinate space, rotate the azimuthal (don't center), so that the centroid coordinate is located at [0,0] in svg space (you can use translates on the pies to position them, but it will just add extra steps). Take each point and run it through projection.invert() using the second projection. You will need to update the projection for each donut chart as each one will have a different geographic centroid.
Once you have lat long points, it's easy - you've already done it with the geo circle function - convert to geojson and project with the orthographic projection.
This approach gave me something like:
Notes: Depending on your data, it might be easiest to preprocess your data into geojson and store that as opposed to calculating the geojson each page load.
You are using canvas, while you don't need to actually use an svg, you need to still be able to access svg functions like getPointAtLength, you do not need to have an svg or display svg elements by using a custom element replicating a path :
document.createElementNS(d3.namespaces.svg, 'path');
Oh, and make sure the second projection's translate is set - the default is [480,250] for all (most?) d3 projections, that will throw things off if unaccounted for.

Calculate the vertex while creating terrain from heightmap using ThreeJs

I'm reading "create terrain from heightmap" example from ThreeJs Cookbook
This example load GrandCanyon: http://lh5.ggpht.com/_-B0hFoGrn-w/SvHiYk39yAI/AAAAAAAABOQ/6IGZwifUYGA/GrandCanyon.png
And create a 3D terrain: http://www.smartjava.org/tjscb/02-geometries-meshes/02.06-create-terrain-from-heightmap.html
There are some code pieces I can not understand:
// draw on canvas
ctx.drawImage(img, 0, 0);
var pixel = ctx.getImageData(0, 0, width, depth);
var geom = new THREE.Geometry;
var output = [];
for (var x = 0; x < depth; x++) {
for (var z = 0; z < width; z++) {
// get pixel
// since we're grayscale, we only need one element
var yValue = pixel.data[z * 4 + (depth * x * 4)] / heightOffset;
var vertex = new THREE.Vector3(x * spacingX, yValue, z * spacingZ);
geom.vertices.push(vertex);
}
}
why is yValue calculated with that value ? why don't we use var yValue = pixel.data[z * 4 + (depth * x )] or something like that ?
And do we really need spacingX and spacingZ ?
Source code is here: https://github.com/josdirksen/threejs-cookbook/blob/master/02-geometries-meshes/02.06-create-terrain-from-heightmap.html
Could you please help me ?
Thank you very much!
You don't NEED spacingX and spacingZ, no. You could adjust scale in other ways, like applying a scale matrix to the entire THREE.Geometry after you've populated the vertices. Up to you, really.
As fort the yValue, the indexing is to adjust for the way the data for the texture is laid out. There are four channels, usually RGBA, but in this case we only need one of them as a height.

famo.us quaternion rotation around z axis

As far as I know a quaternion is a set of four values (W X Y Z) that are used to specify a rotation in 3D space. For a given axis (x y z) and angle (α), the quaternion representing a rotation around the axis from the origin (0,0,0) to (x,y,z). So a rotation of 90 degrees about the z axis (0 0 1) should be:
var quaternion = new Quaternion(Math.PI/2, 0, 0, 1);
but famo.us turns it for ~60 degrees...
I've also tried var quaternion = new Quaternion(90, 0, 0, 1); but in this case famo.us turns it for ~5 degrees
is it a bug of the framework?
How should I use it to turn it on 90 degreez around z axis?
Documentation is still totally useless..
Try using this method Quaternion.makeFromAngleAndAxis(angle, v)
I have found this to be the most straight forward approach to making it a little more readable and useable.
Example jsBin
Where
var degrees = 90;
var angle = Math.PI/180 * degrees;
var v = new Vector(0, 0, 1);
var quaternion = new Quaternion();
quaternion.makeFromAngleAndAxis(angle, v);
...To get the transform
quaternion.getTransform();
Something to remember from Math Class
A circle has 360 degrees. Each degree is represented by the unit circumference of a circle 2 * PI * r. We will assume we have a radius of 1. So divide your total circumference by 360 and you get one degrees 2PI/360 or PI/180.
In Summary:
one degrees of our circle is = Math.PI/180
your angle of direction is = Math.PI/180 * degrees
Just found answer in one wiki article:
var angle = Math.PI/2;
var axis = [0,0,1];
var w = Math.cos(.5 * angle);
var x = axis[0] * Math.sin(.5 * angle);
var y = axis[1] * Math.sin(.5 * angle);
var z = axis[2] * Math.sin(.5 * angle);
var quaternion = new Quaternion(w, x, y, z);
try this transformation - Transform.rotateZ(angle);
Refer to - http://famo.us/docs/reference/pages/0.3/transforms.html

Setting color of mapped image for ThreeJS particles

Originally I was using ParticleSystem, but I discovered that Raycaster does not work with it. So I'm now modifying my code to simply use individual Particle objects.
The problem is, I can't seem to set the color of the image I'm mapping to the particles like I was able to with ParticleSystem.
I tried the following:
texture = THREE.ImageUtils.loadTexture("ball.png");
material = new THREE.ParticleBasicMaterial({
size : 10,
color: 0x00C1BF,
map : texture,
transparent : true,
});
// Generate some random points...
for (var i = 0; i < pointCount; i++) {
var particle = new THREE.Particle(material);
particle.position.x = Math.random() * (max - min) + min;
particle.position.y = Math.random() * (max - min) + min;
particle.position.z = Math.random() * (max - min) + min;
particle.scale.x = particle.scale.y = particle.scale.z = 3;
plot.add(particle);
}
But the color of ball.png remains the same. If I comment out the image I'm mapping to the points, the colors are changing. But it's not working with the mapped image. When I was using ParticleSystem, inside the for loop where I generate the points, I was adding this:
colors[i] = new THREE.Color(0xffffff);
colors[i].setHSL((x + 1000 ) / 2000, 1, 0.5);
And then set particleSys.colors = colors; outside the loop. That changed the color of the points, but this doesn't seem to work with Particle.
I hate to keep bugging the community with questions like this, but I really would appreciate any guidance on this. Many thanks, as always! :)
Also, here's a link to ball.png that I'm using: http://threejsdoc.appspot.com/doc/three.js/examples/textures/sprites/ball.png

Resources