Add a click event to the TextBlock of Instrument Gauge

Hi,
I’m trying to add another feature to this example of Instrument Gauge.
I want to change the value of the Graduated Panel when the user click on the number.
However, I’m not able to fire the click event of the TextBlock.
This is what I’m trying.
(I can see it is working if I add it to the Node title which is a TextBlock also)

$(go.TextBlock,
          { // each tick label
            interval: 4,
            alignmentFocus: go.Spot.Center,
            font: "bold italic 20pt sans-serif",
            stroke: "white",
            segmentOffset: new go.Point(0, 30),
            click: () => alert("You will never see this message.");
          })

Any idea how to achieve this ?

As discussed at GoJS Graduated Panels -- Northwoods Software, “Events on the tick Shapes and TextBlock labels will be ignored.” Part of the problem is that there may appear to be many (or no) renderings of tick marks or tick labels, so it isn’t clear what the click would be hitting.

You could put a click event handler on the main Shape. But it will likely be too thin.

Or you could put it on a containing panel, such as the outer circle Shape.

Okay, I see. The main Shape is too thin like you said.
Is it possible to add a click event on the containing circle and return the nearest textBlock value ? Then I can add like a condition.
Using findPartAt and documentPoint returns the whole Gauge data but not a specific textBlock.

The point is that for performance reasons there is no TextBlock where the tick label is drawn. Or rather, the same TextBlock is used to draw all of the labels at different places along the path of the scale.

You can find the closest scale value for a point in document coordinates:

var scale = ...;  // the "Graduated" Panel
var local = scale.getLocalPoint(docPt);
var val = scale.graduatedValueForPoint(local);

This is demonstrated at GoJS Graduated Panels -- Northwoods Software

Yep, this works as expected.
I will add another event to the main shape also.
I have two more questions about the shape.
1- What is the best way to change the Needle to a Circle (beneath the selected value) ?
I tried this code, but I don’t know if this is the best practice to achieve it.

$(
      // Display a needle
      // go.Shape, { fill: "red", strokeWidth: 0, geometryString: "F30 M-10 0 L0 -6 100 0 0 6z x M-100 0" }, 
      // Display a circle
      go.Shape, "Circle", { alignment: new go.Spot(0.5, 0.8), fill: "grey", strokeWidth: 0, width: 15, height: 15 },
          new go.Binding("alignment", "value", convertValueToAlignment),
          sliderActions(true)
        )

function convertValueToAlignment(value, shape) {
  // Here I need to do some Math to find the Spot
}

2- I’m trying to display a full circle as the main shape.
But I see that the Max value hide the Min value. Which is normal.
In my case I want to keep the mains shape as a circle actually and display the tick labels on another circle with 270°. Is this possible ?

For #1, I replace the needle Shape and center dot Shape, and modify the TextBlock label:

            $(go.TextBlock,
              { alignment: new go.Spot(0.5, 0.5), stroke: "orange", font: "bold italic 14pt sans-serif" },
              new go.Binding("text", "key"),
              new go.Binding("stroke", "color")),
            $(go.Shape, "Circle", { fill: "red", strokeWidth: 0, width: 16, height: 16 },
              new go.Binding("alignment", "value", convertValueToSpot)),

Then change the conversion function to return a Spot:

      function convertValueToSpot(v, shape) {
        var scale = shape.part.findObject("SCALE");
        var p = scale.graduatedPointForValue(v);
        return new go.Spot(0, 0, p.x, p.y);
      }

