I am trying to replace the colored circles with image circles. The following plot is made using d3 in javascript. I cannot add image due to my less than 10 rating.
// https://observablehq.com/@d3/smooth-zooming@58
function _chart(width,height,d3,data,radius)
{
let currentTransform = [width / 2, height / 2, height];
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height])
const g = svg.append("g");
g.selectAll("circle")
.data(data)
.join("circle")
.attr("cx", ([x]) => x)
.attr("cy", ([, y]) => y)
.attr("r", radius)
.attr("fill", (d, i) => d3.interpolateRainbow(i/360))
function transition() {
const d = data[Math.floor(Math.random() * data.length)];
const i = d3.interpolateZoom(currentTransform, [...d, radius * 2 + 1]);
g.transition()
.delay(250)
.duration(i.duration)
.attrTween("transform", () => t => transform(currentTransform = i(t)))
.on("end", transition);
}
function transform([x, y, r]) {
return `
translate(${width / 2}, ${height / 2})
scale(${height / r})
translate(${-x}, ${-y})
`;
}
return svg.call(transition).node();
}
function _height(){return(
500
)}
function _radius(){return(
2
)}
function _step(radius){return(
radius * 2
)}
function _data(step,theta,width,height){return(
Array.from({length: 2000}, (_, i) => {
const r = step * Math.sqrt(i += 0.5), a = theta * i;
return [
width / 2 + r * Math.cos(a),
height / 2 + r * Math.sin(a)
];
})
)}
function _theta(){return(
Math.PI * (3 - Math.sqrt(5))
)}
function _d3(require){return(
require("d3@6")
)}
export default function define(runtime, observer) {
const main = runtime.module();
main.variable(observer("chart")).define("chart", ["width","height","d3","data","radius"], _chart);
main.variable().define("height", _height);
main.variable().define("radius", _radius);
main.variable().define("step", ["radius"], _step);
main.variable().define("data", ["step","theta","width","height"], _data);
main.variable().define("theta", _theta);
main.variable().define("d3", ["require"], _d3);
return main;
}
I think that we might need to alter the data function. I was not able to show it with images as I am a beginner in Javascript.
First off, your immediate question is:
I am trying to replace the colored circles with image circles.
This has been answered a number of times on StackOverflow, for example here.
Doing so in the context of d3 is a bit different, though, since you'll probably want some code along the lines of:
let url = ...
let defs = svg.append("defs");
defs
.append("pattern")
...
.attr("id", 'image')
.append("image")
...
.attr("href", url);
There are more details on this below.
Also, the code block that you've posted looks to me like it was returned by the Observalbe embedding API, i.e.: https://api.observablehq.com/@d3/smooth-zooming.js?v=3
I've forked that notebook and made the changes necessary to include a few images. Here's the fork and here's the proper way to embed that code:
<div id="observablehq-chart-25b97cbc"></div>
<script type="module">
import {Runtime, Inspector} from "https://cdn.jsdelivr.net/npm/@observablehq/runtime@5/dist/runtime.js";
import define from "https://api.observablehq.com/d/adfba0cfb4396df0.js?v=3";
new Runtime().module(define, name => {
if (name === "chart") return new Inspector(document.querySelector("#observablehq-chart-25b97cbc"));
});
</script>
If you examine the forked notebook, you should notice a code block near the top that looks something like so:
image_urls = [
"https://upload.wikimedia.org/wikipedia/commons/3/3d/Perspectiva_Corporum_Regularium_36a.jpg",
"https://upload.wikimedia.org/wikipedia/commons/7/7f/Hexaeder_NdFeB_5041.jpg",
"https://upload.wikimedia.org/wikipedia/commons/f/f6/Hexader_1.jpg"
]
I guess that could be just about any list of publicly accessible URLs pointing to images.
The next code block, that defines the chart
, contains a few lines that look like:
let defs = svg.append("defs");
image_urls.forEach(function (url, i) {
defs
.append("pattern")
.attr("id", `image${i}`)
.attr("x", "0%")
.attr("y", "0%")
.attr("width", "100%")
.attr("height", "100%")
.attr("viewBox", "50 50 200 100")
.append("image")
.attr("x", "0%")
.attr("y", "0%")
.attr("width", "100%")
.attr("height", "100%")
.attr("href", url);
});
That sets up the patterns that you can use as fills. Finally, that bit is used later when the circles are joined:
g.selectAll("circle")
.data(data)
.join("circle")
...
.attr("fill", (_, i) => `url(#image${i % image_urls.length}`);