Public
Edited
Apr 22, 2022
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
const text = await FileAttachment("desk_playground_3.svg").text();
const document = new DOMParser().parseFromString(text, "image/svg+xml");
const map_svg = d3.select(document.documentElement).remove();
const theMap = map_svg.node();

const svg = d3
.create("svg")
.attr("width", theMap.getAttribute("width"))
.attr("height", theMap.getAttribute("height"))
.attr("id", theMap.getAttribute("id"))
.attr("class", "modified") //theMap.getAttribute("class"))
.attr("viewBox", theMap.getAttribute("viewBox"))
.style("cursor", "crosshair");

try {
const deskSymbol = d3.select("svg.modified symbol#desk");
const oldVB = decompose_viewbox(deskSymbol.attr("viewBox"));
const desk_width = oldVB.width;
const desk_height = oldVB.height;

// move the symbol's insertion point to the desired insertion point
// at the moment, this doesn't work at all
if(true) {
const desks = d3.selectAll("svg.modified use");
desks.each((_, i, collection) => {
const el = d3.select(collection[i]);
if(true) { //(el.attr("xlink:href") == "#desk" && !el.classed("translated") {
const initial_transform = extract_transform(el.attr("transform"));
const newX = initial_transform.x + desk_width * 0.5;
const newY = initial_transform.y + desk_height * 0.5;
const newTranslate = `translate(${newX} ${newY})`; // rotate(${initial_transform.rot})`)
el.attr("transform", newTranslate);
el.classed("translated", true);
console.log(
el.attr("xlink:href"),
el.classed("translated"),
initial_transform,
newTranslate,
el.attr("transform")
)
}
});
}

// modify viewbox of symbol for the desk
if(false){
if (deskSymbol.classed("viewbox_moved")) {
console.log("viewbox already moved, not doing it again");
} else {
deskSymbol
.attr("viewBox", translate_viewbox(deskSymbol.attr("viewBox"), 0, 0, 3))
.attr("width", null)
.attr("height", null)
.classed("viewbox_moved", true);
console.log("viewBox", deskSymbol.attr("viewBox"));
}
}

// Move the contents of the symbol definition back into the right place again.
if(false) {
deskSymbol.selectChildren().each((_, i, collection) => {
const el = d3.select(collection[i]);
const vb = decompose_viewbox(deskSymbol.attr("viewBox"));
const x_trans = vb.width / 2;
const y_trans = vb.height / 2;
const trans = `translate(${-x_trans} ${-y_trans})`;
el.attr("transform", trans);
console.log(el, "trans", trans);
});
}
// and by here, everything should look the same
} catch (error) {
console.error(error, "probably the first time through?");
/* I don't know what's going on here. The svg is modified every time this cell
runs, rather than reloading it, and running the transform on a fresh version.
I assume it's a caching thing. There's a correct way to either force a fresh
load, or to do a deep copy, or something?
*/
}

//The rest of this is handling the zooming, and some decorations
// x and y are scales that project the data space to the ‘unzoomed’ pixel referential
const x = d3.scaleLinear([0, 1], [0, 1]);
const y = d3.scaleLinear([0, 1], [0, 1]);

const g = svg.append("g");

g.node().appendChild(theMap);

console.log("data", data);
const origin_points = g
.selectAll("circle")
.data(data)
.join("circle")
.attr("cx", (d) => Math.round(x(d.x)))
.attr("cy", (d) => Math.round(y(d.y)))
.attr("r", 1)
.attr("style", "fill: none; stroke: blue; stroke-width: 1;");

let transform;

const zoom = d3.zoom().on("zoom", (e) => {
g.attr("transform", (transform = e.transform));
g.style("stroke-width", 3 / Math.sqrt(transform.k));
});

