Upon careful examination of the images, I have determined that the top corners should be treated as outer corners. This method can be adjusted to accommodate various corner configurations.
To achieve the desired outcome, the most direct approach is to make modifications to the d3-arc source code and craft a personalized D3 module.
To integrate this functionality, it is essential to remove specific lines of code:
// Does the sector’s inner ring (or point) have rounded corners?
else if (rc0 > epsilon) {
t0 = cornerTangents(x10, y10, x11, y11, r0, -rc0, cw);
t1 = cornerTangents(x01, y01, x00, y00, r0, -rc0, cw);
context.lineTo(t0.cx + t0.x01, t0.cy + t0.y01);
// Have the corners merged?
if (rc0 < rc) context.arc(t0.cx, t0.cy, rc0, atan2(t0.y01, t0.x01), atan2(t1.y01, t1.x01), !cw);
// Otherwise, draw the two corners and the ring.
else {
context.arc(t0.cx, t0.cy, rc0, atan2(t0.y01, t0.x01), atan2(t0.y11, t0.x11), !cw);
context.arc(0, 0, r0, atan2(t0.cy + t0.y11, t0.cx + t0.x11), atan2(t1.cy + t1.y11, t1.cx + t1.x11), cw);
context.arc(t1.cx, t1.cy, rc0, atan2(t1.y11, t1.x11), atan2(t1.y01, t1.x01), !cw);
}
This specific code snippet deals with rounding the inner corners. By removing this segment, only the code responsible for rounding the outer corners will remain. The anticipated result should mirror the provided image.
I have constructed a basic example by integrating the customized source code into an existing module (for chevron arcs) without full compilation, thus avoiding unnecessary dependencies. While not suitable for production purposes, this demonstration effectively showcases the adjustments and allows you to assess the impact. The sample implementation is outlined below.
The new arc function can be accessed using `d3.customArc()` instead of `d3.arc()`, operating similarly to `d3.arc()` but excluding cornerRadius application to the interior corners.
let data = [
{name: "A", value: 100 },
{name: "B", value: 200 },
{name: "C", value: 300 },
{name: "D", value: 400 },
]
const width = 400;
const radius = 150;
const height = 400;
const arc = d3.customArc()
.innerRadius(radius * 0.67)
.outerRadius(radius - 1)
.cornerRadius(20);
const pie = d3.pie()
.padAngle(1 / radius)
.sort(null)
.value(d => d.value);
const color = d3.scaleOrdinal()
.domain(data.map(d => d.name))
.range(d3.quantize(t => d3.interpolateSpectral(t * 0.8 + 0.1), data.length).reverse());
const svg = d3.select("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [-width / 2, -height / 2, width, height])
.attr("style", "max-width: 100%; height: auto;");
svg.append("g")
.selectAll()
.data(pie(data))
.join("path")
.attr("fill", d => color(d.data.name))
.attr("d", arc)
.append("title")
.text(d => `${d.data.name}: ${d.data.value.toLocaleString()}`);
svg.append("g")
.attr("font-family", "sans-serif")
.attr("font-size", 12)
.attr("text-anchor", "middle")
.selectAll()
.data(pie(data))
.join("text")
.attr("transform", d => `translate(${arc.centroid(d)})`)
.call(text => text.append("tspan")
.attr("y", "-0.4em")
.attr("font-weight", "bold")
.text(d => d.data.name))
.call(text => text.filter(d => (d.endAngle - d.startAngle) > 0.25).append("tspan")
.attr("x", 0)
.attr("y", "0.7em")
.attr("fill-opacity", 0.7)
.text(d => d.data.value.toLocaleString("en-US")));
<svg width="960" height="960"></svg>
<script src="https://d3js.org/d3.v7.min.js"></script>
<script src="d3customArc.js"></script>
...
(function (global, factory) {
...
}(this, (function (exports,d3Path) { 'use strict';
...
Object.defineProperty(exports, '__esModule', { value: true });
})));
</script>
The comprehensive source code for d3-arc referenced in this response can be accessed here.