mxGraph 타일 정렬 알고리즘 구현

Dzeko·2023년 8월 8일
0

개발일지

목록 보기
96/112
post-thumbnail

기존 상태가 어땠느냐?

에디터에서 이렇게 타일들과 라인들이 겹쳐있을 때,
라인이나 타일을 클릭하면?


이렇게 서로 연결되어 있음에도, 클릭한 해당 타일이나 라인만 마치 끊긴 것 처럼 위로 올라왔다.
굉장히 부자연스러웠기에 PM팀에 말하고 이번 버전에 넣고 싶어 개발 일정을 쪼개 개발을 단행하였다.

UX 방향으로는
1. 선택한 셀에 직접적으로 연결된 애들만 같이 올린다.
2. 선택한 셀에 연결된 모든 플로우의 타일, 라인을 올린다.

PM팀의 검토로 확정이 되어야겠지만 개발하기에는 두 경우의 수가 큰 차이가 없어 보여 두 방식을 모두 개발하였다.

알고리즘 구현

cell : 타일이나 라인 (mxCell이라는 객체로 이루어진다.)
아래 함수는 선택된 cell이 바뀌었을 때 eventListener로 발동되는 함수다. 이 안에서 실행시키도록 하였다.
getSelectionModel().addListener(mxEvent.CHANGE, function(model, event) {
  .
  .
  .
}

먼저
클릭된 cell과 관련된 cell들을 stack에 추가하기 위해 cellStack이라는 배열과,
cell의 중복을 체크하기 위해 cell의 id를 담을 set자료구조 형태의 idSet을 선언했다.

const cellStack = [];
const orderIdSet = new Set();

cellStack에 들어간 cell에 연결된 cell들을 연쇄적으로 체크하기 위해 재귀를 생각했다.

getOrderStack(cells) 이라는 함수를 만들고,
변수는 배열로 받기 때문에 forEach를 돌려서 라인인지, 타일인지를 구분했다.

const getOrderStack = (cells) => {
  cells.forEach(cell => {
    if (cell.isEdge()) {
      
    } else if (cell.tileName) {
      
    }
  });
}
  1. 라인일 경우
    라인에 연결된 각각의 source, target 타일들이 있다. 그 타일들도 stack에 넣는 작업을 한다.
if (cell.isEdge()) {
  const sourceTile = cell.source;
  const targetTile = cell.target;
  
  const cellsToCheck = [cell, sourceTile, targetTile];
  cellsToCheck.forEach((item) => {
    addStackNotIncluded(item);
  });
}
  1. 타일일 경우
    타일이 가진 라인들도 stack에 넣는 작업을 한다. 그리고 각각의 라인에 연결된 타일들도 stack에 넣어야하기 때문에 재귀시킨다.
if (cell.tileName) {
  addStackNotIncluded(cell);
  const edges = g.getAllEdges([cell]);
  edges.forEach(edge => {
    addStackNotIncluded(edge);
  });
  getOrderStack(edges);
}

이 때, 순서가 중요하다. 클릭/선택한 cell이 가장 위로 올라와야 하기 때문에, stack의 가장 앞인 0번째 자리에 넣어야 한다.

또한 재귀로 돌릴 것이기 때문에, cell이 이미 stack에 있는지 확인하는 작업을 해야 한다. addStackNotIncluded() 라는 함수를 만들어 사용했다.

const addStackNotIncluded = (cell) => {
  const checkId = cell.id;
  // 중복되지 않은 id의 cell이면 cellStack에 추가.
  if (!orderIdSet.has(checkId)) {
    orderIdSet.add(checkId);
    cellStack.push(cell);
  }
}

모든 작업이 마무리되면, mxGraph 내장 함수인 orderCells로 cell을 정렬시킨다.
이 함수는 배열의 마지막부터 차례로 위로 정렬시키기 때문에,
목적이었던 선택한 cell이 제일 위로 오게 하기 위해서는 cellStack을 reverse() 시켜줘야 한다.
(선택한 cell만 따로 빼서 나중에 orderCell시켜도 된다.)

이 작업을 할 때 cellStack을 하나씩 pop 시켜 정렬을 할 지, reverse 후 정렬을 할 지 고민을 했는데,
두 케이스 모두 시간 복잡도는 O(n)으로 같으나, 공간 복잡도는 하나씩 pop하는 방법이 O(1), reverse 후 한꺼번에 실행시키는 방법이 O(n) 이었다. 하지만 효율을 생각하더라도 큰 차이가 없으며, 두 번째 방법이 간결하고 가독성이 높다고 판단되었다.

orderCells(false, cellStack.reverse());
profile
Hound on the Code

0개의 댓글