Public
Edited
Mar 24, 2023
Fork of Simple D3
1 fork
Insert cell
Insert cell
Insert cell
Insert cell
// chart(filteredImages)
chart(mergelibraryGroups[selectedLibrary], groupsImages[selectedLibrary])
Insert cell
chart = (libs, items) => {
// Chart center point is 0,0

// DONE: add labels
// TODO: scale to fit
// TODO: Zoom
// TODO: Pan
const itemSize = 8;
const itemSpacing = itemSize * 0.25;
const layerSpaceing = itemSize * 0.125;
const layerSize = itemSize;

// todo: Group images needs to be an array not an object
console.log("[chart] --", innerRadius(items.length, itemSize, itemSpacing));

const scale = canvasScale(
libs.length,
items.length,
itemSize,
itemSpacing,
layerSpaceing
);
console.log("[chart] - libs", libs.length);

const scaledWidth = width / scale;
const scaledHeigth = height / scale;
const r = (size) => size / 2;

// const sectionHeight =
// (itemSize + layerSpaceing) * indexedOrderedFilteredLibraries.length;
const sectionHeight = (itemSize + layerSpaceing) * libs.length;
// layerOfGroupLibraries;
const sectionWidth = 2;
const radiusOffset =
sectionHeight + innerRadius(items.length, itemSize, itemSpacing);

const canvas = d3
.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [
-scaledWidth / 2,
-scaledHeigth / 2,
scaledWidth,
scaledHeigth
]);

// Images
const images = canvas
.append("g")
.attr("id", "images")
.selectAll("g")
.data(items);

const imagesEnter = images.enter().append("g");
// const imagesUpdate = images.merge();
// const imagesExit = images.exit();

imagesEnter
.attr("id", (d) => {
return d.id;
})
.attr("class", "image")
.on("click", (event, a) => console.log(a));
// .on("mouseover", (event, a) => console.log(a));

// .join(
// enter => enter.append("text")
// .attr("fill", "green")
// .attr("x", (d, i) => i * 16)
// .attr("y", -30)
// .text(d => d)
// .call(enter => enter.transition(t)
// .attr("y", 0)),
// update => update
// .attr("fill", "black")
// .attr("y", 0)
// .call(update => update.transition(t)
// .attr("x", (d, i) => i * 16)),
// exit => exit
// .attr("fill", "brown")
// .call(exit => exit.transition(t)
// .attr("y", 30)
// .remove())
// );

imagesEnter.append("g").attr("class", "label").attr("fill", "grey");
imagesEnter.append("g").attr("class", "files");

// Labeling Images
imagesEnter
.select(".label")
.append("text")
.text((d) => d.id)
.attr("text-anchor", (d, i) =>
angle(items.length, i) < 180 ? "start" : "end"
)
.attr("transform", (d, i) => {
if (angle(items.length, i) < 180) {
return `rotate(-90, 0, 0) translate(${itemSize + itemSpacing}, 6.5)`;
} else {
return `rotate(90, 0, 0) translate(${-itemSize - itemSpacing}, 4.5)`;
}
});

imagesEnter
.select(".label")
.append("circle")
.attr("r", r(itemSize))
.attr("cx", r(itemSize))
.attr("cy", -itemSpacing)
.style("fill", "none");

imagesEnter.attr(
"transform",
(d, i) => `
translate(0, ${0 - radiusOffset})
rotate(${angle(items.length, i)}, ${0}, ${radiusOffset} )
`
);

// Files
const fileTrans = canvas.transition().duration(750);
const delay = 50;

const fileUpdate = imagesEnter
.select(".files")
.selectAll("circle")
.data((d) => d.layers);

