Public
Edited
Jul 12, 2022
Insert cell
# Pride Lane
Insert cell
chart = {
const svg = d3
.create("svg")
.attr("width", width)
.attr(
"height",
height + textHeight + startHeight + 0.25 * widthPerColumn + 15
)
.attr("font-family", "sans-serif");

const noteTypes = notes.map((x) =>
x.filter((r) => r < 0).length > 0 ? "SP" : x.length >= 3 ? "多" : "少"
);

svg
.append("g")
.selectAll("rect")
.data(d3.range(5))
.enter()
.append("rect")
.attr("x", (_, i) => i * widthPerColumn)
.attr("y", 0)
.attr("width", widthPerColumn)
.attr("height", textHeight)
.attr("fill", (_, i) => (i % 2 === 0 ? titleColor : titleColorEven));

svg
.append("rect")
.attr("x", 0)
.attr("y", textHeight)
.attr("height", startHeight)
.attr("width", width)
.attr("fill", titleColor);
svg
.append("text")
.attr("x", width / 2)
.attr("y", textHeight + startHeight / 2)
.attr("fill", "white")
.attr("text-anchor", "middle")
.attr("dominant-baseline", "middle")
.style("font-size", "1.2rem")
.style("font-weight", "bold")
.text("START");

svg
.append("g")
.selectAll("text")
.data(noteTypes)
.enter()
.append("text")
.attr("x", (_, i) => (i + 0.5) * widthPerColumn)
.attr("y", textHeight / 2)
.attr("fill", "white")
.attr("text-anchor", "middle")
.attr("dominant-baseline", "middle")
.style("font-size", "1.2rem")
.style("font-weight", "bold")
.text((x) => x);

svg
.append("g")
.selectAll("rect")
.data(d3.range(5))
.enter()
.append("rect")
.attr("x", (_, i) => i * widthPerColumn)
.attr("y", textHeight + startHeight)
.attr("width", widthPerColumn)
.attr("height", height + 0.25 * widthPerColumn)
.attr("fill", titleColor);

svg
.append("g")
.selectAll("line")
.data(d3.range(4))
.enter()
.append("line")
.attr("x1", (_, i) => (i + 1) * widthPerColumn)
.attr("y1", textHeight + startHeight)
.attr("x2", (_, i) => (i + 1) * widthPerColumn)
.attr("y2", textHeight + startHeight + height + 0.25 * widthPerColumn)
.attr("stroke", "white");

const columns = svg
.append("g")
.selectAll("g")
.data(notes)
.enter()
.append("g");

// SP/A circles
columns
.append("g")
.selectAll("circle")
.data((x, i) => x.map((y) => [y, i]))
.enter()
.append("circle")
.attr("cx", ([__a, i], __b) => (i + 0.5) * widthPerColumn)
.attr(
"cy",
([x, __a], __b) =>
(Math.abs(x) / beat) * height + textHeight + startHeight
)
.attr("r", ([x, __a], __b) => (x < 0 ? 0.25 : 0.15) * widthPerColumn)
.attr("fill", "blue");

// Extra styling for SP circles
columns
.append("g")
.selectAll("circle")
.data((x, i) => x.filter((y) => y < 0).map((y) => [y, i]))
.enter()
.append("circle")
.attr("cx", ([__a, i], __b) => (i + 0.5) * widthPerColumn)
.attr(
"cy",
([x, __a], __b) =>
(Math.abs(x) / beat) * height + textHeight + startHeight
)
.attr("r", 0.15 * widthPerColumn)
.attr("fill", "white")
.attr("fill-opacity", 0.2);

columns
.selectAll("text")
.data((x, i) => x.map((y) => [y, i]))
.enter()
.append("text")
.attr("x", ([__a, i], __b) => (i + 0.5) * widthPerColumn)
.attr(
"y",
([x, __a], __b) =>
(Math.abs(x) / beat) * height + textHeight + startHeight
)
.attr("fill", "white")
.attr("text-anchor", "middle")
.attr("dominant-baseline", "middle")
.attr("font-weight", "bold")
.text(([x, __a], __b) => Math.abs(x));

const columnSkills = svg
.append("g")
.selectAll("g")
.data(skills)
.enter()
.append("g");

// Skill lines
columnSkills
.append("g")
.selectAll("line")
.data((x, i) => x.map((y) => [y, i]))
.enter()
.append("line")
.attr("x1", ([__a, i], __b) => (i + 0.5) * widthPerColumn - skillLineOffset)
.attr("x2", ([__a, i], __b) => (i + 0.5) * widthPerColumn - skillLineOffset)
.attr(
"y1",
([[typ, from, to], __a], __b) =>
(from / beat) * height + textHeight + startHeight
)
.attr(
"y2",
([[typ, from, to], __a], __b) =>
(to / beat) * height + textHeight + startHeight
)
.attr("stroke", ([[typ, from, to], __a], __b) =>
typ === "SP" ? "green" : typ === "A" ? "red" : "white"
)
.attr("stroke-width", ([[typ, from, to], __a], __b) =>
typ === "SP" ? 20 : typ === "A" ? 15 : 10
);

// Skill start notation
columnSkills
.append("g")
.selectAll("text")
.data((x, i) => x.map((y) => [y, i]))
.enter()
.append("text")
.attr("x", ([__a, i], __b) => (i + 0.5) * widthPerColumn - skillLineOffset)
.attr(
"y",
([[typ, from, to], __a], __b) =>
(from / beat) * height + textHeight + startHeight
)
.attr("fill", "white")
.attr("text-anchor", "left")
.attr("dominant-baseline", "middle")
.style("font-size", "1.2rem")
.style("font-weight", "bold")
.text(([[typ, from, to]]) => `${typ}:${from}`);

// Skill end notation
columnSkills
.append("g")
.selectAll("text")
.data((x, i) => x.map((y) => [y, i]))
.enter()
.append("text")
.attr("x", ([__a, i], __b) => (i + 0.5) * widthPerColumn - skillLineOffset)
.attr(
"y",
([[typ, from, to], __a], __b) =>
(to / beat) * height + textHeight + startHeight
)
.attr("fill", "white")
.attr("text-anchor", "left")
.attr("dominant-baseline", "middle")
.style("font-size", "1.2rem")
.style("font-weight", "bold")
.text(([[typ, from, to]]) => `${typ}:${to}`);

return svg.node();
}
Insert cell
notes = [
[11, 82, 157],
[40, 107, -117, 161],
[30, 64, -77, 129, 172],
[35, 56, 93, 125],
[45, 111]
]
Insert cell
skills = [[["SP", 24, 32]], [["A", 25, 49]], [], [], []]
Insert cell
_colX = (center) => (_, i) => (i + center ? 0.5 : 0) * widthPerColumn
Insert cell
beat = 172
Insert cell
width = 600
Insert cell
widthPerColumn = width / 5
Insert cell
height = 800
Insert cell
textHeight = 30
Insert cell
startHeight = 50
Insert cell
titleColorEven = "#32323d"
Insert cell
titleColor = "#484750"
Insert cell
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