Posts Tagged 'tutorial'



Using D3.js with THREE.js, part II

So, our little D3 + 3D experiment continues. This time we will be trying to replicate bar chart tutorial 2 in 3D.

Transitions

Two things need to be done before D3 transitions could work. First, D3 must be able to interpolate “attribute” values, so we need to implement both setAttribute and getAttribute methods in THREE.js:

// this one is to use D3's .attr() on THREE's objects
THREE.Object3D.prototype.setAttribute =
  function (name, value) {
    var chain = name.split('.');
    var object = this;
    for (var i = 0; i < chain.length - 1; i++) {
        object = object[chain[i]];
    }
    object[chain[chain.length - 1]] = value;
}

// and this one is to make'em work with D3's .transition()-s
THREE.Object3D.prototype.getAttribute =
  function (name) {
    var chain = name.split('.');
    var object = this;
    for (var i = 0; i < chain.length - 1; i++) {
        object = object[chain[i]];
    }
    return object[chain[chain.length - 1]];
}

Second, D3’s selectAll() now needs to actually select something to build updating selection. We can make it select various descendants of Object3D type, for example:

// now selectAll must actually do something
THREE.Object3D.prototype.querySelectorAll =
  function (selector) {
    var matches = [];
    var type = eval(selector);
    for (var i = 0; i < this.children.length; i++) {
        var child = this.children[i];
        if (child instanceof type) {
            matches.push(child);
        }
    }
    return matches;
}

These changes are already enough to replicate their simple transition example in THREE.js:

var t = 1297110663,
    v = 30,
    data = d3.range(9).map(next);

function next() {
  return {
    time: ++t,
    value: v = ~~Math.max(10,
      Math.min(90, v + 10 * (Math.random() - .5))
    )
  };
}

setInterval(function() {
  data.shift();
  data.push(next());
  redraw();
}, 1500);

function redraw() {
  d3.select( chart3d )
    .selectAll("THREE.Mesh")
    .data(data)
  .transition()
    .duration(1000)
    .attr("position.y", function(d, i) { return d.value; })
    .attr("scale.y", function(d, i) { return d.value / 10; })
}

But to replicate their final keyed data join example, we need to take care of one last thing…

Exit selections

We want D3’s remove() to actually remove 3D objects. Now, you might be thinking, this is easy one, we’ll just add removeChild() to Object3D prototype

THREE.Object3D.prototype.removeChild =
  function (c) {
    this.remove(c);
  }

and it should work, right? Wrong, it does not! To solve this one, I had to check D3 source. Turns out D3 does not call removeChild on selection parentNode but on node’s parentNode itself, so we need to modify our appendChild as follows:

THREE.Object3D.prototype.appendChild = function (c) {
    this.add(c);
    // create parentNode property
    c.parentNode = this;
    return c;
}

Final result

With these changes, it is now possible to do this:

function redraw() {

	var bars = d3.select( chart3d )
		.selectAll("THREE.Mesh")
		.data(data, function(d) { return d.time; });

	// move existing bars to their new place
	bars.transition()
		.duration(1000)
		.attr("position.x",
			function(d, i) { return 30 * i; })

	// add new bar and make it grow
	bars.enter().append( newBar )
		.attr("position.x",
			function(d, i) { return 30 * (i + 1); })
		.attr("position.y", 0)
		.attr("scale.y", 1e-3)
	  .transition()
		.duration(1000)
		.attr("position.x",
			function(d, i) { return 30 * i; })
		.attr("position.y",
			function(d, i) { return d.value; })
		.attr("scale.y",
			function(d, i) { return d.value / 10; })

	// remove the bar that is no longer in data set
	bars.exit().transition()
		.duration(1000)
		.attr("position.x",
			function(d, i) { return 30 * (i - 1); })
		.attr("position.y", 0)
		.attr("scale.y", 1e-3)
		.remove()
}

Final demo

This is still not complete THREE.js “plugin” but, as you can see, it’s a nice start.

Advertisements

Using D3.js with THREE.js

This blog name has “3D” in it, but there was no 3D here for a long time now. So I am fixing this tonight.

Once upon a time we did simple AS3 port of D3.js lib here at CodeOrchestra, codenamed “D6” by Potapenko. The name D6 came from D3 + 3D, since our port was able to work with 3D engines to enable 3D data visualization. That port never went into production, but the idea behind it is still valid today.

I mean, can you do 3D with D3? There is no 3D examples in their gallery, so the obvious answer seems to be “no”. The reason is that D3 was built with browser DOM in mind, and it is not straightforward to extend it to support arbitrary object models such as THREE.js, de facto standard for 3D in javascript world. Nevertheless, as you can guess, it could be done.

Let’s take a look at D3 simplest example (bar chart):