const fileEnter = fileUpdate
.enter()
.append("circle")
.attr("class", "file")
.attr("cx", 0)
.attr("cy", (d) => {
// const layer = layerOfFilteredLibrary(d.library_name || "__NONE__");
const layer = layerOfGroupLibraries(d.library_name || "__NONE__");
const offset = (itemSize + layerSpaceing) * layer;
return offset + r(itemSize);
})
.attr("r", 0)
.style("fill", (d) => {
return cameraModelStyling?.[d.camera_model_name]?.["fill"] || "red";
})
.attr("opacity", 0.4)
.call((update) =>
update
.transition(fileTrans)
.delay((d, i) => i * delay)
.attr("r", (d) => {
const image = items.find((element) => element.id == d.file_name);
const isSame = image.checksums.size == 1;
const scale = isSame ? 0.5 : 1;

return r(itemSize) * scale;
})
);

const fileExit = fileUpdate
.exit()
.call((exit) => exit.transition(fileTrans).attr("r", 0).remove());

// imagesExit.transition().duration(750).style("fill-opacity", 0).remove();

// fileEnter
// .attr("class", "file")
// .attr("cx", r(itemSize))
// .attr("cy", (d) => {
// const layer = layerOfFilteredLibrary(d.library_name || "__NONE__");
// const offset = (itemSize + layerSpaceing) * layer;
// return offset + r(itemSize);
// })
// .attr("r", (d) => {
// const image = items.find((element) => element.id == d.file_name);
// const isSame = image.checksums.size == 1;
// const scale = isSame ? 0.5 : 1;

// return r(itemSize) * scale;
// })
// .style("fill", (d) => {
// // if (!cameraModelStyling.hasOwnProperty(d.camera_model_name)) {
// // console.log(d);
// // }

// return cameraModelStyling?.[d.camera_model_name]?.["fill"] || "red";
// })
// .attr("stroke", (d) => {
// return cameraModelStyling?.[d.camera_model_name]?.["stroke"] || "black";
// })
// .attr("stroke-width", (d) => {
// return cameraModelStyling?.[d.camera_model_name]?.["stroke-width"] || 0;
// })
// .attr("opacity", 0.5);

// .attr("opacity", (d) => {
// const image = items.find((element) => element.id == d.file_name);
// const isSame = image.checksums.size == 1;

// return isSame ? 0.25 : 1;
// });

const guides = canvas.append("g").attr("id", "guides");
// Inner Guide ;)
const innerGuide = false;
if (innerGuide) {
guides
.append("circle")
.attr("id", "center")
.attr("cx", 0)
.attr("cy", 0)
.attr("r", innerRadius(items.length, itemSize, itemSpacing))
.attr("stroke-width", 1)
.attr("stroke-opacity", 0.4)
.attr("stroke", "indianred")
.attr("fill", "none");
}

const layerGuide = false;
if (layerGuide) {
guides
.selectAll(".layerGuide")
.data(d3.range(0, libs.length))
.join("circle")
.attr("class", "layerGuide")
.attr("cx", 0)
.attr("cy", 0)
.attr("r", (d) => {
const ir = innerRadius(items.length, itemSize, itemSpacing);
const f = (itemSize + layerSpaceing) * d;
return ir + f;
})
.style("fill", "none")
.style("stroke", "black")
.style("stroke-opacity", 0.1);
}

return canvas.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
indexedOrderedLibraries = {
return Object.keys(libraries).sort(sortNames);
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
__indexedOrderedFilteredLibraries = indexOuterToInnerRadius(__filteredLibraries)
Insert cell
layerOfFilteredLibrary = (library) =>
__indexedOrderedFilteredLibraries.indexOf(library)
Insert cell
layerOfLibrary = (library) => indexedOrderedLibraries.indexOf(library)
Insert cell
indexOuterToInnerRadius = (object) => {
return Object.keys(object).sort(
(a, b) => object[b.length] - object[a.length]
);
}
Insert cell
layerOfGroupLibraries = (library) => {
const group = selectedLibrary;
return mergelibraryGroups[group].indexOf(library);
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
matrix_rowNodes = indexedOrderedLibraries
Insert cell
matrix_columnNodes = indexedOrderedLibraries
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
// {
// for (let index = 0; index < indexedOrderedLibraries.length; ++index) {
// console.log(index, indexedOrderedLibraries[index]);
// }
// }
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