如何在cytoscape.js中使用bezier曲线

时间:2017-01-13 15:41:49

标签: cytoscape.js

好的,不要笑。我正在尝试制作弯曲的边缘,绕过源和目标之间的节点。我无法弄清楚该怎么做(我读http://js.cytoscape.org/#style/bezier-edges,但不理解它),所以我把假节点放在从源到目标的路上空的地方,并制作了一系列边缘。结果非常荒谬:

really bad edges

这样做的正确方法是什么?我的目标是稍微优雅一点,比如:

来自http://twitter.github.io/labella.js/

http://twitter.github.io/labella.js/

根据@maxkfranz的建议,我确实更接近了:

enter image description here enter image description here

但最终决定放弃。这花了太长时间。回到直边。如果有人读过这篇文章并且可以描述一种在Cytoscape.js或其他工具中实现我的目标的方法,我很乐意听到它。

为了清楚我在放弃之前所做的事情,我:

  • 在网格上布置节点,使得之间始终存在空的网格单元 同一行上的任何两个节点(这不是理想的,因为空 细胞应优选尽可能窄)
  • 表示源节点和目标节点之间网格布局上的每一行:
    • 指定距离来源最近的直接路径的“航点”(或 前一个航路点)目标是通过一个空的网格单元,
    • 将航点的行/列坐标转换为像素坐标,
    • 将它们转换为距离/重量值(基于所描述的功能) here
    • 将这些用于非捆绑贝塞尔控制点。

这是我的代码中最相关的部分:

  function waypoints(from, to) {
    let stubPoint = { row: from.data().row, col: from.data().col, 
                      distance: 0, weight: .5, };
    if (from.data().layer === to.data().layer)
      return [stubPoint];
    let fromCol = from.data().col, 
        curCol = fromCol,
        toCol = to.data().col,
        fromRow = from.data().row,
        curRow = fromRow,
        toRow = to.data().row,
        fromX = from.position().x,
        fromY = from.position().y,
        toX = to.position().x,
        toY = to.position().y,
        x = d3.scaleLinear().domain([fromCol,toCol]).range([fromX,toX]),
        y = d3.scaleLinear().domain([fromRow,toRow]).range([fromY,toY]);
    let rowsBetween = _.range(fromRow, toRow).slice(1);
    let edgeLength = pointToPointDist(x(fromCol),y(fromRow),x(toCol),y(toRow));
    let points = rowsBetween.map(
      (nextRow,i) => {
        let colsRemaining = toCol - curCol;
        let colsNow = Math.ceil(colsRemaining / Math.abs(toRow - curRow));
        let nextCol = findEmptyGridCol(nextRow, curCol, curCol + colsNow);
        let curX = x(curCol), curY = y(curRow),
            nextX = x(nextCol), nextY = y(nextRow);
        let [distanceFromEdge, distanceOnEdge] = 
              perpendicular_coords(curX, curY, toX, toY, nextX, nextY);

        let point = {curCol, curRow, 
                      toCol, toRow,
                      nextCol, nextRow,
                      curX, curY, toX, toY, nextX, nextY,
                      edgeLength,
                      colsNow,
                      //wayCol, wayRow,
                      distance:-distanceFromEdge * 2, 
                      weight:distanceOnEdge / edgeLength,
                    };
        curCol = nextCol;
        curRow = nextRow;
        return point;
      });
    if (points.length === 0)
        return [stubPoint];
    return points;
  }

1 个答案:

答案 0 :(得分:2)

边缘的控制点集由N个权重定义(w 1 ,w 2 ,... w N )和N个相应的距离(d 1 ,d 2 ,... d N )。

权重定义了点与源或目标的接近程度,距离控制点距离源 - 目标线的距离。距离的符号控制曲线所在的源 - 目标线的一侧(即手性)。有关详细信息,请参阅文档:http://js.cytoscape.org/#style/bezier-edges

为简单起见,下图总结了单个点的值,但您可以将其扩展到多个点:

Weight and distance

请注意,上图为edge-distances: node-position,而下图假定为edge-distances: intersection(默认值):

enter image description here

对于复杂的用例,edge-distances: node-position使计算更容易 - 但您必须更加小心,不要在节点体中指定点。这些例子的最终结果几乎相同,所以我没有更新曲线。不同大小的较大节点会使这些情况之间的差异更明显。

bezier控制点与控制点不相交。控制点"拉动"朝向这一点的曲线。对于像Cytoscape中的二次贝塞尔曲线,曲线的距离将是控制点的一半。有关贝塞尔人的更多信息,请参阅维基百科:https://en.wikipedia.org/wiki/B%C3%A9zier_curve

另一方面,分段边缘将与您指定的点相交,因为它们只是一系列直线:http://js.cytoscape.org/#style/segments-edges