Можно ли в GoJS сделать группы непересекающимися?

У меня есть несколько групп с узлами, и я хотел бы, чтобы эти группы не пересекались при перемещении. Что мне для этого нужно сделать? Вот пример шаблона моей группы.

$(go.Group, "Auto",
  {
    layout: $(go.LayeredDigraphLayout, {
      direction: 0,
      columnSpacing: 10,
      initializeOption: go.LayeredDigraphLayout.InitDepthFirstOut,
      aggressiveOption: go.LayeredDigraphLayout.AggressiveMore
    }),
    minSize: new go.Size(800, 30),
    computesBoundsIncludingLocation: true,
    computesBoundsIncludingLinks: true,
    computesBoundsAfterDrag: true,
    isSubGraphExpanded: true
  },
  $(go.Shape, "Rectangle", [
    {
      fill: null,
      stroke: "gray",
      strokeWidth: 2
    },
    new go.Binding('fill', '', function (group) {
      return group.data.isEditable ? '#eee' : '#F7EAEC';
    }).ofObject('')
  ]),
  $(go.Panel, "Vertical",
    { defaultAlignment: go.Spot.Left },
    $(go.Panel, "Horizontal",
      { defaultAlignment: go.Spot.Top },
      $(go.TextBlock,
        { font: "Bold 18px Sans-Serif", textAlign: "left" },
        new go.Binding("text", "name"))
      ),
      $(go.Placeholder,
        { padding: new go.Margin(10, 10), margin: 0 })
      )
    );

Я нашел одно решение: forum.nwoods.com/t/disable-overlap-of-nodes/6058/2, но оно работает очень медленно для групп с большим количеством узлов внутри.

blackhard 22.05.2019 17:04
Формы c голосовым вводом в React с помощью Speechly
Формы c голосовым вводом в React с помощью Speechly
Пытались ли вы когда-нибудь заполнить веб-форму в области электронной коммерции, которая требует много кликов и выбора? Вас попросят заполнить дату,...
В чем разница между Promise и Observable?
В чем разница между Promise и Observable?
Разберитесь в этом вопросе, и вы значительно повысите уровень своей компетенции.
Сравнение структур данных: Массивы и объекты в Javascript
Сравнение структур данных: Массивы и объекты в Javascript
Итак, вы изучили основы JavaScript и хотите перейти к изучению структур данных. Мотивация для изучения/понимания Структур данных может быть разной,...
Создание собственной системы электронной коммерции на базе Keystone.js - настройка среды и базовые модели
Создание собственной системы электронной коммерции на базе Keystone.js - настройка среды и базовые модели
Прошлая статья была первой из цикла статей о создании системы электронной коммерции с использованием Keystone.js, и она была посвящена главным образом...
Приложение для отслеживания бюджета на React js для начинающих
Приложение для отслеживания бюджета на React js для начинающих
Обучение на практике - это проверенная тема для достижения успеха в любой области. Если вы знаете контекст фразы "Практика делает человека...
Стоит ли использовать React в 2022 году?
Стоит ли использовать React в 2022 году?
В 2022 году мы все слышим о трендах фронтенда (React, Vue), но мы не знаем, почему мы должны использовать эти фреймворки, когда их использовать, а...
0
1
554
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Необходимая оптимизация заключается в том, чтобы рассматривать группы как атомарные объекты. Нет необходимости проверять, перекрываются ли какие-либо узлы-члены группы с какими-либо узлами, если уже проверена вся группа.

