Published
Edited
Oct 26, 2019
3 stars
Insert cell
md`# Volume Grammar`
Insert cell
{
const svg = d3.create("svg").attr("viewBox", [-width / 2, -height / 2, width, height]);
var cellData = rootGrid.concat(refinedData);
var cells = createPaths(cfg, cellData);
var interpolators = [flubber.interpolateAll(cells.disk, cells.memory),
flubber.interpolateAll(cells.memory, cells.cyl),
flubber.interpolateAll(cells.cyl, cells.disk)];
var cellColor = d3.scaleSequential(d3.interpolateSpectral).domain([0, cellData.length - 1]);
svg.selectAll("path")
.data(cellData)
.enter()
.append("path")
.attr("stroke", "black")
.attr("fill", (d, flatIndex) => cellColor(flatIndex))
.attr("fill-opacity", 0.75)
.attr("d", (d, path_i) => cells.disk[path_i]);
yield svg.node();
var transitionIndex = 0;
while (true) {
await svg.selectAll("path")
.data(cellData)
.transition()
.delay((d, flatIndex) => (flatIndex + 1) * 10)
.duration(2000)
.attrTween("d", function(d, path_i) { return interpolators[transitionIndex % 3][path_i]})
.end();
await Promises.delay(5000);
yield svg.node();
transitionIndex += 1;
}
}
Insert cell
{
var svg = d3.create("svg").attr("viewBox", [-width / 2, -height / 2, width, height]);
var complexConfig = defaultConfig([32, 32], [ [ [-128.0, 128.0], [ 20.0, 200.0 ] ],
[ [-128.0, 128.0], [ 0.0, Math.PI ] ] ] );
var cellData = createCells(complexConfig, 0, [32, 32], [0, 0], 0);
cellData.forEach( d => d.value = kh[d.j][d.i] );
var cellPaths = createPaths(complexConfig, cellData, 10);
var cellColor = d3.scaleSequential(d3.interpolateViridis).domain([0.95, 2.15]);
console.log(cellData[0]);
console.log(complexConfig);
svg.selectAll("path")
.data(cellData)
.enter()
.append("path")
.attr("stroke", "lightgrey")
.attr("stroke-width", 1.0)
.attr("stroke-opacity", 0.5)
.attr("fill", (d, flatIndex) => cellColor(d.value))
.attr("fill-opacity", 1.0)
.attr("d", (d, path_i) => cellPaths.memory[path_i]);
yield svg.node();
await Promises.delay(5000);
svg.selectAll("path")
.data(cellData)
.transition()
.duration(2000)
.attr("fill-opacity", d => (d.value > 1.5 ? 0.85 : 0.5))
.attr("transform", "translate(-100, 0)");
}
Insert cell
{
var svg = d3.create("svg").attr("viewBox", [-width/2, -height/2, width, height]);
yield svg.node();
var complexConfig = defaultConfig([32, 32], [ [ [-128.0, 128.0], [ 20.0, 200.0 ] ],
[ [-128.0, 128.0], [ 0.0, Math.PI ] ] ] );
var cellData = createCells(complexConfig, 0, [32, 32], [0, 0], 0);
var fields = ["density", "temperature", "velocity_x", "velocity_y",
"velocity_z", "velocity_magnitude"];
var extents = {};
fields.forEach( v => {
cellData.forEach( d => {
d[v] = galaxy0030[v][d.j][d.i]
})
extents[v] = d3.extent(cellData, d=>d[v]);
});
var cellPaths = createPaths(complexConfig, cellData, 10);
var vel_fields = [extents["velocity_x"], extents["velocity_y"], extents["velocity_z"]];
extents["velocity_total"] = [d3.min(vel_fields, d=>d[0]), d3.max(vel_fields, d=>d[1])];
var colorScales = {
density: d3.scaleSequentialLog(d3.interpolateViridis).domain(extents["density"]),
temperature: d3.scaleSequentialLog(d3.interpolateInferno).domain(extents["temperature"]),
velocity_x: d3.scaleSequentialSymlog(d3.interpolateRdBu).domain(extents["velocity_total"]),
velocity_y: d3.scaleSequentialSymlog(d3.interpolateRdBu).domain(extents["velocity_total"]),
velocity_z: d3.scaleSequentialSymlog(d3.interpolateRdBu).domain(extents["velocity_total"]),
velocity_magnitude: d3.scaleSequentialLog(d3.interpolateReds).domain(extents["velocity_magnitude"])
};
console.log(extents);
svg.selectAll("path")
.data(cellData)
.enter()
.append("path")
.attr("stroke", "lightgrey")
.attr("stroke-width", 1.0)
.attr("stroke-opacity", 0.5)
.attr("fill-opacity", 1.0)
.attr("d", (d, path_i) => cellPaths.memory[path_i])
.attr("fill", (d) => (colorScales["density"](d["density"])));
await Promises.delay(2500);
for (var vi = 0; vi < fields.length ; vi++) {
var v = fields[vi];
svg.selectAll("path")
.data(cellData)
.transition()
.duration(2000)
.attr("stroke", "lightgrey")
.attr("fill", (d) => (colorScales[v](d[v])))
.attr("fill-opacity", 1.0)
.attr("d", (d, path_i) => cellPaths.memory[path_i]);
yield svg.node();
await Promises.delay(5000);
};
}
Insert cell
{
var svg = d3.create("svg").attr("viewBox", [0, 0, width, height]);
var cellData = kelvinHelmholtzAMR.cellData;
var cellPaths = kelvinHelmholtzAMR.cellPaths;
var tree = kelvinHelmholtzAMR.tree;
var cellColor = d3.scaleSequential(d3.interpolateViridis).domain([0.95, 2.15]);
var cellColorBW = d3.scaleSequential(d3.interpolateGreys).domain([0.95, 2.15]);
cellData.forEach( d => d.selected = false);
var maxCells = d3.max(d3.nest().key(d => d.level).rollup(d => d.length).entries(cellData), d=>d.value);
function brushed() {
var extent = d3.event.selection;
var newLevels = new Array(3).fill(0);
tree.visit(findOverlappingNodesRect(extent, nd => {
newLevels[nd.level] += 1;
nd.selected = true;
}));
imageRect.filter(d=>!d.selected)
.style("fill", d => cellColorBW(d.density))
imageRect.filter(d=>d.selected)
.style("fill", d => cellColor(d.density))
.each(d=>{d.selected=false;});
svg.select("g#barchart")
.selectAll("rect")
.data(newLevels)
.transition()
.attr("y", d=> histY(d))
.attr("height", d => height - (32 + histY(d)));
}
var histY = d3.scaleLinear()
.domain([0, maxCells]).nice()
.range([height - 32, 32]);
var histX = d3.scaleBand()
.domain([0, 1, 2])
.range([512 + 48, width - 64])
.padding(0.1)
svg.append("g").attr("transform", "translate(560,0)").call(d3.axisLeft(histY));
svg.append("g").attr("transform", "translate(0," + (height - 32) + ")").call(d3.axisBottom(histX));
var brush = d3.brush().extent([[0.0, 0.0], [512, 512]]).on("brush", brushed);
var imageRect = svg.append("g")
.attr("id", "image")
.selectAll("rect")
.data(cellData)
.enter()
.append("rect")
.attr("class", "selectable")
.attr("stroke", "lightgrey")
.attr("stroke-width", 1.0)
.attr("stroke-opacity", 0.5)
.style("fill", (d, flatIndex) => cellColor(d.density))
.attr("x", (d, flatIndex) => cellData[flatIndex].x[0])
.attr("y", (d, flatIndex) => cellData[flatIndex].y[0])
.attr("width", (d, flatIndex) => cellData[flatIndex].width[0])
.attr("height", (d, flatIndex) => cellData[flatIndex].height[0])
.each(d=>{d.selected=false});
svg.append("g")
.attr("id", "barchart")
.selectAll("rect")
.data([0, 0, 0])
.enter()
.append("rect")
.attr("x", (d, i) => histX(i))
.attr("y", d=> histY(d) + 32)
.attr("y", d=> height - 32 - histY(d))
.attr("width", histX.bandwidth())
.attr("fill", "");
yield svg.node();
svg.append("g")
.attr("class", "brush")
.call(brush)
.call(brush.move, [[0, 0], [512, 512]]);
yield svg.node();
let v = await Promises.delay(2500);
svg.select("g.brush")
.transition()
.duration(2500)
.call(brush.move, [[20, 20], [50, 50]]);
yield svg.node();
console.log("done")
}
Insert cell
{
var svg = d3.create("svg").attr("viewBox", [0, 0, width, height]);
var viewExtent = [[0, 0], [width, height]];
var cellData = kelvinHelmholtzAMR.cellData;
var cellPaths = kelvinHelmholtzAMR.cellPaths;
var tree = kelvinHelmholtzAMR.tree;
var cellColor = d3.scaleSequential(d3.interpolateGreys).domain([0.95, 2.15]);
var biggestCell = [d3.max(cellData, d=>d.width[0]), d3.max(cellData, d=>d.height[0])];
console.log("biggest", biggestCell);
svg.append("g")
.attr("id", "image")
.selectAll("rect")
.data(cellData)
.enter()
.append("rect")
.attr("class", "selectable")
.attr("stroke", "darkgrey")
.attr("stroke-width", 1.0)
.attr("stroke-opacity", 0.5)
.style("fill", (d, flatIndex) => cellColor(d.density))
.style("fill-opacity", 0.2)
.attr("x", (d, flatIndex) => cellData[flatIndex].x[0])
.attr("y", (d, flatIndex) => cellData[flatIndex].y[0])
.attr("width", (d, flatIndex) => cellData[flatIndex].width[0])
.attr("height", (d, flatIndex) => cellData[flatIndex].height[0])
.each(d=>{d.selected=false});
var line = d3.line()
.curve(d3.curveCatmullRom.alpha(0.5));
var myPath = svg.append("g")
.attr("id", "pathintegration")
.append("path")
.attr("d", line(myPoints))
.attr("stroke", "none")
.attr("display", "none")
.style("fill", "none").node();
yield svg.node();
var length = myPath.getTotalLength();
var Nsegments = 200;
var dt = length / Nsegments;
var pathSegments = d3.range(Nsegments).map( d => {
var segment = {x1: myPath.getPointAtLength(d * dt).x,
y1: myPath.getPointAtLength(d * dt).y,
x2: myPath.getPointAtLength((d+1) * dt).x,
y2: myPath.getPointAtLength((d+1) * dt).y,
i: d};
tree.visit(findOverlappingNodesPoint([segment.x2, segment.y2], biggestCell, nd => {
segment.density = nd.density;
segment.velocity_x = nd.velocity_x;
segment.velocity_y = nd.velocity_y;
segment.level;
}));
return segment;
});

var pathsegments = svg.append("g").attr("id", "pathsegments")
.selectAll("line")
.data(pathSegments)
.enter()
.append("line")
.attr("x1", d=>d.x1)
.attr("y1", d=>d.y1)
.attr("x2", d=>d.x2)
.attr("y2", d=>d.y2)
.style("stroke", "#7C4DFF")
.style("stroke-width", 2.0)
.style("fill", "#7C4DFF")
.style("stroke-opacity", 0.0)
.style("fill-opacity", 0.0);

var linePlotY = d3.scaleLinear()
.domain(d3.extent(pathSegments, d=>d.density))
.range([height - 32, 32]);
var linePlotX = d3.scaleLinear()
.domain([0, Nsegments])
.range([512 + 48, width - 64]);
svg.append("g").attr("transform", "translate(560,0)").call(d3.axisLeft(linePlotY));
svg.append("g").attr("transform", "translate(0," + (height - 32) + ")").call(d3.axisBottom(linePlotX));
var linequery = svg.append("g")
.attr("id", "linequery")
.selectAll("line")
.data(pathSegments.slice(1))
.enter()
.append("line")
.attr("fill", "#7C4DFF")
.attr("stroke", "#7C4DFF")
.attr("stroke-width", 1.5)
.style("stroke-opacity", 0.0)
.style("fill-opacity", 0.0)
.attr("x1", (d, i) => linePlotX(i-1))
.attr("x2", (d, i) => linePlotX(i))
.attr("y1", (d, i) => linePlotY(pathSegments[i].density))
.attr("y2", (d, i) => linePlotY(d.density));

var circle = svg
.append("circle")
.attr("cx", myPoints[0][0])
.attr("cy", myPoints[0][1])
.attr("r", 5.0)
.style("fill", "#2196F3");
var circleQuery = svg
.append("circle")
.attr("cx", linePlotX(0))
.attr("cy", linePlotY(pathSegments[0].density))
.attr("r", 5.0)
.style("fill", "#2196F3");
var lastPos = myPath.getPointAtLength(0.0);
for (var segi = 0; segi < Nsegments; segi++) {
var t = d3.transition().duration(10);
circle.transition(t)
.attr("cx", pathSegments[segi].x2)
.attr("cy", pathSegments[segi].y2);
pathsegments
.filter(d => (d.i <= segi))
.transition(t)
.style("fill-opacity", 1.0)
.style("stroke-opacity", 1.0);
circleQuery.transition(t)
.attr("cx", linePlotX(segi))
.attr("cy", linePlotY(pathSegments[segi].density));
linequery
.filter(d => (d.i <= segi))
.transition(t)
.style("fill-opacity", 1.0)
.style("stroke-opacity", 1.0);
await t.end();
yield svg.node();
}
}
Insert cell
import { createPaths, defaultConfig, createCells, ringermacherMead, rootGrid, refinedData, cfg, kelvinHelmholtzAMR, galaxy0030, kh, findOverlappingNodesRect, findOverlappingNodesPoint } from "@matthewturk/grid-cells";
Insert cell
myPoints = [[18, 13], [381, 79], [443, 228], [381, 331], [239, 338], [160, 183], [88, 316], [184, 441], [109, 491]];
Insert cell
height = Math.min(width, 500);
Insert cell
d3 = require("d3@5");
Insert cell
d3rect = require("d3-rect");
Insert cell
flubber = require("flubber");
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more