elkjs:基于 JavaScript 的图布局算法库项目

ELK's layout algorithms for JavaScript

分支43Tags19
文件最后提交记录最后更新时间
5 个月前
5 个月前
3 年前
1 个月前
1 个月前
1 个月前
2 年前
8 年前
6 年前
1 个月前
5 个月前
11 个月前
7 年前
3 年前
3 年前
1 个月前
1 个月前

npm version

Eclipse Layout Kernel (ELK) 实现了一种基础设施,用于将图表编辑器或查看器连接到自动布局算法。本库提取了 ELK 中与布局相关的部分,并将其应用于 JavaScript 环境。ELK 的核心是一种基于层的布局算法,特别适用于具有内在方向和端口(节点边界上的显式连接点)的节点链接图。它基于 Sugiyama 等人最初提出的思想。下方截图展示了一个示例。

请注意,elkjs 本身并非绘图框架,它仅计算图表元素的位置。

elkjs 是 klayjs 的继任者。

演示

您可以通过 TypeFox 的 sprotty 绘图框架查看 elkjs 的 在线演示。如果 elklive 暂时无法访问,您可以使用已发布的 docker 容器 自行运行和托管。

常见问题与常见问题

以下列表包含一些可能已在 issue 中解决的常见主题:

  • #100elk#355elk#627 - 如何考虑先前的布局结果,包括

    • 动态布局,以及
    • 向现有布局中增量添加节点和边。
  • elk#315:独立边路由。

  • #85 elkjs 本身是一个图形布局引擎。换句话说,它不提供渲染、样式等功能。

  • #127#141#142 - 由于 GWT 进行的底层代码转译和/或过时的 js 模块使用导致的问题:

    • g is not defined
    • Can't resolve web-worker,以及
    • 在 react、webpack 等环境中的常规使用问题。
    • #6 模块化程度不足
    • 欢迎对此处提供贡献!

安装

最新发布版本:

npm install elkjs

基于 ELK master 分支的开发版本。

npm install elkjs@next

发布与版本控制

版本发布在一定程度上与 ELK 的版本保持同步:次要版本号始终一致,但修订号可能会有所不同。例如,elkjs 0.3.0 的功能与 ELK 0.3.0 相当,但 elkjs 0.3.2 可能与 ELK 0.3.2 不同。这是必要的,因为可能存在仅与 elkjs 相关的修复,需要独立于 ELK 进行发布。

文件

该库包含两个主要文件:

  • elk-api.js:提供 API,并且仅提供 API。
  • elk-worker.js:提供实际执行图布局的代码。此文件是使用 GWT 从 ELK 的 Java 代码库生成的。

另外还提供了两个文件:

  • elk.bundled.js:上述两个文件的捆绑版本,可直接放入浏览器的 <script> 标签中使用。该文件经过 browserify 处理,并且在浏览器环境中会将 ELK 暴露为全局变量。
  • main.js:node.js 模块的主文件。允许通过 require('elkjs') 方便地引入,而无需手动组合上述文件。

示例

一个使用 node.js 的简单示例, 更多使用场景请参见下一节。

const ELK = require('elkjs')
const elk = new ELK()

const graph = {
  id: "root",
  layoutOptions: { 'elk.algorithm': 'layered' },
  children: [
    { id: "n1", width: 30, height: 30 },
    { id: "n2", width: 30, height: 30 },
    { id: "n3", width: 30, height: 30 }
  ],
  edges: [
    { id: "e1", sources: [ "n1" ], targets: [ "n2" ] },
    { id: "e2", sources: [ "n1" ], targets: [ "n3" ] }
  ]
}

elk.layout(graph)
   .then(console.log)
   .catch(console.error)

请注意,如果遇到错误,你可能需要切换到未压缩版本以获取完整的堆栈跟踪。

布局选项

你可以使用_布局选项_来配置布局算法。为此,你需要将一个layoutOptions对象附加到图形元素上,该对象包含表示所需布局选项的键值对。例如,参见上面示例中的root。你可以只使用布局选项的后缀:用algorithm代替org.eclipse.elk.layered。但是,如果后缀不唯一,该布局选项可能会被忽略。为安全起见,布局选项应始终以elk.开头。所有选项的列表及其确切效果的更多详细信息,请参见ELK的文档

