constructor(body, images, groups) {
    this.body = body;
    this.images = images;
    this.groups = groups;

    // create the edge API in the body container
    this.body.functions.createEdge = this.create.bind(this);

    this.edgesListeners = {
      add: (event, params) => {
        this.add(params.items);
      },
      update: (event, params) => {
        this.update(params.items);
      },
      remove: (event, params) => {
        this.remove(params.items);
      },
    };

    this.options = {};
    this.defaultOptions = {
      arrows: {
        to: { enabled: false, scaleFactor: 1, type: "arrow" }, // boolean / {arrowScaleFactor:1} / {enabled: false, arrowScaleFactor:1}
        middle: { enabled: false, scaleFactor: 1, type: "arrow" },
        from: { enabled: false, scaleFactor: 1, type: "arrow" },
      },
      endPointOffset: {
        from: 0,
        to: 0,
      },
      arrowStrikethrough: true,
      color: {
        color: "#848484",
        highlight: "#848484",
        hover: "#848484",
        inherit: "from",
        opacity: 1.0,
      },
      dashes: false,
      font: {
        color: "#343434",
        size: 14, // px
        face: "arial",
        background: "none",
        strokeWidth: 2, // px
        strokeColor: "#ffffff",
        align: "horizontal",
        multi: false,
        vadjust: 0,
        bold: {
          mod: "bold",
        },
        boldital: {
          mod: "bold italic",
        },
        ital: {
          mod: "italic",
        },
        mono: {
          mod: "",
          size: 15, // px
          face: "courier new",
          vadjust: 2,
        },
      },
      hidden: false,
      hoverWidth: 1.5,
      label: undefined,
      labelHighlightBold: true,
      length: undefined,
      physics: true,
      scaling: {
        min: 1,
        max: 15,
        label: {
          enabled: true,
          min: 14,
          max: 30,
          maxVisible: 30,
          drawThreshold: 5,
        },
        customScalingFunction: function (min, max, total, value) {
          if (max === min) {
            return 0.5;
          } else {
            const scale = 1 / (max - min);
            return Math.max(0, (value - min) * scale);
          }
        },
      },
      selectionWidth: 1.5,
      selfReference: {
        size: 20,
        angle: Math.PI / 4,
        renderBehindTheNode: true,
      },
      shadow: {
        enabled: false,
        color: "rgba(0,0,0,0.5)",
        size: 10,
        x: 5,
        y: 5,
      },
      background: {
        enabled: false,
        color: "rgba(111,111,111,1)",
        size: 10,
        dashes: false,
      },
      smooth: {
        enabled: true,
        type: "dynamic",
        forceDirection: "none",
        roundness: 0.5,
      },
      title: undefined,
      width: 1,
      value: undefined,
    };

    deepExtend(this.options, this.defaultOptions);

    this.bindEventListeners();
  }
@param {object} body
@param {Array.<Image>} images
@param {Array.<Group>} groups