Positioning labels on bezier-links with from/toEndSegmentLength

Hello,

I’m trying to position labels at the start/end of the link.
Following the Link Labels | GoJS Intro we got it working for almost all scenarios except curved links.

If we use segmentIndex: -2 we get:

If we use segmentIndex: -1 we get:

We use toSpot and fromSpot and have set the {from|to}EndSegmentLength to get more curvy bezier curves.

I’m not sure how we can achieve a label at the end of link which isn’t longer than the link (segmentIndex: -1) or somewhere off in the distance (segmentIndex: -2).

Before I try to build a (more complex) solution, maybe there is an obvious solution I haven’t considered.

There is a short html example below, where I tried to pinpoint the problem we’re having.

Cheers
Niklas

Example Code:

<!DOCTYPE html>
<html lang="en">

<body>
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/release/go.js"></script>
  <link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism.min.css" rel="stylesheet" />
  <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js"></script>

  <div id="allSampleContent" class="p-4 w-full">
    <script id="code">
      function init() {
        diagram = new go.Diagram('myDiagramDiv', {

        });

        diagram.nodeTemplate =
          new go.Node("Auto")
            .bind("location", "loc", go.Point.parse)
            .add(
              new go.Shape("RoundedRectangle", { fill: "lightgray", portId: "", toSpot: go.Spot.TopCenter, fromSpot: go.Spot.BottomCenter }),
              new go.TextBlock({ margin: 5 })
                .bind("text", "key")
            );

        diagram.linkTemplate =
          new go.Link({ curve: go.Link.Bezier, fromEndSegmentLength: 200, toEndSegmentLength: 200 })
            .add(
              new go.Shape(),                           // this is the link shape (the line)
              new go.Shape({ toArrow: "Standard" }),  // this is an arrowhead
              new go.TextBlock({
                segmentIndex: -2,
                segmentOrientation: go.Link.OrientUpright,
                alignmentFocus: go.Spot.BottomCenter,
                textAlign: "center",
              })                        // this is a Link label
                .bind("text")
            );

        const nodeDataArray = [
          { key: "Alpha", loc: "0 0" },
          { key: "Beta", loc: "200 50" }
        ];
        const linkDataArray = [
          { from: "Alpha", to: "Beta", text: "a really long long long label" }
        ];
        diagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);

      }

      window.addEventListener('DOMContentLoaded', init);
    </script>

    <div id="sample">
      <div id="myDiagramDiv" style="border: solid 1px black; width: 100%; height: 470px; background: whitesmoke"></div>
    </div>
  </div>
</body>

</html>

Thanks for providing the sample.

Did you mean something like:

              new go.TextBlock({
                segmentIndex: -1,
                segmentOrientation: go.Link.OrientUpright,
                alignmentFocus: go.Spot.BottomRight,
                textAlign: "center",
              })                        // this is a Link label

Thanks, that already helps.
Is there a built-in way to angle to label along the curve created between the last and previous to last segment?
Or would we have to compute the angle?

Sorry, yes, if you want an average angle opposite the two segments at the next-to-last point (i.e. point 2), that needs to be computed dynamically.

I haven’t tried this, but there is an undocumented method on the Link class that you could try overriding:

  /**
   * Dynamically compute the desired angle of a GraphObject along a segment of the route.
   * This method is only called when the GraphObject's {@link GraphObject.segmentOrientation} property value is
   * not {@link Orientation.None}.
   * @virtual
   * @param elt - The {@link GraphObject} being rotated.
   * @param orient - An {@link Orientation} that indicates how the angle should be computed.
   * @param angle - The angle of the segment of the route where the GraphObject is attached.
   * @returns The intended angle for the GraphObject, in degrees.
   */
  public computeAngle(elt: GraphObject, orient: Orientation, angle: number): number;

Hey, a Link.computeAngle override works, at least for this particular case.

class CustomLink extends go.Link {
  constructor(init) {
    super();
    if (init) Object.assign(this, init);
  }
  computeAngle(elt, orient, angle) {
    let a = super.computeAngle(elt, orient, angle);
    if (elt.segmentIndex === -1 && this.curve === go.Curve.Bezier && elt instanceof go.TextBlock) {
      const p1 = this.getPoint(1);
      const p2 = this.getPoint(2);
      const b = p2.directionPoint(p1);
      let r = ((a + b) / 2) - 90;
      if (r < 180) r += 360
      else if (r >= 360) r -= 360;
      return r;
    } else {
      return a;
    }
  }
}
        diagram.linkTemplate =
          new CustomLink({ curve: go.Link.Bezier, fromEndSegmentLength: 200, toEndSegmentLength: 200 })
            .add(
              new go.Shape(),                           // this is the link shape (the line)
              new go.Shape({ toArrow: "Standard" }),  // this is an arrowhead
              new go.TextBlock({
                segmentIndex: -1,
                segmentOrientation: go.Link.OrientUpright,
                segmentFraction: 0.5,
                textAlign: "center",
              })                        // this is a Link label
                .bind("text")
            );

Thanks for suggesting this.
Is computeAngle a new method with gojs v3?
Or is it just not part of the type definitions?

I don’t think it’s ever been in the d.ts file.