Push nodes to desired layers

Yes, it’s hard to make a circle with a single node. I had missed that case originally, but added that fix back on March 3rd.

You could have each layer, if it only has a single node, position that node at different angles.

here I am struggling how to position the node in layer if it only has a single node, please help

Well, you have seen the code, which both of us have posted before. You could just move the node to a different point that is rad distance from the origin.

Ok, Thanks , I tried it did not work.

Precisely why did it not work?

you are genious in this product, you are the better person to tell me why.
As workaround i am adding dummy nodes for layers which are having single nodes. And we are using tooltip for nodes.

So atleast help me in hiding the tooltip for dummy nodes. I am keeping key:unknown for dummy nodes

so I want to hide tooltip for nodes having key:unknown.

That is a completely different approach than I expected. I was just suggesting that you change that one line of code:

          nodes.first().moveTo(0, -rad, true);

to:

          nodes.first().move(new go.Point(rad, 0).rotate(-layer*20+10), true);

I haven’t actually tried this code, but I’m hoping it works better for your cases.

No walter it is not working, it is not adding node at any position.

Really? I just tried it with 6 layers, one node per layer:

it is working but it is not centering the chart in the frame, it is cutting sides have not nodes. in prevoius post you have provided

this.diagram.zoomToRect(biggest.getDocumentBounds());
but still not working as expected, simply say in the directions top, right, bottom, left if it is not having node that direction is cutting

see image.

Capture

Which Diagram properties have you set (or listeners have you added) whose names have “Initial…” in them? And if you have, to which values?

here are these