可以将全局布局选项作为layout方法第二个参数的一部分进行传递。这些选项将应用于每个图形元素,除非该元素自身指定了该选项:

elk.layout(graph, {
  layoutOptions: { ... }
})

此外,ELK 的构造函数接受一个包含布局选项的对象,该对象会用于所有未指定布局选项的 layout 调用:

const elk = new ELK({
  defaultLayoutOptions: { ... }
})

用法

由于绘制图表布局可能是一项耗时的工作(即使对于计算机而言),而且我们不希望冻结您的用户界面,因此 Web Workers 是开箱即支持的。以下示例说明了该库如何在使用和不使用 Web Worker 的情况下进行使用。

node.js

const ELK = require('elkjs')
// without web worker
const elk = new ELK()

elk.layout(graph)
   .then(console.log)
const ELK = require('elkjs')
// with web worker
const elk = new ELK({
  workerUrl: './node_modules/elkjs/lib/elk-worker.min.js'
})

elk.layout(graph)
   .then(console.log)

自 10.x 版本起,node.js 提供了 worker threads 实现,它与浏览器的 Worker 类相似,但并不完全相同。

为了简化我们这边的实现,我们使用了一个名为 web-worker 的库,该库提供了一个围绕 node 的 worker_threads 的包装器,其 API 与浏览器的 Worker 兼容。不过,任何其他提供标准 Web Worker 方法的库也应该适用。

该包不会自动安装,以避免给那些不感兴趣使用 web worker 的用户带来不必要的依赖。

如果有人在未安装该包的情况下请求 web worker,将会发出警告。在这种情况下,elkjs 会回退到非 Web Worker 版本。

浏览器

<html>
  <script src="./elk.bundled.js"></script>
  <script type="text/javascript">
    const elk = new ELK()

    elk.layout(graph)
       .then(function(g) {
         document.body.innerHTML = "<pre>" + JSON.stringify(g, null, " ") + "</pre>"
       })
  </script>
</html>
<html>
  <script src="./elk-api.js"></script> <!-- use elk-api.js here! -->
  <script type="text/javascript">
    const elk = new ELK({
      workerUrl: './elk-worker.js'
    })

    elk.layout(graph)
       .then(function(g) {
         document.body.innerHTML = "<pre>" + JSON.stringify(g, null, " ") + "</pre>"
       })
  </script>
</html>

TypeScript

import ELK from 'elkjs/lib/elk.bundled.js'
const elk = new ELK()


import ELK from 'elkjs/lib/elk-api'
const elk = new ELK({
  workerUrl: './elk-worker.min.js'
})

调试

出于调试目的,您可能希望使用同样可用的非压缩版本。 在这种情况下,可以按如下方式配置非压缩的 Web Worker 版本:

const ELK = require('elkjs/lib/elk-api.js')
const elk = new ELK({
    workerFactory: function(url) { // the value of 'url' is irrelevant here
        const { Worker } = require('elkjs/lib/elk-worker.js') // non-minified
        return new Worker(url)
    }
})

API

elkjs 库提供了一个对象:ELKELK 有一个可用于构造它的构造函数:

  • new ELK(options) - 可向 ELK 传入选项,所有选项都是可选的:
    • defaultLayoutOptions - 一个包含默认布局选项的对象,这些选项以键值对形式指定。如果未向 layout(graph, options) 方法(见下文)传递更多布局选项,则使用这些默认选项。默认值:{}
    • algorithms - 算法 ID 数组(仅后缀)。默认值:[ 'layered', 'stress', 'mrtree', 'radial', 'force', 'disco' ]。注意,boxfixedrandom 布局器始终包含在内。
    • workerUrl - elk-worker.js 脚本的路径。因此,ELK 将使用 Web Worker 来执行布局。默认值:undefined