For #2, set graduatedStart and/or graduatedEnd on the TextBlock label or Shape tick mark. For example:

            $(go.TextBlock,
              { // each tick label
                graduatedStart: 0.01, graduatedEnd: 0.99,
                . . .

will hide the first and last labels.

For point #2, actually I don’t want to hide them but I want to keep a circular main shape and show both Min and Max values
Similar to this picture with 0 and 40 are the min and max.

Just add a “Circle” Shape of the desired width and height and strokeWidth and stroke, and no fill, to the “Spot” Panel holding the “Graduated” Panel. Something like:

$(go.Shape, "Circle", { fill: null, stroke: "lime", strokeWidth: 4, width: 200, height: 200 }),

Minor optimization: you can then set the opacity of the original “Graduated” Panel path Shape to 0.0, which will avoid actually drawing that arc that is just going to be overdrawn by your new circle.

Yeah it’s working as expected. Awesome.

#1 I’m trying to find a way to calculate the neaset point of the green circle. I want to make the red indicator on the green circle instead of the main one. I tried a bunch of methods but I couldn’t find a method to calculate the nearest point of a GraphObject.

Something like this :

function convertValueToSpot(v, shape) {
        var scale = shape.part.findObject("SCALE");
        var innerCircle = shape.part.findObject("INNER_CIRCLE");
        var p = scale.graduatedPointForValue(v);
=>    var innerCirclePoint = innerCircle.nearestPointTo(p);
        return new go.Spot(0, 0, innerCirclePoint .x, innerCirclePoint .y);
      }

#2 Is it possible to switch the direction of the ticks indicator to the other way ? I bring them closer to theirs values.
This is what I have done to change the position of the values :

$(go.TextBlock,
            { // each tick label
              interval: 2,
              alignmentFocus: go.Spot.Center,
              font: "bold italic 10pt sans-serif",
              stroke: "white",
              segmentOffset: new go.Point(0, -20) // HERE
            })

#1 And you can’t use the Graduated Panel because it doesn’t extend the whole way, right? But it’s a circle – just do the math and figure out whether you want numbers less than the minimum or greater than the maximum.

#2 This is covered by GoJS Graduated Panels -- Northwoods Software You probably want to set alignmentFocus.

#1 Actually I want just display the indicator (little red circle) on the inner circle (the green one). But I couldn’t find a method to calculate the position. Any hint to achieve this ? I tried getLocalPoint and some other methods …

#2 Awesome. That’s exactly what I want.

If you have called Panel.graduatedPointForValue, you have a Point on the graduated scale, in local coordinates.
Subtract the center point, scale the point by the fraction that is the green circle’s radius compared to the graduated panel’s circle’s radius, add the center point back, and that’s what you use for the Spot offset.

I’m trying to figure out a way to scale the indicator by specific steps.
The solution that I found is to add a function which take the scale.graduatedValueForPoint(loc) as an argument and return the nearest point or number allowed.
Do you think this is a good solution for that ?

actionMove: function(e, obj) {
                    if (!obj._dragging) return;
                    const scale = obj.part.findObject("knobGraduationScale");
                    const pt = e.diagram.lastInput.documentPoint;
                    const loc = scale.getLocalPoint(pt);
                    let val = Math.round(scale.graduatedValueForPoint(loc));
                    // Find the nearest allowed value
                    val = that.findClosestValue(that.allowedValues, val); 
                    if (that.allowedValues.includes(val)) {
                        e.diagram.model.commit(function(m) {
                            m.set(obj.part.data, "value", val);
                        }, null);  // null means skipsUndoManager
                    }
                }

I thought you wanted to have a point on the circle, not on the graduated scale which is only a partial arc of the circle.

It’s a different subject.

I didn’t change the position of indicator (the red circle) because I didn’t found how to substract the “circle’s radius compared to the graduated panel’s circle’s radius”.

Lately I’m trying to change the indicator steps according to a defined value.

Well, I tried to achieve this since few days but I couldn’t figure it out.
Is there any example for a similar behavior in the demos so I can follow it ?

I haven’t tried this, since I still don’t know what you really want to do. But from what I wrote earlier, this is what it sounds like you need to do. Assume the radius of the “Graduated” scale is 50 and the radius of the green circle is 40:

var p = scale.graduatedPointForValue(v);
p.offset(-50, -50).scale(40/50, 40/50).offset(50, 50);
... new go.Spot(0, 0, p.x, p.y)

That’s exactly what I want ! Thanks man.