Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 31 additions & 14 deletions lib/features/erd/erd_router.dart
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,15 @@ class _Grid {
final List<double> xs, ys;
final List<Rect> rects;
late final Uint8List _node;

// Search state, one entry per (cell, direction). Sized lazily on first search.
late final int _states = xs.length * ys.length * 4;
late final Float64List _cost = Float64List(_states);
late final Int32List _prev = Int32List(_states);
late final Int32List _startOf = Int32List(_states);
late final Int32List _stamp = Int32List(_states);
late final Int32List _closed = Int32List(_states);
int _generation = 0;
late final Uint8List _hSeg;
late final Uint8List _vSeg;

Expand Down Expand Up @@ -322,16 +331,24 @@ class _Grid {
return best;
}

final cost = <int, double>{};
final prev = <int, int>{};
final startOf = <int, int>{};
// Per-state data lives in flat arrays shared by every search of this grid.
// An entry is valid only when its stamp is the current search's generation,
// so nothing is cleared between searches.
final gen = ++_generation;
double costOf(int k) => _stamp[k] == gen ? _cost[k] : double.infinity;
void setCost(int k, double c) {
_stamp[k] = gen;
_cost[k] = c;
}

final heap = _Heap();
for (var s = 0; s < starts.length; s++) {
final i = _ix(starts[s].$1.dx), j = _iy(starts[s].$1.dy);
if (i < 0 || j < 0 || _blockedNode(i, j)) continue;
final k = key(i, j, starts[s].$2);
cost[k] = 0;
startOf[k] = s;
setCost(k, 0);
_prev[k] = -1;
_startOf[k] = s;
heap.push(k, heuristic(i, j));
}

Expand All @@ -340,14 +357,14 @@ class _Grid {
int? bestKey;
var bestGoal = -1;
var expanded = 0;
final closed = <int>{};
while (heap.isNotEmpty) {
if (heap.peekPriority >= bestCost) break;
final k = heap.pop();
if (!closed.add(k)) continue;
if (_closed[k] == gen) continue;
_closed[k] = gen;
if (++expanded > w * h * 4) break;
final d = k % 4, cell = k ~/ 4, i = cell ~/ h, j = cell % h;
final c = cost[k]!;
final c = _cost[k];
final goal = goalAt[cell];
if (goal != null) {
final want = goal.$2;
Expand All @@ -374,9 +391,9 @@ class _Grid {
if (_blockedSegment(i, j, nd)) continue;
final nc = c + (a - b).distance + (nd == d ? 0 : bendCost);
final nk = key(ni, nj, nd);
if (nc < (cost[nk] ?? double.infinity)) {
cost[nk] = nc;
prev[nk] = k;
if (nc < costOf(nk)) {
setCost(nk, nc);
_prev[nk] = k;
heap.push(nk, nc + heuristic(ni, nj));
}
}
Expand All @@ -388,11 +405,11 @@ class _Grid {
while (true) {
final cc = cur ~/ 4;
pts.add(Offset(xs[cc ~/ h], ys[cc % h]));
final p = prev[cur];
if (p == null) break;
final p = _prev[cur];
if (p < 0) break;
cur = p;
}
return (pts.reversed.toList(), startOf[cur]!, bestGoal);
return (pts.reversed.toList(), _startOf[cur], bestGoal);
}
}

Expand Down
25 changes: 25 additions & 0 deletions test/features/erd/erd_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1195,6 +1195,31 @@ void main() {
expect(schema.truncated, isFalse);
});
});

group('router on a larger schema (#1168)', () {
test('a 100-table schema routes every relation with a real path', () {
final columns = <List<String>>[];
final fks = <List<String>>[];
for (var i = 0; i < 100; i++) {
columns.add(['t$i', 'id', 'int', '1', '0']);
columns.add(['t$i', 'parent_id', 'int', '0', '1']);
if (i > 0) {
fks.add(['t$i', 'parent_id', 't${i ~/ 2}', 'id']);
}
if (i > 10 && i % 7 == 0) {
fks.add(['t$i', 'parent_id', 't${i - 9}', 'id']);
}
}
final schema = ErdSchema.fromCatalog(columnRows: columns, fkRows: fks);
final layout = ErdLayout.compute(schema);
final routes = ErdRouter.route(schema, layout);

expect(routes.length, schema.relations.length);
for (final r in routes) {
expect(r.points.length, greaterThanOrEqualTo(2));
}
});
});
}

/// Passes every call on and records the row limit it was given.
Expand Down
Loading