return svg
.call(zoom)
.call(zoom.transform, d3.zoomIdentity)
.node();
}
Insert cell
decompose_viewbox = (viewbox_string) => {
const parts = viewbox_string.split(" ");
const min_x = parseFloat(parts[0]);
const min_y = parseFloat(parts[1]);
const width = parseFloat(parts[2]);
const height = parseFloat(parts[3]);
return {min_x, min_y, width, height}
}
Insert cell
translate_viewbox = (viewbox_string, newCenX, newCenY, dp) => {
const coords = decompose_viewbox(viewbox_string)
// console.log("coords", coords)
const nvb = {
min_x: (coords.min_x + newCenX - (coords.width * 0.5)).toFixed(dp),
min_y: (coords.min_y + newCenY - (coords.height * 0.5)).toFixed(dp),
width: coords.width.toFixed(dp),
height: coords.height.toFixed(dp)
}
return `${nvb.min_x} ${nvb.min_y} ${nvb.width} ${nvb.height} `
}
Insert cell
[
translate_viewbox("0 0 100 100", -10, -10),
translate_viewbox("0 0 100 100", 10, 10),
translate_viewbox("0 0 100 100", 0, 0),
translate_viewbox("0 0 100 100", 50, 50),
]
Insert cell
d3 = require("d3@6")
Insert cell
desk_data = get_desk_data()
Insert cell
construct_data = ()=> {
let data = []
desk_data.forEach(d => {
if (Math.random() > 0) { //Change this number to give sparser fills
const payload = {
x: d.x,
y: d.y,
rot: d.rot,
desk: d.desk,
dock: d.dock,
name: d.rot + ", desk:" + d.desk + " dock:" + d.dock
}
data.push(payload);
}
})
return data
}
Insert cell
data = construct_data()
Insert cell
construct_data()
Insert cell
get_desk_data()
Insert cell
async function get_desk_data(){
const text = await FileAttachment("desk_playground_3.svg").text();
const document = (new DOMParser).parseFromString(text, "image/svg+xml");
const map_svg = d3.select(document.documentElement).remove();
const desks = map_svg.selectAll("use")
console.log(desks)
let desk_attrs = [];
const deskData = desks.each((d, i, j) => {
const desk_group = d3.select(j[i])
const tranform_components = extract_transform(desk_group.attr("transform"))
console.log(tranform_components)
desk_attrs.push({...tranform_components})
})
return desk_attrs
}
Insert cell
function extract_transform(transform){
const regex = /translate\((\d+\.*\d*) (\d+\.*\d*)\)( rotate\((-*\d+)\))*/g;

let m;
while ((m = regex.exec(transform)) !== null) {
// This is necessary to avoid infinite loops with zero-width matches
if (m.index === regex.lastIndex) {
regex.lastIndex++;
}
let rot = parseInt(m[4], 10);
if (!isNaN(rot) ){rot = 0}
return {
x: parseInt(m[1], 10),
y: parseInt(m[2], 10),
rot: parseInt(m[4], 10) | 0,
check_transform_string: m[0]
}
}
console.log("failed transform", transform)
}
Insert cell
deg2Rad = (degrees) => degrees * Math.PI / 180
Insert cell
move_to_chair = (data, dist, angle_nudge_in_degrees) => {
const ang = deg2Rad(data.rot + angle_nudge_in_degrees)
const dx = dist * Math.cos(ang);
const dy = -dist * Math.sin(ang);
const trans_string = `translate(${dx.toFixed(1)} ${dy.toFixed(1)})`
console.log(
"move_to_chair " + data.message,
{data, dist, angle_nudge_in_degrees, ang, dx, dy, trans_string}
)
return trans_string
}
Insert cell
[
move_to_chair({rot:0, message: "🍟this one"}, 9, 135),
// move_to_chair({rot:45}, 10, 90),
// move_to_chair({rot:90}, 10, 90),
// move_to_chair({rot:270}, 10, 90),
// move_to_chair({rot:-45}, 10, 90),
// move_to_chair({rot:360}, 10, 90),
]
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