【问题标题】:venn.js selecting Set[A] except Set[B]venn.js 选择 Set[A] 除了 Set[B]
【发布时间】:2016-05-17 23:48:31
【问题描述】:

我有这个代码:

var sets = [
    {sets: ['A'], size: 10},
    {sets: ['B'], size: 10},
    {sets: ['A','B'], size: 5}
];

var chart = venn.VennDiagram();
var div = d3.select("#venn").datum(sets).call(chart);

使用优秀的venn.js 库,我的维恩图绘制得很好。

使用此代码:

            div.selectAll("g")
                .on("mouseover", function (d, i) {
                    // sort all the areas relative to the current item
                    venn.sortAreas(div, d);

                    // Display a tooltip with the current size
                    tooltip.transition().duration(400).style("opacity", .9);
                    tooltip.text(d.size + " items");

                    // highlight the current path
                    var selection = d3.select(this).transition("tooltip").duration(400);
                    selection.select("path")
                        .style("stroke-width", 3)
                        .style("fill-opacity", d.sets.length == 1 ? .4 : .1)
                        .style("stroke-opacity", 1)
                        .style("cursor", "pointer");
                })

                .on("mousemove", function () {
                    tooltip.style("left", (d3.event.pageX) + "px")
                           .style("top", (d3.event.pageY - 28) + "px");
                })

                .on("click", function (d, i) {
                    window.location.href = "/somepage"
                })

                .on("mouseout", function (d, i) {
                    tooltip.transition().duration(400).style("opacity", 0);
                    var selection = d3.select(this).transition("tooltip").duration(400);
                    selection.select("path")
                        .style("stroke-width", 1)
                        .style("fill-opacity", d.sets.length == 1 ? .25 : .0)
                        .style("stroke-opacity", 0);
                });

我可以向我的 venn 添加点击、鼠标悬停...功能。

问题出在这里:

向圈子(集合 A 或 B)添加功能可以正常工作。

向交集(Set A intersect Set B)添加功能可以正常工作。

我需要在除区域之外添加一些功能(设置 A 除了设置 B)

这个问题有点帮助:2D Polygon Boolean Operations with D3.js SVG

但我没有成功完成这项工作。

尝试使用 clipperjsGreiner-Hormann polygon clipping algorithm 找出除外区域,但无法正常工作。

更新 1:

本题中的代码复制自 venn.js 示例:http://benfred.github.io/venn.js/examples/intersection_tooltip.html

其他示例: https://github.com/benfred/venn.js/

【问题讨论】:

标签: javascript d3.js svg venn.js


【解决方案1】:

也许你可以做这样的事情......

给定 2 个重叠的圆圈,

  • 找到两个交点,然后
  • 手动创建一条从 IP1 沿着圆 A 到 IP2 的弧形路径,然后从 IP2 沿着圆 B 回到 IP1。

创建该路径后(涵盖 A 不包括 B),您可以根据需要设置它的样式并将点击事件(等)添加到该 SVG 路径元素。

查找交点 (IP)

Circle-circle intersection points

var getIntersectionPoints = function(circleA, circleB){

  var x1 = circleA.cx,
      y1 = circleA.cy,
      r1 = circleA.r,
      x2 = circleB.cx,
      y2 = circleB.cy,
      r2 = circleB.r;

  var d = Math.sqrt(Math.pow(x2-x1,2)+Math.pow(y2-y1,2)),
      a = (Math.pow(r1,2)-Math.pow(r2,2)+Math.pow(d,2))/(2*d),
      h = Math.sqrt(Math.pow(r1,2)-Math.pow(a,2));

  var MPx = x1 + a*(x2-x1)/d,
      MPy = y1 + a*(y2-y1)/d,
      IP1x = MPx + h*(y2-y1)/d,
      IP1y = MPy - h*(x2-x1)/d,
      IP2x = MPx - h*(y2-y1)/d,
      IP2y = MPy + h*(x2-x1)/d;

  return [{x:IP1x,y:IP1y},{x:IP2x,y:IP2y}]

}

手动创建路径

var getExclusionPath = function(keepCircle, excludeCircle){

  IPs = getIntersectionPoints(keepCircle, excludeCircle);

  var start = `M ${IPs[0].x},${IPs[0].y}`,
      arc1 = `A ${keepCircle.r},${keepCircle.r},0,1,0,${IPs[1].x},${IPs[1].y}`,
      arc2 = `A ${excludeCircle.r},${excludeCircle.r},0,0,1,${IPs[0].x},${IPs[0].y}`,
      pathStr = start+' '+arc1+' '+arc2;

  return pathStr;

}

var height = 900;
    width  = 1600;
			
d3.select(".plot-div").append("svg")
		.attr("class", "plot-svg")
		.attr("width", "100%")
    .attr("viewBox", "0 0 1600 900")

var addCirc = function(circ, color){
  d3.select(".plot-svg").append("circle")
  .attr("cx", circ.cx)
  .attr("cy", circ.cy)
  .attr("r", circ.r)
  .attr("fill", color)
  .attr("opacity", "0.5")
}