d3.select(".chart")
  .selectAll()
    .data(data)
  .enter().append("div")
    .style("width", function(d) { return d * 10 + "px"; })
    .text(function(d) { return d; });

Here, a number of methods accept browser-specific magic strings such as “.chart” selector or “div” tag name. While convenient in 2D browser environment, this is totally useless for our purposes. But have no fear, for D3 comes with alternative signatures for these methods! These signatures were designed for boring things like reusing existing selections, but we can actually bend them to swallow non-DOM objects. For example, we can rewrite the bar chart above like this:

function createDiv() {
	return document.createElement("div");
}

var chart = {
	appendChild: function (child) {
		// this is called by append() after createDiv()
		return document.getElementById("chartId")
			.appendChild(child);
	},
	querySelectorAll: function () {
		// this is called by selectAll()
		return [];
	}
}

d3.select( chart )
  .selectAll()
    .data(data)
  .enter().append( createDiv )
    .style("width", function(d) { return d * 10 + "px"; })
    .text(function(d) { return d; });

Here, we 1) explicitly instruct D3 how to create “div” tags, and 2) trick D3 into thinking that “chart” is normal DOM object, while the end result of the script is exactly the same as before. Now when we learned how to work around D3 helpful APIs, we can easily do the same with THREE.js:

THREE.Object3D.prototype.appendChild = function (c) {
	this.add(c); return c;
}

THREE.Object3D.prototype.querySelectorAll = function () {
	return [];
}

chart3d = new THREE.Object3D();

d3.select( chart3d )
  .selectAll()
  .data(data)
.enter().append(
  function (d, i) {
	var bar = new THREE.Mesh( geometry, material );
	bar.position.x = 30 * i;
	bar.position.y = d;
	bar.scale.y = d / 10;
	return bar;
  }
);

In this demo, I actually went ahead and made D3’s attr() calls work for why not.

Well, this is it for today :) I hope to continue this post some day with part 2, where I’d get D3’s transitions to work, but right now I need to sleep. Zzzz..

How to draw numbered billiard balls

This blog desperately needs more posts, but I hardly have any subjects to write at length about. And this is where wonderfl comes to the rescue – some of those codes are so complicated that one could give two hours talk about them. This code is that kind of code, and tonight I am going to explain just one small part of it – drawing numbered billiard balls. Continue reading ‘How to draw numbered billiard balls’

WTF is Transformation Matrix?

A transformation matrix transforms a vector of point coordinates in given frame of reference into coordinates in another one (e.g. from child to parent in our beloved display lists or scene graphs) – Captain Obvious.

Many people think of transformation matrices as impenetrable black boxes filled with random numbers that are impossible to make sense of while fiddling with your application. So this 1st post of the year is my attempt to help those people, by providing as short and simple explanation for those numbers as possible.

P.S. Since matrices transform vectors, you need to know a thing or two about vectors 1st. Continue reading ‘WTF is Transformation Matrix?’

Simplest FLARToolKit example ever?

FLARToolKit is said to be slow, but exactly how slow is it? To answer this question, I had to write the most simple bare bones FLARToolKit example ever :) It has neither trix to speed it up, nor complex 3D content to slow it down.

update: source updated to FLARToolKit svn head; it can work with 2.5.4 download if you remove param.setValue() call, line 48.

HOW TO: Integrate FlashDevelop with Azoth

  1. First, if you don’t know what a hell Azoth is and why would you need it in FD, read this post.
  2. Second, download Azoth zip.
  3. Find com subfolder inside zip’s as3class folder and extract it to C:\Program Files\FlashDevelop\Library\AS3\classes – now you can import Azoth’s fastmem class in any AS3 project.
  4. Make azoth folder in C:\Program Files\FlashDevelop\Tools.
  5. Find azoth.exe inside zip’s bin folder and extract it to azoth folder you have made at step 4.
  6. In the project that uses Azoth, go to project properties dialog, Build tab, and paste following into Post-Build Command Line field: “$(ToolsDir)\azoth\azoth.exe” “$(OutputDir)\$(OutputName)” “$(OutputDir)\$(OutputName)”

Update: FD response was incredibly fast – there’s now special FD extension available with AS3 project template.

Cubic panorama in Alternativa3D

Until v5.6 is out, at least, we have no means to make real skybox in Alternativa3D. We can fake it by placing very large static box around our scene. The problem with this solution is that there are many ways to unwrap the box and, correspondingly, there are many incompatible skybox textures. Changing box UVs by hand every time you replace skybox texture in your project is getting boring at some point, and when they want you to support multiple textures originating from all kinds of different sources, it gets nearly impossible. And then you write this thing:

Alternativa3D skybox demo

Continue reading ‘Cubic panorama in Alternativa3D’


Old stuff

September 2017
M T W T F S S
« Jan    
 123
45678910
11121314151617
18192021222324
252627282930  

Oh, btw…