Find the center Point of a Shape relative to its Part


I’m trying to center a Shape within an Adornment over another Shape within the adornedObject with alignments and offsets. The Adornment Part is the exact same size as of the adornedObject Panel, which is good but the Shape within it is smaller and can be positioned anywhere in the adornedObject and is also nested in multiple Panels so the actualBounds doesn’t give me the values I need.

The Shape inside the Adornment is in a Center Spot and I need to find the correct offsets to center it over to other Shape in the adornedObject.

I’m looking for a way to find the relative Center spot of the Shape within the adornedPart or within the adornedObject.

I tried getDocumentPoint in combination with getLocalPoint which doesn’t work properly since the Nodes can rotate and the Center Point of both Objects is changing depending on the angle.

Is there something similar as getDocumentPoint that returns a Point but in Part/Panel coordinates ?

Of course I can hardcode the alignment spot for each Node template but I would need to guess and repeat until the Shape is perfectly aligned, and this for all our Node templates… Which is not really practical since I would need to update the offsets every time we make changes in the Node template.

Thanks in advance !

Here’s an example selection Adornment that aligns its Shape to center on an element of the Adornment.adornedObject whose is a data bound name. In this case the optional name is stored as the data.a property value. You will probably have different requirements.

<!DOCTYPE html>
  <title>Minimal GoJS Sample</title>
  <!-- Copyright 1998-2022 by Northwoods Software Corporation. -->
  <div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:600px"></div>
  <textarea id="mySavedModel" style="width:100%;height:250px"></textarea>

  <script src=""></script>
  <script src=""></script>
  <script id="code">
const $ = go.GraphObject.make;

const myDiagram =
  $(go.Diagram, "myDiagramDiv",
      layout: $(go.TreeLayout, { angle: 90 }),
      "undoManager.isEnabled": true,
      "ModelChanged": e => {     // just for demonstration purposes,
        if (e.isTransactionFinished) {  // show the model data in the page's TextArea
          document.getElementById("mySavedModel").textContent = e.model.toJson();

myDiagram.nodeTemplate =
  $(go.Node, "Auto",
      width: 150, height: 100,
      isShadowed: true
      { fill: "white", stroke: "lightgray" },
      new go.Binding("fill", "color", c => go.Brush.darkenBy(c, 0.05))),
    $(go.Shape, "Ellipse",
      { fill: "white", stroke: "lightgray", stretch: go.GraphObject.Fill },
      new go.Binding("figure"),
      new go.Binding("fill", "color")),
    $(go.Panel, "Table",
        function(node) { return `${}.html`; },
          { name: "LINK", stroke: "dodgerblue", isUnderline: true },
          new go.Binding("text", "name")),
        { row: 0, margin: 4 }),
      $(go.TextBlock, { row: 1, name: "TEXT" },
        new go.Binding("text", "country")),
      $(go.Picture, "",
        { row: 2, name: "ICON",
          width: 24, height: 24,
          click: (e, pic) =>`some url for ${}`)

myDiagram.nodeTemplate.selectionAdornmentTemplate =
  $(go.Adornment, "Spot",
      $(go.Shape, "Circle",
        { width: 20, height: 20, fill: null, stroke: "magenta" },
        new go.Binding("alignment", "a", (name, shape) => {
          const obj = shape.part.adornedObject;
          let focus = obj.findObject(name);
          if (!focus) focus = obj;
          const oc = obj.getDocumentPoint(go.Spot.Center);
          const fc = focus.getDocumentPoint(go.Spot.Center);
          return new go.Spot(0.5, 0.5, fc.x-oc.x, fc.y-oc.y);

myDiagram.model = new go.GraphLinksModel(
  { key: 1, name: "Diagram", color: "lightgoldenrodyellow", country: "United States", a: "ICON" },
  { key: 2, name: "Node", color: "khaki", country: "United Kingdom", figure: "Triangle", a: "LINK" }
  { from: 1, to: 2 }

Thanks Walter for your solution, getDocumentPoint wasn’t enought since the block/node can rotate and it changes the x and y distance between the two points but you pointed me the right way, I managed to make your solution work in combination with getLocalPoint from a relative parent Panel.

Thanks !