srecord noeq G22_RegionToSSIs_v2(IImageRegion region) extends Meta { Color color; L ssis; new L growingSSIs; settable bool withDiagonals; settable bool checkCoherence = true; settable bool checkStreaks = true; private int x1, y; class GrowingSSI { settable int y1; new ShortBuffer data; int y2() { ret y1+l(data)/2; } SSI finish() { ret addAndReturn(ssis, new SSI(y1, y2()).data(data.toArray()) .color(color)); } bool isEmpty() { ret data.isEmpty(); } int lastx1() { ret data.get(l(data)-2); } int lastx2() { ret data.get(l(data)-1); } IntRange lastRange() { ret isEmpty() ? null : intRange(lastx1(), lastx2()); } // can add this line without breaking coherence? bool canAdd(IntRange r) { ret isEmpty() || intRangesOverlapNempty(r.start, r.end, lastx1()-diag(), lastx2()+diag()); } void add(IntRange r) { if (checkCoherence() && !canAdd(r)) fail("Coherence fail", +lastx1(), +lastx2(), +r); data.add(toShort_enforce(r.start)); data.add(toShort_enforce(r.end)); } } void finishSSI(int iSSI) { growingSSIs.remove(iSSI).finish(); } private GrowingSSI startSSI(int iSSI default l(growingSSIs), IntRange range) { if (scaffoldingEnabled()) printVars("startSSI", +iSSI, +range); var ssi = new GrowingSSI().y1(y); ssi.add(range); ret addAndReturn(growingSSIs, iSSI, ssi); } L lastStreaks() { ret reallyLazyMap(growingSSIs, -> .lastRange()); } int diag() { ret withDiagonals ? 1 : 0; } L get() { if (region == null) null; color = region.color(); ssis = new L; Rect r = region.bounds(); int x1 = this.x1 = r.x1(), y1 = r.y1(), y2 = r.y2(), h = y2-y1, w = r.w; bool scaff = scaffoldingEnabled(); for (y = y1; y < y2; y++) { reMutable y; L streaks = shiftIntRanges(x1, genericStreaks(w, x -> region.contains(x1+x, y))); var lastStreaks = lastStreaks(); if (checkStreaks()) assertProperStreaks(lastStreaks); // advance iSSI & iStreak simultaneously int iStreak = 0, iSSI = 0; int nStreaks = l(streaks); if (scaff) printVars(+y, +lastStreaks, +streaks); while ping (iStreak < nStreaks) { var range = streaks.get(iStreak); var ssi = _get(growingSSIs, iSSI); if (scaff) printVars(+y, +iStreak, +iSSI, +range, ssi := ssi?.lastRange()); // case 1: // ------ // ------- // // case 2: // // ------- // (left of current SSI or to the right of everything) if (ssi == null || range.end <= ssi.lastx1()-diag()) { startSSI(iSSI++, range); ++iStreak; continue; } // Now we know that we have another SSI. // case 3: // ------- // ------ // (Just end current SSI and do streak again.) if (range.start >= ssi.lastx2()+diag()) { finishSSI(iSSI); continue; } // Now we know the streak and the SSI overlap. // Find out if we have a choice on the top or on the bottom. // Check choices for streak int jStreak = iStreak+1; while (jStreak < nStreaks && ssi.canAdd(streaks.get(jStreak))) ++jStreak; // Check choices for SSI int jSSI = iSSI+1; while (jSSI < l(growingSSIs) && growingSSIs.get(jSSI).canAdd(range)) ++jSSI; int nSSI = jSSI-iSSI, nStreak = jStreak-iStreak; if (scaff) printVars(+nSSI, +nStreak); new Best best; if (nSSI > 1) { if (nStreak > 1) fail("ANOMALY", +nSSI, +nStreak, ssis := subList(lastStreaks(), iSSI, jSSI), streaks := subList(streaks, iStreak, jStreak)); // Choose longest SSI for (int idx = iSSI; idx < jSSI; idx++) best.put(idx, l(growingSSIs.get(idx).lastRange())); jSSI = best!; while (jSSI-- > iSSI) finishSSI(iSSI); } else if (nStreak > 1) { // Choose longest streak for (int idx = iStreak; idx < jStreak; idx++) best.put(idx, l(streaks.get(idx))); jStreak = best!; while (iStreak < jStreak) startSSI(iSSI++, streaks.get(iStreak++)); } // add line to SSI growingSSIs.get(iSSI++).add(streaks.get(iStreak++)); } // remaining SSIs are unpaired, close them while (iSSI < l(growingSSIs)) finishSSI(iSSI); } // finish all remaining SSIs for (ssi : cloneAndClear(growingSSIs)) ssi.finish(); ret ssis; } }