除此之外,ELK 还提供以下方法:

  • layout(graph, options)
    • graph - 要进行布局的图,采用 ELK JSON 格式。必填项!
    • options - 配置对象。可选。
      • layoutOptions:其最重要的用途是传递全局布局选项。即,这些布局选项将应用于每个图元素,除非该元素自身指定了该选项。
      • logging:布尔值(自 0.6.0 版本起)。是否应将日志信息作为已布局图的一部分返回。默认值为 false
      • measureExecutionTime:布尔值(自 0.6.0 版本起)。是否应将执行时间(以秒为单位)信息作为已布局图的一部分返回。默认值为 false
    • 返回一个 Promise,成功时传递已布局的图,失败时传递(希望是有帮助的)错误信息。
  • knownLayoutOptions()
    • 返回已知布局选项的数组。对于每个选项,会提供额外信息,例如其 idgroup
  • knownLayoutAlgorithms()
    • 返回已注册布局算法的数组(见上文)以及每个算法的进一步信息。
  • knownLayoutCategories()
    • 返回已注册布局类别的数组。
  • terminateWorker() - 如果使用了 Web Worker,将调用 worker 的 terminate() 方法。

known 开头的三个方法基本上返回的信息,在 Java 环境中,可以从 LayoutMetaDataService 中获取。

日志和执行时间

(自 0.6.0 版本起)

ELK 提供了一些方法,用于在布局算法执行期间记录调试信息。详细内容可在 ELK 文档的 “算法调试” 部分找到。不过,并非所有功能在 elkjs 中都可用,例如,无法保存已布局图形的中间结果。此外,在 Java 端,内部执行时间以纳秒为单位进行测量,而在 elkjs 中,我们不得不采用毫秒。请注意,返回的执行时间以秒为单位。对于小型图形,报告的执行时间常常可能为 0

以下是示例调用和示例输出。

elk.layout(simpleGraph, {
    layoutOptions: {
        'algorithm': 'layered'
    },
    logging: true,
    measureExecutionTime: true
})
{
  "id": "root",
  "children": [ ... ],
  "edges": [ ... ],
  "logging": {
    "name": "Recursive Graph Layout",
    "executionTime": 0.000096,
    "children": [ {
      "name": "Layered layout",
      "logs": [
        "ELK Layered uses the following 17 modules:",
        "   Slot 01: org.eclipse.elk.alg.layered.p1cycles.GreedyCycleBreaker",
            [ ... ]
        "   Slot 16: org.eclipse.elk.alg.layered.intermediate.ReversedEdgeRestorer"
      ],
      "executionTime": 0.000072,
      "children": [ { "name": "Greedy cycle removal", "executionTime": 0.000002 },
                      [ ... ]
                    { "name": "Restoring reversed edges", "executionTime": 0 } ]
    } ]
  }
}

构建

在 Gitpod 中打开

构建时,需要检出 ELK 代码库,且该代码库应与本代码库的检出目录位于同一目录下。具体如下:

some_dir/
 ├── elkjs
 └── elk
npm install
npm run build

对于新版本发布,需要修改以下版本号:

  • package.json 中的 version
  • build.gradle 中的 melk,以及
  • 不要忘记检出 ELK 仓库的正确发布标签。

之后,你可以在 lib 文件夹中找到生成的文件。

当前流程

git checkout -b releases/0.x.x
# Check that the version numbers are correct, if necessary update versions and commit the changes
npm install
npm run build
npm run test
# Add ./lib/ directory and commit
git tag 0.x.x
# Push release branch and tags to remote
git push --tags --set-upstream origin releases/0.x.x
# Create a new release on Github for the new tag and afterwards publish to npm
Once the release is published on Github, the release on npm will be automatically triggered

之后,需将以下版本号更改为下一个发布版本号:

  • package.json 中的 version
  • build.gradle 中的 melk

错误的 npm 标签

可使用 npm dist-tag add elkjs@<version> <latest/next> 更新 npm 上标记错误的版本。

链接

以下是一系列其他项目和网站的链接,可能会有所帮助:

elkjs 的示例用户

注:我们很乐意进一步扩展此列表,因此如果您有项目要添加,请联系我们

致谢

项目介绍

ELK 为 JavaScript 提供的布局算法【此简介由AI生成】

定制我的领域
342.75 K123访问 GitHub