Почему Cytoscape переходит в «бесконечный» цикл в этой конкретной ситуации?

#javascript #cytoscape.js

#javascript #cytoscape.js

Вопрос:

Если я клонирую конфигурацию макета с Object.assign помощью и пытаюсь использовать этот макет, cytoscape быстро вызывает ошибку нехватки памяти. Я могу обойти это, просто определив второй макет, в основном идентичный первому, и не клонируя, но мне интересно узнать причину проблемы или возможную ошибку в cytoscape.

В этом примере кода щелкните add and layout 2 сразу после загрузки страницы, и она зависнет / закончится память. (Имейте под рукой диспетчер задач, чтобы отключить вашу вкладку или браузер.) Различные комбинации добавления узлов и запуска клонированного макета в основном будут зависать, но не всегда.

 let cy
const layout1 = {
  name: 'euler',
  springLength: edge => 80,
  mass: node => 4,
  randomize: true,
  animate: false,
  gravity: -1.2,
  maxIterations: 1000,
  maxSimulationTime: 4000,
}

const layout2 = Object.assign({}, layout1, {
  fit: false,
  animate: true,
  randomize: false,
})

document.addEventListener('DOMContentLoaded', function() {
  cy = cytoscape({
    container: document.getElementById('cy'),
    layout: layout1,
    style: [
      {
        selector: 'node',
        style: {
          label: 'data(id)',
        },
      },
    ],
    elements: [
      { data: { id: 'a' } },
      { data: { id: 'b' } },
      { data: { id: 'a_b', source: 'a', target: 'b' } },
    ],
  })
})

function add() {
  cy.add([
    { data: { id: 'c' } },
    { data: { id: 'd' } },
    { data: { id: 'c_d', source: 'c', target: 'd' } },
  ])
  // cy.layout(layout2).run()
}

function doLayout1() {
  cy.layout(layout1).run()
}

function doLayout2() {
  cy.layout(layout2).run()
}

function addAndLayout2() {
  add()
  doLayout2()
} 
 <!DOCTYPE html>
    <html>
      <head>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.5.0/cytoscape.min.js"></script>
        <script src="https://cdn.jsdelivr.net/npm/cytoscape-euler@1.2.1/cytoscape-euler.min.js"></script>
      </head>
    
      <style>
        body {
          height: 100%;
        }
        #cy {
          height: 100%;
          flex-grow: 1;
        }
        .main {
          height: 100vh;
          display: flex;
          flex: 1;
        }
      </style>
    
      <body>
        <button onclick="add()">add nodes</button>
        <button onclick="doLayout1()">layout 1</button>
        <button onclick="doLayout2()">layout 2</button>
        <button onclick="addAndLayout2()">add and layout 2</button>
        <div class="main">
          <div id="cy"></div>
        </div>
      </body>
    </html> 

Ответ №1:

Это не имеет никакого отношения к Ojbect.assign (даже если вы неправильно скопировали объект, он не должен зависать).

Причина в randomize опции. Для этого конкретного графика, когда для параметра randomize установлено значение false , макет никогда не заканчивается. Просто удалите randomize: false из второго макета или после добавления новых узлов и перед запуском layout2 запустите random макет (или просто рандомизируйте узлы вручную) — layout2 завершится.

Проблема в том, что: макет должен завершиться в какой-то момент (в худшем случае, когда достигается максимальное количество итераций). Но этот конкретный макет никогда не завершается.

Интересно то, что этот простой график оказывается одним из худших случаев и для некоторых других алгоритмов компоновки (for randomized: false ). Я пытался cose-bilkent . Это также занимает немного больше времени и завершается при достижении максимального количества итераций (установка numIter параметра на меньшее число приведет к досрочному завершению, ухудшению качества) — но результат действительно плохой.

Комментарии:

1. Спасибо, что разъяснили, что я ввел ложную ассоциацию в Object.assign , это очень помогает! Интересно, randomized требуется ли это, или узлы точно перекрываются, поэтому это приводит к сбою физического моделирования? Я попробую проверить эту теорию, но мне было интересно, о чем вы думаете.

2. Что вы подразумеваете под » randomized требуется»? Если бы это было необходимо true , то это не было бы вариантом. Когда применяется рандомизированный false пошаговый макет — текущие позиции узлов используются в качестве начальной позиции в алгоритме компоновки. Это хорошо для сохранения ментальной карты пользователя. Когда это верно, изначально узлам назначаются случайные точки, а затем выполняется алгоритм компоновки. Это не обязательно перекрытие, это общее состояние позиций узлов (даже если вы просто добавляете узел c в add function , он никогда не завершается).

3. В любом случае макет должен завершиться. Либо когда он сходится, либо достигается максимальное количество итераций. Возможно, вы можете сообщить об этом как о проблеме.

4. Я имею в виду, что с randomize: false макетом происходит сбой, поэтому, похоже, это неявно требуется true . Я понимаю, что макет должен каким-то образом урегулировать / завершить, но в данном случае это явно не так.