layoutNetwork() {
    if (
      this.options.hierarchical.enabled !== true &&
      this.options.improvedLayout === true
    ) {
      const indices = this.body.nodeIndices;

      // first check if we should Kamada Kawai to layout. The threshold is if less than half of the visible
      // nodes have predefined positions we use this.
      let positionDefined = 0;
      for (let i = 0; i < indices.length; i++) {
        const node = this.body.nodes[indices[i]];
        if (node.predefinedPosition === true) {
          positionDefined += 1;
        }
      }

      // if less than half of the nodes have a predefined position we continue
      if (positionDefined < 0.5 * indices.length) {
        const MAX_LEVELS = 10;
        let level = 0;
        const clusterThreshold = this.options.clusterThreshold;

        //
        // Define the options for the hidden cluster nodes
        // These options don't propagate outside the clustering phase.
        //
        // Some options are explicitly disabled, because they may be set in group or default node options.
        // The clusters are never displayed, so most explicit settings here serve as performance optimizations.
        //
        // The explicit setting of 'shape' is to avoid `shape: 'image'`; images are not passed to the hidden
        // cluster nodes, leading to an exception on creation.
        //
        // All settings here are performance related, except when noted otherwise.
        //
        const clusterOptions = {
          clusterNodeProperties: {
            shape: "ellipse", // Bugfix: avoid type 'image', no images supplied
            label: "", // avoid label handling
            group: "", // avoid group handling
            font: { multi: false }, // avoid font propagation
          },
          clusterEdgeProperties: {
            label: "", // avoid label handling
            font: { multi: false }, // avoid font propagation
            smooth: {
              enabled: false, // avoid drawing penalty for complex edges
            },
          },
        };

        // if there are a lot of nodes, we cluster before we run the algorithm.
        // NOTE: this part fails to find clusters for large scale-free networks, which should
        //       be easily clusterable.
        // TODO: examine why this is so
        if (indices.length > clusterThreshold) {
          const startLength = indices.length;
          while (indices.length > clusterThreshold && level <= MAX_LEVELS) {
            //console.time("clustering")
            level += 1;
            const before = indices.length;
            // if there are many nodes we do a hubsize cluster
            if (level % 3 === 0) {
              this.body.modules.clustering.clusterBridges(clusterOptions);
            } else {
              this.body.modules.clustering.clusterOutliers(clusterOptions);
            }
            const after = indices.length;
            if (before == after && level % 3 !== 0) {
              this._declusterAll();
              this.body.emitter.emit("_layoutFailed");
              console.info(
                "This network could not be positioned by this version of the improved layout algorithm." +
                  " Please disable improvedLayout for better performance."
              );
              return;
            }
            //console.timeEnd("clustering")
            //console.log(before,level,after);
          }
          // increase the size of the edges
          this.body.modules.kamadaKawai.setOptions({
            springLength: Math.max(150, 2 * startLength),
          });
        }
        if (level > MAX_LEVELS) {
          console.info(
            "The clustering didn't succeed within the amount of interations allowed," +
              " progressing with partial result."
          );
        }

        // position the system for these nodes and edges
        this.body.modules.kamadaKawai.solve(
          indices,
          this.body.edgeIndices,
          true
        );

        // shift to center point
        this._shiftToCenter();

        // perturb the nodes a little bit to force the physics to kick in
        const offset = 70;
        for (let i = 0; i < indices.length; i++) {
          // Only perturb the nodes that aren't fixed
          const node = this.body.nodes[indices[i]];
          if (node.predefinedPosition === false) {
            node.x += (0.5 - this._rng()) * offset;
            node.y += (0.5 - this._rng()) * offset;
          }
        }

        // uncluster all clusters
        this._declusterAll();

        // reposition all bezier nodes.
        this.body.emitter.emit("_repositionBezierNodes");
      }
    }
  }
Use Kamada Kawai to position nodes. This is quite a heavy algorithm so if there are a lot of nodes we
cluster them first to reduce the amount.