Coco DaWhiteBerry
Coco DaWhiteBerry

Reputation: 91

How to change arrow size in Sigma.js?

I'm trying to draw a directed (arrowed) graph but the arrows are so small you can barely see them.

function addArrowToGraph(src, dst) {
    s.kill();
    g.edges.push({
        id: 'e' + g.edges.length,
        source: 'n' + src,
        target: 'n' + dst,
        size: 100,
        color: '#ccc',
        minArrowSize: 100,
        type: 'arrow'
    });
    s = new sigma({
        graph: g,
        container: 'graph-container'
    });
}

No matter what number I set `minArrowSize` to, it stays the same.  I have to zoom in real close to even see the arrows at all.

Upvotes: 0

Views: 1137

Answers (2)

sgrubsmyon
sgrubsmyon

Reputation: 1229

If someone wonders how to change arrow size in sigma.js v2 without making edges thicker: I managed to do it via implementing a custom edge arrow head program like this:

In your index.js:

import ArrowEdgeProgram from "./edge.arrow";

// [...]

const renderer = new Sigma(graph, container, {
  edgeProgramClasses: {
    arrow: ArrowEdgeProgram
  }
});

Create these two files in the same directory as your index.js: (taken from the sigma.js code, located in src/rendering/webgl/programs, only converted from TypeScript to JS and with correct import paths)

edge.arrow.js:

import { createEdgeCompoundProgram } from "sigma/rendering/webgl/programs/common/edge";
import EdgeArrowHeadProgram from "./edge.arrowHead";
import EdgeClampedProgram from "sigma/rendering/webgl/programs/edge.clamped";

const EdgeArrowProgram = createEdgeCompoundProgram([EdgeClampedProgram, EdgeArrowHeadProgram]);

export default EdgeArrowProgram;

edge.arrowHead.js:

import { floatColor } from "sigma/utils";
import vertexShaderSource from "sigma/rendering/webgl/shaders/edge.arrowHead.vert.glsl";
import fragmentShaderSource from "sigma/rendering/webgl/shaders/edge.arrowHead.frag.glsl";
import { AbstractEdgeProgram } from "sigma/rendering/webgl/programs/common/edge";

const POINTS = 3,
  ATTRIBUTES = 9,
  STRIDE = POINTS * ATTRIBUTES;

export default class EdgeArrowHeadProgram extends AbstractEdgeProgram {
  // Locations
  positionLocation;
  colorLocation;
  normalLocation;
  radiusLocation;
  barycentricLocation;
  matrixLocation;
  sqrtZoomRatioLocation;
  correctionRatioLocation;

  constructor(gl) {
    super(gl, vertexShaderSource, fragmentShaderSource, POINTS, ATTRIBUTES);

    // Locations
    this.positionLocation = gl.getAttribLocation(this.program, "a_position");
    this.colorLocation = gl.getAttribLocation(this.program, "a_color");
    this.normalLocation = gl.getAttribLocation(this.program, "a_normal");
    this.radiusLocation = gl.getAttribLocation(this.program, "a_radius");
    this.barycentricLocation = gl.getAttribLocation(this.program, "a_barycentric");

    // Uniform locations
    const matrixLocation = gl.getUniformLocation(this.program, "u_matrix");
    if (matrixLocation === null) throw new Error("EdgeArrowHeadProgram: error while getting matrixLocation");
    this.matrixLocation = matrixLocation;

    const sqrtZoomRatioLocation = gl.getUniformLocation(this.program, "u_sqrtZoomRatio");
    if (sqrtZoomRatioLocation === null)
      throw new Error("EdgeArrowHeadProgram: error while getting sqrtZoomRatioLocation");
    this.sqrtZoomRatioLocation = sqrtZoomRatioLocation;

    const correctionRatioLocation = gl.getUniformLocation(this.program, "u_correctionRatio");
    if (correctionRatioLocation === null)
      throw new Error("EdgeArrowHeadProgram: error while getting correctionRatioLocation");
    this.correctionRatioLocation = correctionRatioLocation;

    this.bind();
  }

