如何在 d3 的两个不同坐标 system/transforms 中用一条线连接两个点?
How to connect two points with a line in two different coordinate system/transforms in d3?
我试图通过连接 4 组点来创建闭合路径,但这些点位于 2 个不同的 transforms
。
例如,我有三个饼图,其中两个使用 d3.layout.pack()
并且在更大的饼图中:
var width = 700,
height = 500,
radius = Math.min(width, height) / 2,
thickness = 20,
length_inner = 2 * Math.sqrt(Math.pow(radius, 2) / 2);
var data = [{
"name": "bob",
"toward": "against",
"fruit": "apple",
"value": 5
}, {
"name": "rob",
"toward": "for",
"fruit": "apple",
"value": 9
}, {
"name": "alice",
"toward": "for",
"fruit": "orange",
"value": 3
}, {
"name": "mike",
"toward": "against",
"fruit": "orange",
"value": 6
}, {
"name": "katy",
"toward": "for",
"fruit": "orange",
"value": 8
}]
var data_inner = _(data).chain()
.groupBy("fruit")
.map(function(v, k) {
return {
"name": k,
"sum": _(v).chain()
.pluck("value")
.reduce(function(memo, num) {
return memo + num;
}, 0)
.value(),
"data": v
};
})
.value()
var svg = d3.select("svg")
.attr({
"width": width,
"height": height
})
.append("g")
.attr("transform", "translate(" + (width / 2) + "," + (height / 2) + ")");
var outer = svg
.append("g")
.attr("class", "outer");
var color_outer = d3.scale.category20b();
var arc_outer = d3.svg.arc()
.innerRadius(radius - thickness)
.outerRadius(radius);
var pie_outer = d3.layout.pie()
.value(function(d) {
return d.value;
})
.sort(null)
.padAngle(.02);
var path_outer = outer.selectAll("path")
.data(pie_outer(data))
.enter()
.append("g")
.attr("class", "arc_outer")
.append("path")
.attr("d", arc_outer)
.attr("fill", function(d, i) {
return color_outer(d.data["name"]);
});
var inner = svg
.append("g")
.attr({
"class": "inner",
"transform": "translate(" + -(length_inner / 2) + "," + -(length_inner / 2) + ")"
});
var bubble_inner = d3.layout.pack()
.value(function(d) {
return d.sum;
})
.sort(null)
.size([length_inner, length_inner])
.padding(10);
var node_inner = inner.selectAll("g.node_inner")
.data(
bubble_inner.nodes({
children: data_inner
})
.filter(function(d) {
return !d.children;
})
)
.enter()
.append("g")
.attr({
"class": "node_inner",
"transform": function(d) {
return "translate(" + d.x + "," + d.y + ")";
}
});
var arc_inner = d3.svg.arc();
var pie_inner = d3.layout.pie()
.value(function(d) {
return d.value;
});
var arc_inner_g = node_inner.selectAll("g.arc_inner")
.data(function(d) {
return pie_inner(d.data).map(function(m) {
m.r = d.r;
return m;
});
});
arc_inner_g.enter()
.append("g")
.attr("class", "arc_inner")
.append("path")
.attr("d", function(d) {
arc_inner.innerRadius(d.r - thickness);
arc_inner.outerRadius(d.r);
return arc_inner(d);
})
.style("fill", function(d, i) {
return d.data.toward === "for" ? "#2ca02c" : "d62728";
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<body>
<svg>
</svg>
</body>
现在我正尝试用贝塞尔曲线从饼图的外部到内部连接两个点,如下所示:
然后关闭另外两个点创建一个<path>
,稍后填充(抱歉,Photoshop 技能太差):
我试图避免使用一个统一的坐标系。即使我必须这样做,我也不确定该怎么做,因为我不知道如何在没有 transforms
.
的情况下创建两个内圆
我能够使用几个 d3.path()
函数绘制路径:
// d is the data attached to the outer pie but also includes
// the respective inner pie's angles, cx, cy, and radius
function link_d_path(d) {
if(d.value === 0) return "";
var r_o = radius - thickness,
offset = -Math.PI / 2,
path_o_start = d.startAngle + offset,
path_o_end = d.endAngle + offset,
cx_i = -length_inner / 2 + d.node_x,
cy_i = -length_inner / 2 + d.node_y,
path_i_start = d.inner_startAngle + offset,
path_i_end = d.inner_endAngle + offset,
angle_diff = ((path_o_start + path_o_end) / 2 + (path_i_start + path_i_end) / 2) / 2,
r_i = d.node_r,
path = d3_path.path();
path.arc(0, 0, r_o, path_o_start, path_o_end);
path.bezierCurveTo(
r_o * Math.cos(path_o_end),
r_o * Math.sin(path_o_end),
cx_i + (r_i + link_radius_cp_offset) * Math.cos(path_i_end),
cy_i + (r_i + link_radius_cp_offset) * Math.sin(path_i_end),
cx_i + r_i * Math.cos(path_i_end),
cy_i + r_i * Math.sin(path_i_end)
);
path.arc(cx_i, cy_i, r_i, path_i_end, path_i_start, true);
path.bezierCurveTo(
cx_i + (r_i + link_radius_cp_offset) * Math.cos(path_i_start),
cy_i + (r_i + link_radius_cp_offset) * Math.sin(path_i_start),
r_o * Math.cos(path_o_start),
r_o * Math.sin(path_o_start),
r_o * Math.cos(path_o_start),
r_o * Math.sin(path_o_start)
);
return path.toString();
}
我只需要在贝塞尔曲线期间从极坐标转换为笛卡尔曲线。 path.arc()
已经取了极坐标。
请注意,length_inner
是内部饼图的 cx 和 cy 相对于外部饼图的 cx 和 cy 的偏移量。
我试图通过连接 4 组点来创建闭合路径,但这些点位于 2 个不同的 transforms
。
例如,我有三个饼图,其中两个使用 d3.layout.pack()
并且在更大的饼图中:
var width = 700,
height = 500,
radius = Math.min(width, height) / 2,
thickness = 20,
length_inner = 2 * Math.sqrt(Math.pow(radius, 2) / 2);
var data = [{
"name": "bob",
"toward": "against",
"fruit": "apple",
"value": 5
}, {
"name": "rob",
"toward": "for",
"fruit": "apple",
"value": 9
}, {
"name": "alice",
"toward": "for",
"fruit": "orange",
"value": 3
}, {
"name": "mike",
"toward": "against",
"fruit": "orange",
"value": 6
}, {
"name": "katy",
"toward": "for",
"fruit": "orange",
"value": 8
}]
var data_inner = _(data).chain()
.groupBy("fruit")
.map(function(v, k) {
return {
"name": k,
"sum": _(v).chain()
.pluck("value")
.reduce(function(memo, num) {
return memo + num;
}, 0)
.value(),
"data": v
};
})
.value()
var svg = d3.select("svg")
.attr({
"width": width,
"height": height
})
.append("g")
.attr("transform", "translate(" + (width / 2) + "," + (height / 2) + ")");
var outer = svg
.append("g")
.attr("class", "outer");
var color_outer = d3.scale.category20b();
var arc_outer = d3.svg.arc()
.innerRadius(radius - thickness)
.outerRadius(radius);
var pie_outer = d3.layout.pie()
.value(function(d) {
return d.value;
})
.sort(null)
.padAngle(.02);
var path_outer = outer.selectAll("path")
.data(pie_outer(data))
.enter()
.append("g")
.attr("class", "arc_outer")
.append("path")
.attr("d", arc_outer)
.attr("fill", function(d, i) {
return color_outer(d.data["name"]);
});
var inner = svg
.append("g")
.attr({
"class": "inner",
"transform": "translate(" + -(length_inner / 2) + "," + -(length_inner / 2) + ")"
});
var bubble_inner = d3.layout.pack()
.value(function(d) {
return d.sum;
})
.sort(null)
.size([length_inner, length_inner])
.padding(10);
var node_inner = inner.selectAll("g.node_inner")
.data(
bubble_inner.nodes({
children: data_inner
})
.filter(function(d) {
return !d.children;
})
)
.enter()
.append("g")
.attr({
"class": "node_inner",
"transform": function(d) {
return "translate(" + d.x + "," + d.y + ")";
}
});
var arc_inner = d3.svg.arc();
var pie_inner = d3.layout.pie()
.value(function(d) {
return d.value;
});
var arc_inner_g = node_inner.selectAll("g.arc_inner")
.data(function(d) {
return pie_inner(d.data).map(function(m) {
m.r = d.r;
return m;
});
});
arc_inner_g.enter()
.append("g")
.attr("class", "arc_inner")
.append("path")
.attr("d", function(d) {
arc_inner.innerRadius(d.r - thickness);
arc_inner.outerRadius(d.r);
return arc_inner(d);
})
.style("fill", function(d, i) {
return d.data.toward === "for" ? "#2ca02c" : "d62728";
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<body>
<svg>
</svg>
</body>
现在我正尝试用贝塞尔曲线从饼图的外部到内部连接两个点,如下所示:
然后关闭另外两个点创建一个<path>
,稍后填充(抱歉,Photoshop 技能太差):
我试图避免使用一个统一的坐标系。即使我必须这样做,我也不确定该怎么做,因为我不知道如何在没有 transforms
.
我能够使用几个 d3.path()
函数绘制路径:
// d is the data attached to the outer pie but also includes
// the respective inner pie's angles, cx, cy, and radius
function link_d_path(d) {
if(d.value === 0) return "";
var r_o = radius - thickness,
offset = -Math.PI / 2,
path_o_start = d.startAngle + offset,
path_o_end = d.endAngle + offset,
cx_i = -length_inner / 2 + d.node_x,
cy_i = -length_inner / 2 + d.node_y,
path_i_start = d.inner_startAngle + offset,
path_i_end = d.inner_endAngle + offset,
angle_diff = ((path_o_start + path_o_end) / 2 + (path_i_start + path_i_end) / 2) / 2,
r_i = d.node_r,
path = d3_path.path();
path.arc(0, 0, r_o, path_o_start, path_o_end);
path.bezierCurveTo(
r_o * Math.cos(path_o_end),
r_o * Math.sin(path_o_end),
cx_i + (r_i + link_radius_cp_offset) * Math.cos(path_i_end),
cy_i + (r_i + link_radius_cp_offset) * Math.sin(path_i_end),
cx_i + r_i * Math.cos(path_i_end),
cy_i + r_i * Math.sin(path_i_end)
);
path.arc(cx_i, cy_i, r_i, path_i_end, path_i_start, true);
path.bezierCurveTo(
cx_i + (r_i + link_radius_cp_offset) * Math.cos(path_i_start),
cy_i + (r_i + link_radius_cp_offset) * Math.sin(path_i_start),
r_o * Math.cos(path_o_start),
r_o * Math.sin(path_o_start),
r_o * Math.cos(path_o_start),
r_o * Math.sin(path_o_start)
);
return path.toString();
}
我只需要在贝塞尔曲线期间从极坐标转换为笛卡尔曲线。 path.arc()
已经取了极坐标。
请注意,length_inner
是内部饼图的 cx 和 cy 相对于外部饼图的 cx 和 cy 的偏移量。