Guided dragging tool issue regarding position

If you have a node on x: 19.003

and use the guided dragging tool with multiple selection of 2 nodes or more that are on the same x currently.

like this:

and the original node I spoke about:


with coordinates:

If you then use the guided dragging tool to snap on the node with xx3
Recording2025-06-11131126-ezgif.com-video-to-gif-converter

(the coordinate values are converted to the values we use in our interface, hence the -180 z value, but this doesn’t matter because it comes directly from the diagram)

then only for the first node it uses the correct coordinates.

This because of this code:

// from the doDropOnto method.
const partItr = draggingParts.iterator;
        if (partItr.next()) {
            const part = partItr.key;
            // snaps only when the mouse is released without shift modifier
            const e = this.diagram.lastInput;
            const snap = this.isGuidelineSnapEnabled && !e.shift;
            this.showHorizontalMatches(part, false, snap); // false means don't show guidelines
            this.showVerticalMatches(part, false, snap);
        }

Now we changed it to:

  while (partsIterator.next())

so that every part is iterated and the correct X and Y is applied, but the issue is that the performance of this when you have a lot of nodes in selection and or in your diagram the performance is really bad and it takes a very big chunk of time to apply the change to the diagram.

So I’m wondering what I can do to fix this without losing any functionality.

I guess I don’t understand what it is that you are trying to accomplish.

Did you want all of the dragged non-Link Parts to be aligned just like the first dragged Part? If so, you don’t need to show the guidelines for each dragged Part, do you? So you should just make the change in the doDropOnto method, not in the doDragOver method.

Or did you want all of the dragged non-Link Parts to be aligned individually to whatever they should be aligned with as if they were dragged separately? This seems less likely to me, judging from your description, and it definitely would be a lot slower to implement. But why else do you want to call show...Matches for each dragged Part?

Anyway, assuming the former case is what you want, how do you then want to align all of the dragged Parts besides the first one? If the drop will shift the alignment of the first dragged Part horizontally, should all of the rest of the dragged Parts be shifted by the same amount? Or should they all be set to the same X value? Or some other plausible alternative behavior?

Hi,

Yeah the first guess you made is actually what I want:

I want all the non-link parts to be aligned the same way as the non-link part I was dragging.

so:

and (also for y, if it’s a vertical match):

I originally thought that this would be how to the guided dragging tool would treat multiple dragged items.

But so yes, what would be a valid way to implement this in the doDropOnto? Without causing performance issues?

Sorry I missed this. What should happen when the primary node is shifted in both X and Y directions? If you think they should all be coincident, at the same position, it means that the result will depend on very minor differences of position during dragging.

I think it should be straightforward to modify the GuidedDraggingTool to do what you want. If I get a chance I’ll demonstrate it.

Actually, the code that you want to modify is where Part.move is called, three times each (according to which spot best aligns with the best object) in showHorizontalMatches and in showVerticalMatches.

Instead of just moving that one part, you’ll want to move all of the draggingParts by the same offset.

1 Like

Hi, thanks for replying.

I’m still unsure how I’d implement it at the part.move location.

Nvm, I got it done after rereading your idea five times, ha. ha.

Thanks, man

  public showHorizontalMatches(part: go.Part, guideline: boolean, snap: boolean, draggingParts?: go.Part[]): void {
    if (!draggingParts) {
      draggingParts = [part];
    }

    for(const part of draggingParts) {
      const offsetX = objBounds.x - part.actualBounds.x;
      const offsetY = objBounds.y - part.actualBounds.y;
      const bestBounds = bestObj.locationObject.getDocumentBounds();
      // line extends from x0 to x2
      const x0 = Math.min(objBounds.x, bestBounds.x) - 10;
      const x2 = Math.max(objBounds.x + objBounds.width, bestBounds.x + bestBounds.width) + 10;
      // find bestObj's desired Y
      const bestPoint = new go.Point().setRectSpot(bestBounds, bestOtherSpot);
      if (bestSpot === go.Spot.Center) {
        if (snap) {
          // call Part.move in order to automatically move member Parts of Groups
          part.move(new go.Point(objBounds.x - offsetX, bestPoint.y - objBounds.height / 2 - offsetY));
          this.invalidateLinks(part);
        }
        if (guideline) {
          this.guidelineHcenter.position = new go.Point(x0, bestPoint.y);
          this.guidelineHcenter.elt(0).width = x2 - x0;
          this.diagram.add(this.guidelineHcenter);
        }
      }
    }