  bind() {
    const gl = this.gl;

    // Bindings
    gl.enableVertexAttribArray(this.positionLocation);
    gl.enableVertexAttribArray(this.normalLocation);
    gl.enableVertexAttribArray(this.radiusLocation);
    gl.enableVertexAttribArray(this.colorLocation);
    gl.enableVertexAttribArray(this.barycentricLocation);

    gl.vertexAttribPointer(this.positionLocation, 2, gl.FLOAT, false, ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 0);
    gl.vertexAttribPointer(this.normalLocation, 2, gl.FLOAT, false, ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 8);
    gl.vertexAttribPointer(this.radiusLocation, 1, gl.FLOAT, false, ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 16);
    gl.vertexAttribPointer(
      this.colorLocation,
      4,
      gl.UNSIGNED_BYTE,
      true,
      ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT,
      20,
    );

    // TODO: maybe we can optimize here by packing this in a bit mask
    gl.vertexAttribPointer(
      this.barycentricLocation,
      3,
      gl.FLOAT,
      false,
      ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT,
      24,
    );
  }

  computeIndices() {
    // nothing to do
  }

  process(
    sourceData,
    targetData,
    data,
    hidden,
    offset,
  ) {
    if (hidden) {
      for (let i = offset * STRIDE, l = i + STRIDE; i < l; i++) this.array[i] = 0;

      return;
    }

    const thickness = data.size || 1,
      radius = targetData.size || 1,
      x1 = sourceData.x,
      y1 = sourceData.y,
      x2 = targetData.x,
      y2 = targetData.y,
      color = floatColor(data.color);

    // Computing normals
    const dx = x2 - x1,
      dy = y2 - y1;

    let len = dx * dx + dy * dy,
      n1 = 0,
      n2 = 0;

    if (len) {
      len = 1 / Math.sqrt(len);

      n1 = -dy * len * thickness;
      n2 = dx * len * thickness;
    }

    let i = POINTS * ATTRIBUTES * offset;

    const array = this.array;

    // First point
    array[i++] = x2;
    array[i++] = y2;
    array[i++] = -n1;
    array[i++] = -n2;
    array[i++] = radius;
    array[i++] = color;
    array[i++] = 1;
    array[i++] = 0;
    array[i++] = 0;

    // Second point
    array[i++] = x2;
    array[i++] = y2;
    array[i++] = -n1;
    array[i++] = -n2;
    array[i++] = radius;
    array[i++] = color;
    array[i++] = 0;
    array[i++] = 1;
    array[i++] = 0;

    // Third point
    array[i++] = x2;
    array[i++] = y2;
    array[i++] = -n1;
    array[i++] = -n2;
    array[i++] = radius;
    array[i++] = color;
    array[i++] = 0;
    array[i++] = 0;
    array[i] = 1;
  }

  render(params) {
    if (this.hasNothingToRender()) return;

    const gl = this.gl;

    const program = this.program;
    gl.useProgram(program);

    // Binding uniforms
    gl.uniformMatrix3fv(this.matrixLocation, false, params.matrix);
    gl.uniform1f(this.sqrtZoomRatioLocation, Math.sqrt(params.ratio));
    gl.uniform1f(this.correctionRatioLocation, params.correctionRatio);

    // Drawing:
    gl.drawArrays(gl.TRIANGLES, 0, this.array.length / ATTRIBUTES);
  }
}

Now, to increase arrow size, modify the line const thickness = data.size || 1, in edge.arrowHead.js, e.g.:

// [...]
const thickness = data.size * 2.5 || 1,
// [...]

Upvotes: 2

torusJKL
torusJKL

Reputation: 352

I was able to change the arrow size by setting the render type to canvas and defining the minArrowSize in the settings of the graph.

let s = new sigma({
            renderer: {
                container: document.getElementById('graph-container'),
                type: 'canvas'
            },
            settings: {
                edgeLabelSize: 'proportional',
                minArrowSize: 15
            }
        });

Upvotes: 0

Related Questions