Convert 2D shape into 3D in d3.js and adjust height according to the value in ANGULAR

Viewed 570

I am using d3.js v6 to create a 3D graph of the below 2D chart representation. This circle has multiple squares in it and each square has been assigned a color based on the value. The bigger the value, more darker the square.

enter image description here

Now I want to convert this in 3D shape where only the height of a particular square increases when the value gets high, so the result would be somehow similar to the image below. The base would be circular but the height of each value would go up based on the value

enter image description here

I am trying to achieve this in angular, if anyone could please help me out. Here is the Stackblitz Link

1 Answers

I made the one as you requested.

source code on github

here's working demo: https://stackoverflow-angular-3d-chart.surge.sh/

This involved several intricate steps.

I couldn't go any deeper from this answer because every part that I mentioned here could be hours worth tutorial. These are what I've felt interesting when I was working on it.

Used Stacks

EDIT: the stackblitz code is now outdated. I've used the most recent version for each package.

  • Three.js r143
  • D3.js v7.6.1
  • Angular.js v14

Getting Circle Grid

First I've experimented on SVG with D3.js to get proper circle grid.

It seemed daunting but turned out very simple. I've slightly modified Midpoint circle algorithm to fill box grids in circular shape. It is little different from filling grids in 3d space; 2d space has top left corner as beginning of everything. In 3d space, everything starts from center.

const midPointX = gridWidth / 2;
const midPointY = gridHeight / 2;
const { midPointX, midPointY, radius } = config;

const getCollision = ({ x, y }) => {
  return (midPointX - x) ** 2 + (midPointY - y) ** 2 - radius ** 2 > 0;
}

grids filled in circular shape on SVG

Calculating Gaps

d3's scale band supports automatic calculation of gaps and content size in responsive environment.

  const scaleBandX = d3
    .scaleBand()
    .domain(d3.range(0, config.gridWidth))
    .range([config.margin, config.svgWidth - config.margin * 2])
    .paddingInner(0.2);

  const scaleBandY = d3
    .scaleBand()
    .domain(d3.range(0, config.gridHeight))
    .range([config.margin, config.svgHeight - config.margin * 2])
    .paddingInner(0.2);

scaleBandX.bandwidth(); // width of box in 2d space
scaleBandY.bandwidth(); // height of box in 2d space
scaleBandX(boxIndex); // x position of box in 2d space with gap
scaleBandY(boxIndex); // y position of box in 2d space with gap

as D3 assumes vector calculation as normal, it was pretty easy to apply the very same method in 3D.

Expressing on 3D space

I've used Three.js to express everything in 3D. The app is running on Angular per request but it does not matter which frontend framework is used.

Everything about expressing 2d bar chart on 3d is very trivial. However, the dimension is different from 2d; the positions have to be swapped.

// code to make a single bar mesh
  makeBar(d: typeof gridData[0]) {
    // length and height is swapped. because camera is looking from 90 degree angle by default.
    const geo = new T.BoxGeometry(d.w, d.l, d.h, 32, 32);
    const mat = new T.MeshPhysicalMaterial({ color: 'red' });
    const mesh = new T.Mesh(geo, mat);
    mesh.position.x = d.x;
    // z and y is also swapped. because of the same reason.
    mesh.position.z = d.y;
    mesh.position.y = d.z;
    return mesh;
  }

then each element is assigned as 3d Group, to make them centered altogether.

app screenshot

EDIT: color scheme was missing. it is now added.

Related