【问题标题】:how to use bezier curves in cytoscape.js如何在 cytoscape.js 中使用贝塞尔曲线
【发布时间】:2017-01-19 21:32:01
【问题描述】:

好了,别笑了。我正在尝试制作弯曲的边缘,绕过源和目标之间的节点。我不知道怎么做(我读了http://js.cytoscape.org/#style/bezier-edges,但没看懂),所以我把假节点放在从源到目标的路上空的地方,并做了一系列的边缘。结果很可笑:

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

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

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

但最终还是决定放弃。时间太长了回到直边。如果有人读过这篇文章并且可以描述一种在 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;
  }

【问题讨论】:

    标签: cytoscape.js


    【解决方案1】:

    边的控制点集由 N 个权重定义(w1, w2, ... wN)和N个各自的距离(d1, d2, ... dN)。

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

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

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

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

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

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

    【讨论】:

    • 谢谢,@maxfranz!我正要问如何指定控制点以使曲线通过多个点,但后来我发现:stackoverflow.com/questions/7715788/…。第一个答案表明我需要使用多条连接曲线,第二个答案表明我可能能够用一条曲线获得一个很好的近似值(我认为)。
    • @Sigfried 您可以为每条边指定多个控制点,但每个点只为单个二次贝塞尔曲线指定一个点。这允许您将多个二次贝塞尔曲线链接在一起以获得一条边。曲线会自动为您连接。请记住,指定的点拉出曲线的极值——它们没有指定曲线上的点。
    • 再次感谢@maxfranz。我快到那里了。另一个问题:我正在为节点使用网格布局,但我认为控制点/段距离需要以像素为单位。是否有一些直接的方法可以将行/列转换为像素,或者我是否需要通过找到(在 x 方向上)两个节点的 col 距离与其 position.x 距离之间的比率来计算它?
    • 在布局完成之前,您不会知道结束位置。即使是像网格这样简单的布局也不仅仅是一个单行公式。
    • 谢谢,@maxfranz。我认为这花费了太长时间并放弃了(描述了我在上面学到的知识),但这部分,我最终通过从布局后的节点中提取 x/y 位置并使用它们来计算控制点而不是行/列位置。
    猜你喜欢
    • 2013-01-21
    • 2011-03-10
    • 2023-03-27
    • 2016-10-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多