Реализация этого заключается в добавлении двух строк к функции navig в этом примере, https://gojs.net/latest/samples/dragUnoccupied.html.

  function isUnoccupied(r, node) {
    var diagram = node.diagram;

    // nested function used by Layer.findObjectsIn, below
    // only consider Parts, and ignore the given Node and any Links
    function navig(obj) {
      var part = obj.part;
      if (part === node) return null;
      if (part instanceof go.Link) return null;

      // add these two checks:
      if (part.isMemberOf(node)) return null;
      if (node.isMemberOf(part)) return null;

      return part;
    }

    // only consider non-temporary Layers
    var lit = diagram.layers;
    while (lit.next()) {
      var lay = lit.value;
      if (lay.isTemporary) continue;
      if (lay.findObjectsIn(r, navig, null, true).count > 0) return false;
    }
    return true;
  }

  // a Part.dragComputation function that prevents a Part from being dragged to overlap another Part
  function avoidNodeOverlap(node, pt, gridpt) {
    if (node.diagram instanceof go.Palette) return gridpt;
    // this assumes each node is fully rectangular
    var bnds = node.actualBounds;
    var loc = node.location;
    // use PT instead of GRIDPT if you want to ignore any grid snapping behavior
    // see if the area at the proposed location is unoccupied
    var r = new go.Rect(gridpt.x - (loc.x - bnds.x), gridpt.y - (loc.y - bnds.y), bnds.width, bnds.height);
    // maybe inflate R if you want some space between the node and any other nodes
    r.inflate(-0.5, -0.5);  // by default, deflate to avoid edge overlaps with "exact" fits
    // when dragging a node from another Diagram, choose an unoccupied area
    if (!(node.diagram.currentTool instanceof go.DraggingTool) &&
        (!node._temp || !node.layer.isTemporary)) {  // in Temporary Layer during external drag-and-drop
      node._temp = true;  // flag to avoid repeated searches during external drag-and-drop
      while (!isUnoccupied(r, node)) {
        r.x += 10;  // note that this is an unimaginative search algorithm --
        r.y += 10;  // you can improve the search here to be more appropriate for your app
      }
      r.inflate(0.5, 0.5);  // restore to actual size
      // return the proposed new location point
      return new go.Point(r.x - (loc.x - bnds.x), r.y - (loc.y - bnds.y));
    }
    if (isUnoccupied(r, node)) return gridpt;  // OK
    return loc;  // give up -- don't allow the node to be moved to the new location
  }

  function init() {
    var $ = go.GraphObject.make;

    myDiagram =
      $(go.Diagram, "myDiagramDiv",
          {
            "undoManager.isEnabled": true,
            // support creating groups with Ctrl-G
            "commandHandler.archetypeGroupData": { isGroup: true, text: "NEW GROUP" }
          });

    myDiagram.nodeTemplate =
      $(go.Node, "Auto",
        { // avoid overlapping other nodes
          dragComputation: avoidNodeOverlap
        },
        $(go.Shape,
          { fill: "white", portId: "", fromLinkable: true, toLinkable: true, cursor: "pointer" },
          new go.Binding("fill", "color")),
        $(go.TextBlock,
          { margin: 8, editable: true },
          new go.Binding("text").makeTwoWay())
      );

    myDiagram.groupTemplate =
      $(go.Group, "Vertical",
        { // avoid overlapping other nodes
          dragComputation: avoidNodeOverlap,
          // support ungrouping by Ctrl-Shift-G
          ungroupable: true
        },
        $(go.TextBlock,
          { font: "bold 14pt sans-serif", editable: true },
          new go.Binding("text").makeTwoWay()),
        $(go.Panel, "Auto",
          $(go.Shape, { fill: "lightgray" }),
          $(go.Placeholder, { padding: 5 })
        )
      );

    myDiagram.model = new go.GraphLinksModel(
    [
      { key: 1, text: "Alpha", color: "lightblue" },
      { key: 2, text: "Beta", color: "orange" },
      { key: 3, text: "Gamma", color: "lightgreen" },
      { key: 4, text: "Delta", color: "pink" }
    ],
    [
      { from: 1, to: 2 },
      { from: 1, to: 3 },
      { from: 2, to: 2 },
      { from: 3, to: 4 },
      { from: 4, to: 1 }
    ]);
  }

Чтобы создать группу, выберите несколько узлов и нажмите Control-G.

Установите это в своем nodeTemplate:

 myDiagram.nodeTemplate =
        $(go.Node, "Auto",
          { dragComputation: avoidNodeOverlap });

Вы можете проверить решение здесь: https://gojs.net/latest/samples/dragUnoccupied.html

Другие вопросы по теме