0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【CytoscapeJS】複合ノード内にラベルを配置する

Posted at

はじめに

CYtoscapeJSでのlabelの付け方は以下の種類が用意されています。

そして、Styleにはこのようにプロパティ定義をすることで実現できます。

ではもし、例えば下記画像のように複合ノード内にタイトルを含めたい場合はどうすればいいのでしょうか?

スクリーンショット 2022-08-20 20.15.30.png

以下、本記事ではノードを包括する親ノードを複合ノードと呼びます。

indicates the compound node parent id
Elements JSON

より。

結論

/** TextMetricsの取得 */
const getMeasureText = (target: cytoscape.NodeCollection | cytoscape.EdgeCollection): TextMetrics => {
  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext( '2d' )!;
  const fontStyle = target.style('font-style');
  const fontSize = target.style('font-size');
  const fontFamily = target.style('font-family');
  const fontWeight = target.style('font-weight');

  ctx.font = fontStyle + ' ' + fontWeight + ' ' + fontSize + ' ' + fontFamily;

  return ctx.measureText(target.data('name'));
}

/** 横幅の取得 */
const getCharacterWidthInCanvas = (target: cytoscape.NodeCollection | cytoscape.EdgeCollection): number => {
  const labelWidthMargin = 10;

  const measure = getMeasureText(target);

  return measure.width + labelWidthMargin;
}

/** 高さの取得 */
const getCharacterHeightInCanvas = (target: cytoscape.NodeCollection | cytoscape.EdgeCollection): number => {
  const labelHeightMargin = 10;

  const measure = getMeasureText(target);

  return measure.actualBoundingBoxAscent + measure.actualBoundingBoxDescent + labelHeightMargin;
}

/** --- generate cytoscape --- */

cytoscape({
  container: /** target dom */,
  layout: /** layout */,
  elements: {
    nodes: /** nodes */,
    edges: /** edges */,
  },
  style: [
    {
      selector: 'node',
      css: {
        content: 'data(name)',
        width: getCharacterWidthInCanvas,
        shape: 'round-rectangle',
        'text-valign': 'center',
        'text-halign': 'center',
      },
    },
    {
      selector: ':parent',
      css: {
        padding: getCharacterHeightInCanvas,
        'text-valign': 'bottom',
        'text-halign': 'center',
        // @ts-ignore
        'text-margin-y': (node: cytoscape.NodeCollection) => -getCharacterHeightInCanvas(node),
      },
    },
  ],
});

複合ノードに paddingtext-margin-y を与えてやります。

説明

getCharacterWidthInCanvasgetCharacterHeightInCanvas で与えられた文字列の横幅と高さを計算しています。(【CytoscapeJS】The style value of label is deprecated for widthを参考にしてください)
また、見栄えも気にして+αで固定パディング値も与えてやりましょう。

すでに node に対してcssで content: 'data(name)' を与えているため、複合ノードも囲っているとはいえノードの一種、ラベルが適用されます。

CytoscapeJSのつまづきポイントとして、ノードを囲う、いわゆるグループの役割を果たすノードが親ノードとして parent で定義します。木構造を知っていると親ノードはより上位のノードを指すため、parentでエッジを貼るのかと思えますが、実際は複合ノードをparentで定義します。私はここでつまづきました。
さて、CSSの擬似クラスチックに selector: ':parent' と定義してやり複合ノードを指定します。
その中にパディングでラベルを入れられるだけのパディングを作成します。これは用いるレイアウトエンジンによりますが、ラベルとノードがかぶる危険を排除するためです。
そして空いたスペースに text-margin-y でラベルを動かして挿入します。負の値を与えることでマージンがめり込むので、それを活用しています。
@types/cytoscapeを利用しているのですが、型定義が上手く聞いてくれなかったため、 @ts-ignore をつけています。

これで複合ノード内にラベルを配置することができました!

スクリーンショット 2022-08-20 20.40.21.png

おわりに

2022年8月現在、cytoscapeJSの型定義ファイルでは型定義が間違っているように思える部分がいくつかあります。そういうときはあまり好ましく無いですが、ts-ignoreや型アサーションなどを活用しましょう。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?