var getIntersectionPoints = function(circleA, circleB){
  
  var x1 = circleA.cx,
    y1 = circleA.cy,
    r1 = circleA.r,
    x2 = circleB.cx,
    y2 = circleB.cy,
    r2 = circleB.r;
 
var d = Math.sqrt(Math.pow(x2-x1,2)+Math.pow(y2-y1,2)),
    a = (Math.pow(r1,2)-Math.pow(r2,2)+Math.pow(d,2))/(2*d),
    h = Math.sqrt(Math.pow(r1,2)-Math.pow(a,2));

var MPx = x1 + a*(x2-x1)/d,
    MPy = y1 + a*(y2-y1)/d,
    IP1x = MPx + h*(y2-y1)/d,
    IP1y = MPy - h*(x2-x1)/d,
    IP2x = MPx - h*(y2-y1)/d,
    IP2y = MPy + h*(x2-x1)/d;
  
  return [{x:IP1x,y:IP1y},{x:IP2x,y:IP2y}]

}

var getExclusionPath = function(keepCircle, excludeCircle){

  IPs = getIntersectionPoints(keepCircle, excludeCircle);
  
  var start = `M ${IPs[0].x},${IPs[0].y}`,
      arc1 = `A ${keepCircle.r},${keepCircle.r},0,1,0,${IPs[1].x},${IPs[1].y}`,
      arc2 = `A ${excludeCircle.r},${excludeCircle.r},0,0,1,${IPs[0].x},${IPs[0].y}`,
      pathStr = start+' '+arc1+' '+arc2;
  
  return pathStr;
  
}



var circleA = {cx: 600, cy: 500, r: 400};
var circleB = {cx: 900, cy: 400, r: 300};

var pathStr = getExclusionPath(circleA, circleB)

addCirc(circleA, "steelblue");
addCirc(circleB, "darkseagreen");

d3.select(".plot-svg").append("text")
  .text("Hover over blue circle")
  .attr("font-size", 70)
  .attr("x", 30)
  .attr("y", 70)

d3.select(".plot-svg").append("path")
  .attr("class","exlPath")
  .attr("d", pathStr)
  .attr("stroke","steelblue")
  .attr("stroke-width","10")
  .attr("fill","white")
  .attr("opacity",0)
.plot-div{
  width: 50%;
  display: block;
  margin: auto;
}

.plot-svg {
  border-style: solid;
  border-width: 1px;
  border-color: green;
}

.exlPath:hover {
  opacity: 0.7;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

<div class="plot-div">

</div>

如果您的维恩图中有更复杂的重叠(3 个以上区域重叠),那么这显然会变得更复杂,但我认为您仍然可以针对这些情况扩展这种方法。

快速(有点)注意处理 3 组交叉点,即。 A∩B\C 或 A∩B∩C

A∩B和圆C之间有3个“层次”重叠...

  • 完全包含在 C 中 | 两个 AB IP 都在 C 中
  • 部分重叠; C“切入”A∩B | C 中只有一个 AB IP
  • A∩B 完全在 C 之外 | C 中没有 AB IP

注意:这是假设 C 不是 A 或 B 的子集或完全包含在 A 或 B 中——否则,例如,两个 BC IP 都可能包含在 A 中

总共需要 3 个点来为 3 个重叠的圆圈创建路径。前 2 个沿着 C,它“穿过”A∩B。那些是……

  1. A中包含的BC交点
  2. B中包含的AC交点

对于路径的第三个点,这取决于您想要 (i)A∩B∩C 还是 (ii)A∩B\C...

  1. (i) A∩B∩C:C中包含的AB交点

    (ii)A∩B\C:C中包含的AB交点NOT

通过这些点,您可以使用适当的弧线手动绘制路径。


奖金 -- 获得 2 个圈子的任何小节

同样值得注意的是,您可以通过选择正确的 large-arc-flagsweep-flag 来获得任何小节。明智地挑选,你会得到......

  1. 圆 A(作为路径)
  2. 圆 B(作为路径)
  3. A 排除 B --在所示示例中
  4. B 排除 A
  5. 联合 B
  6. A 相交 B

...以及一些更时髦的,不匹配任何有用的东西。

一些资源...

W3C site for elliptical curve commands

Good explanation for arc flags

大圆弧标志:A value of 0 means to use the smaller arc, while a value of 1 means use the larger arc.

扫旗:The sweep-flag determines whether to use an arc (0) or its reflection around the axis (1).

【讨论】:

  • 非常感谢您的出色回答。我将奖励你答案点和赏金点。如果有两个圆圈,这个解决方案会很完美,但是 3 个圆圈呢?如果我们想要 [SetA + SetB - SetC] 怎么办?需要哪些改变来实现这一点?您能否更新您的代码或指导我正确的方向来计算和突出显示 [(SetA + SetB) - SetC]?
  • 不幸的是,超过 3 个圈子会变得更棘手。我正在考虑编写一些代码来处理任意集合操作/组合——如果我这样做了,我会在这里更新。这可能类似于用一些额外的功能重写 Venn.js(我对 Venn.js 不太熟悉),并且可能需要付出很大的努力。至少,我会添加一个关于如何处理 3 个圆圈/组的注释。
  • 谢谢史蒂夫。我相信你的回答会让我走上正确的道路。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-09-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-01
  • 2017-08-05
  • 1970-01-01
相关资源
最近更新 更多