function init() {
var $ = go.GraphObject.make; // for conciseness in defining templates in this function

		  myDiagram =
			$(go.Diagram, "myDiagramDiv",  // must be the ID or reference to div
			  {
				layout: $(ConcentricLayout),
				//initialAutoScale: go.Diagram.Uniform,
				"animationManager.isEnabled": false,
				
			  });
			// shows when hovering over a node

this.diagram.zoomToRect(biggest.getDocumentBounds());

Ah, the problem is that at the time of the layout it cannot zoom to any particular rectangle because it’s happening too early – the viewport hasn’t been set yet. Sorry for going down the garden path.

So, we should go back to the normal way of doing such things – set Diagram.initialAutoScale to go.Diagram.Uniform, and have the visible size of the background Part holding those concentric circles in the “Background” Layer, where it will take up its space in the Diagram.documentBounds.

<!DOCTYPE html>
<html>
<head>
    <title>Concentric Layout</title>
  <meta name="description" content="Arrange nodes into concentric circles using multiple CircularLayouts." />
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
  <script src="go.js"></script>
  <script id="code">
    function ConcentricLayout() {
      go.Layout.call(this);
      // create a background grid Part consisting of concentric Circle Shapes
      this.colors = [];
      for (var i = 0; i < 10; i++) this.colors.push(go.Brush.mix("lightgreen", "blue", i/10));
      this.radius = 100;
      var back = new go.Part(go.Panel.Spot);
      back.layerName = "Background";
      back.pickable = false;
      back.selectable = false;
      back.locationSpot = go.Spot.Center;
      back.location = new go.Point(0, 0);
      var main = new go.Shape();
      main.desiredSize = new go.Size(0, 0);
      main.opacity = 0.0;
      back.add(main);
      for (var i = this.colors.length-1; i >= 0; i--) {
        var shp = new go.Shape();
        shp.figure = "Circle";
        shp.fill = this.colors[i];
        shp.strokeWidth = 0;
        shp.desiredSize = new go.Size(i*this.radius*2+this.radius, i*this.radius*2+this.radius);
        back.add(shp);
      }
      this.backgroundShapes = back;
    }
    go.Diagram.inherit(ConcentricLayout, go.Layout);

    ConcentricLayout.prototype.doLayout = function(coll) {
      var $ = go.GraphObject.make;  // for conciseness in defining templates
      this.diagram.startTransaction("Multi Circle Layout");
      var coll = this.collectParts(coll);

      // assume all circles will be centered at the origin
      this.nodesByLayer(coll, 0).each(function(n) { n.location = new go.Point(0, 0); });

      var rad = this.radius;
      var layer = 1;
      var nodes = null;
      while (nodes = this.nodesByLayer(coll, layer), nodes.count > 0) {
        if (nodes.count === 1) {
          var p = new go.Point(rad, 0).rotate(-layer*20-10);
          var n = nodes.first();
          n.move(p, true);
        } else {
          var layout =
            $(go.CircularLayout,
              {
                radius: rad,
                arrangement: go.CircularLayout.ConstantAngle,
                spacing: NaN
              });
          layout.doLayout(nodes);
          // recenter at (0, 0)
          var cntr = layout.actualCenter;
          this.diagram.moveParts(nodes, new go.Point(-cntr.x, -cntr.y));
        }
        // next layout uses a larger radius
        rad += this.radius;
        layer++;
      }

      var list = new go.List(this.backgroundShapes.elements);
      for (var i = list.count-1; i >= 1 ; i--) {
        var shp = list.elt(i);
        shp.visible = (i >= (list.count-layer));
      }
      if (this.backgroundShapes.diagram === null) {
        this.diagram.add(this.backgroundShapes);
      }
      this.diagram.commitTransaction("Multi Circle Layout");
    }

    ConcentricLayout.prototype.nodesByLayer = function(coll, layer) {
      var set = new go.Set(/*go.Node*/);
      coll.each(function(part) {
        if (part instanceof go.Node && part.data.layer === layer) set.add(part);
      });
      return set;
    }


    function init() {
      var $ = go.GraphObject.make;  // for conciseness in defining templates in this function

      myDiagram =
        $(go.Diagram, "myDiagramDiv",  // must be the ID or reference to div
          {
            layout: $(ConcentricLayout),
            initialAutoScale: go.Diagram.Uniform,
            "animationManager.isEnabled": false
          });

      myDiagram.nodeTemplate =
        $(go.Node, "Auto",
          { locationSpot: go.Spot.Center },
          { // limit node dragging to stay within their circular layer
            dragComputation: function(node, newloc, gridloc) {
              // assume nodes are laid out relative to the origin, (0, 0)
              // first find the node's expected distance from the origin:
              var dist = node.data.layer * node.diagram.layout.radius;
              // now find the angle of the desired NEWLOC, relative to the origin:
              var ang = go.Point.direction(0, 0, newloc.x, newloc.y);
              // then project the point to the correct distance from the origin
              return new go.Point(dist, 0).rotate(ang);
            }
          },
          $(go.Shape, "Circle",
            { fill: "gray", stroke: "#D8D8D8" },
            new go.Binding("fill", "color")),
          $(go.TextBlock,
            { margin: 5 },
            new go.Binding("text", "key"))
        );

      // create the model for the multiple circles
      var nodedata = [];
      // if you want a node in the center, set its layer: 0
      nodedata.push({ key: 0, layer: 0, color: "red" });
      for (var i = 0; i < 10; i++) nodedata.push({ key: nodedata.length, layer: 1, color: go.Brush.randomColor() });
      for (var i = 0; i < 20; i++) nodedata.push({ key: nodedata.length, layer: 2, color: go.Brush.randomColor() });
      for (var i = 0; i < 30; i++) nodedata.push({ key: nodedata.length, layer: 3, color: go.Brush.randomColor() });
      for (var i = 0; i < 40; i++) nodedata.push({ key: nodedata.length, layer: 4, color: go.Brush.randomColor() });

      var linkdata = [];
      for (var i = 0; i < nodedata.length-40; i++) {
        linkdata.push({ from: i, to: i + 20 + Math.floor(Math.random() * 20) });
      }
      myDiagram.model = new go.GraphLinksModel(nodedata, linkdata);
    }
  </script>
</head>
<body onload="init()">
<div id="sample">
  <div id="myDiagramDiv" style="background-color: white; border: solid 1px black; width: 100%; height: 700px"></div>
</div>
</body>
</html>

excellent, this is what i was expected, this looks perfect now. Thank you for your continuous support. much appreciated. you are genius.

A post was split to a new topic: Printing to PDF file

Hi Walter,

In this example Concentric Layout , how can we have circle radius as user input, so that i can change based on data complexity

Set the ConcentricLayout.radius property. Hmmm, we should change that name, since it is really the thickness of each ring, not their radius.

Please help me how to do this on button click and redraw chart

Actually in that sample we did rename radius to thickness.

Within a transaction, set ConcentricLayout.thickness and call Layout.invalidateLayout.

This topic is getting too long – assumptions have changed too much with the addition of that extra sample. Please start a new topic if you want any further discussions.