Skip to content

Instantly share code, notes, and snippets.

@abellaismail7
Last active August 31, 2023 18:40
Show Gist options
  • Save abellaismail7/ad00634893450845433d68eeb8f10931 to your computer and use it in GitHub Desktop.
Save abellaismail7/ad00634893450845433d68eeb8f10931 to your computer and use it in GitHub Desktop.
rectdonut.js
const D3colors = [
"#88d9cc",
"#edbd69",
"#f4e884",
"#f69787",
"#edd0b7",
"#c6d",
"#f4f",
];
function createRect(svg, it, klass = "scalable rect", props) {
var color = it.color ? it.color : "black";
svg
.append("g")
.attr("class", klass)
.attr("transform", it.transform)
.append("rect")
.attr("x", props.h / 2 + it.xstart * props.rectw)
.attr("y", it.y)
.attr("width", (it.xend - it.xstart) * props.rectw)
.attr("height", props.outerRadius - props.innerRadius)
.attr("fill", color);
}
function createArc(svg, it, klass = "scalable", props) {
var color = it.color ? it.color : "black";
const cl = 2 * Math.PI;
const arc = d3
.arc()
.innerRadius(props.innerRadius)
.outerRadius(props.outerRadius)
.startAngle(it.start * cl)
.endAngle(it.end * cl);
svg
.append("g")
.attr("transform", it.transform)
.append("path")
.attr("d", arc)
.attr("class", klass)
.attr("fill", color)
.append("arc");
}
function createRectArc(svg, it, props) {
const g = svg.append("g").attr("class", "scalable tr").attr("fill", it.color);
it.shapes.forEach((it) => {
if (it.type === "rect") {
createRect(g, it, "", props);
return;
}
createArc(g, it, "", props);
});
}
function rectData(props, val, index, offset, mode) {
if (mode === "rect2") {
console.log(offset, val);
return {
type: "rect",
xstart: 1 - (offset + val) / props.rectw,
xend: 1 - offset / props.rectw,
y: props.h - (props.outerRadius - props.innerRadius),
color: D3colors[index % D3colors.length],
transform: `translate(0, 0)`,
};
}
return {
type: "rect",
xstart: offset / props.rectw,
xend: (offset + val) / props.rectw,
y: 0,
color: D3colors[index % D3colors.length],
transform: `translate(0, 0)`,
};
}
function arcData(props, val, index, offset, mode) {
const arcLen = Math.PI * props.h;
const is2 = mode === "arc2" ? 0.5 : 0;
return {
type: "arc",
start: offset / arcLen + is2,
end: (offset + val) / arcLen + is2,
color: D3colors[index % D3colors.length],
transform:
mode === "arc2"
? `translate(${props.h / 2}, ${props.h / 2})`
: `translate(${props.w - props.h / 2}, ${props.h / 2})`,
};
}
function getShape(offset, props) {
if (offset < props.rectw) {
return "rect";
}
if (offset < props.rectw + Math.PI * (props.h / 2)) return "arc";
if (offset < props.rectw * 2 + Math.PI * (props.h / 2)) return "rect2";
return "arc2";
}
function getNextData(props, val, index, offset) {
const bounds = {
rect: 0,
arc: props.rectw,
rect2: props.rectw + Math.PI * (props.h / 2),
arc2: props.rectw * 2 + Math.PI * (props.h / 2),
};
let data;
let rest = 0;
const arcLen = Math.PI * (props.h / 2);
const mode = getShape(offset, props);
const ctxOffset = offset - bounds[mode];
if (mode === "rect" || mode === "rect2") {
rest = ctxOffset + val - props.rectw;
data = rectData(
props,
rest > 0 ? props.rectw - ctxOffset : val,
index,
ctxOffset,
mode
);
} else if (mode === "arc" || mode === "arc2") {
rest = ctxOffset + val - arcLen;
data = arcData(
props,
rest > 0 ? arcLen - ctxOffset : val,
index,
ctxOffset,
mode
);
}
return [rest, data];
}
function getData(props, val, index, offset) {
const bounds = {
rect: props.rectw,
arc: props.rectw + Math.PI * (props.h / 2),
rect2: props.rectw * 2 + Math.PI * (props.h / 2),
arc2: props.total,
};
const begins = {
rect: 0,
arc: props.rectw,
rect2: props.rectw + Math.PI * (props.h / 2),
arc2: props.rectw * 2 + Math.PI * (props.h / 2),
};
const type = getShape(offset, props);
if (offset + val > bounds[type]) {
const res = {
type: "rectarc",
color: D3colors[index % D3colors.length],
shapes: [],
};
do {
const [rest, data] = getNextData(props, val, index, offset);
offset += val - rest;
val = rest;
res.shapes.push(data);
if (rest <= 0.01) break;
} while (true);
return res;
}
if (type === "rect" || type === "rect2")
return rectData(props, val, index, offset - begins[type], type);
if (type === "arc" || type === "arc2")
return arcData(props, val, index, offset - begins[type], type);
throw new Error("unknown type");
}
function couponToData(coupons, props) {
const ctotal = coupons.reduce((acc, it) => acc + it.longeur, 0);
const scale = props.total / ctotal;
let offset = 0;
const data = [];
for (const [index, it] of coupons.entries()) {
data.push(getData(props, it.longeur * scale, index, offset));
offset += it.longeur * scale;
}
return data;
}
const setup = (props, parent, data) => {
const svg = d3
.select(parent)
.append("svg")
.attr("viewBox", "0 0 " + props.w + " " + props.h);
data.forEach((it) => {
if (it.type === "rect") {
createRect(svg, it, "", props);
} else if (it.type === "arc") {
createArc(svg, it, "", props);
} else if (it.type === "rectarc") {
createRectArc(svg, it, props);
}
});
};
window.FT_setup = setup;
window.FT_couponToData = couponToData;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment