void PickCandidate(ref PackCandidate pc) { var box = this.input[this._toPack[pc.packIndex]]; for ( pc.skylineIndex = 0; pc.skylineIndex < this.skylines.Count; ++pc.skylineIndex ) { var skyline = this.skylines[pc.skylineIndex]; if (box.h + skyline.h > bin.h) { continue; } // if (box.h + skyline.h - this.lowestSky > this.spreadFactor) { // continue; // } pc.spreadSatisfied = (box.h + skyline.h - this.lowestSky) <= this.spreadFactor; pc.h = skyline.h; if (skyline.rFit >= box.w) { pc.crossingCount = 1; pc.fitNum = 0; if (box.w == skyline.w) { pc.fitNum += 1; } if (box.h + skyline.h == bin.h) { pc.fitNum += 1; } if (pc.skylineIndex > 0 && box.h == (this.skylines[pc.skylineIndex - 1].h - skyline.h)) { pc.fitNum += 1; } pc.localWaste = 0; for ( var widthLeft = box.w - skyline.w; widthLeft > 0; pc.crossingCount++ ) { var nsi = pc.crossingCount + pc.skylineIndex; UnityEngine.Debug.Assert(nsi < this.skylines.Count); var ns = this.skylines[nsi]; UnityEngine.Debug.Assert(skyline.h > ns.h); pc.localWaste += (ns.w) * (skyline.h - ns.h); widthLeft -= ns.w; } var mh = pc.h + box.h; // left for (int li = pc.skylineIndex - 1, lw = 0, ml = 0; li >= 0; li--) { var left = skylines[li]; if (left.h > mh) { if (ml < this.hSpread) { // if (true) { pc.localWaste += lw; } break; } ml += left.w; lw += (mh - left.h) * left.w; } { // right var crossIdx = pc.crossingCount + pc.skylineIndex - 1; var cross = this.skylines[crossIdx]; var mr = (cross.w + cross.x - skyline.x - box.w); UnityEngine.Debug.Assert(mr >= 0); var rw = (mh - cross.h) * mr; for (int ri = crossIdx + 1; ri < this.skylines.Count; ++ri) { var right = skylines[ri]; if (right.h > mh) { if (mr < this.hSpread) { pc.localWaste += rw; } break; } mr += right.w; rw += (mh - right.h) * right.w; } } // upper if (bin.h - mh < this.minHeight) { pc.localWaste += (bin.h - mh) * box.w; } packCandidates.Add(pc); } if (skyline.lFit >= box.w) { pc.crossingCount = -1; pc.fitNum = 0; if (box.w == skyline.w) { pc.fitNum += 1; } if (box.h + skyline.h == bin.h) { pc.fitNum += 1; } if (pc.skylineIndex < (this.skylines.Count - 1) && box.h == (this.skylines[pc.skylineIndex + 1].h - skyline.h)) { pc.fitNum += 1; } pc.localWaste = 0; var widthLeft = box.w - skyline.w; while (widthLeft > 0) { var nsi = pc.crossingCount + pc.skylineIndex; UnityEngine.Debug.Assert(nsi >= 0); var ns = this.skylines[nsi]; UnityEngine.Debug.Assert(skyline.h > ns.h); pc.localWaste += (ns.w) * (skyline.h - ns.h); widthLeft -= ns.w; pc.crossingCount--; } var mh = pc.h + box.h; // right for (int ri = pc.skylineIndex + 1, rw = 0, mr = 0; ri < skylines.Count; ++ri) { var right = skylines[ri]; if (right.h > mh) { if (mr < this.hSpread) { pc.localWaste += rw; } break; } mr += right.w; rw += (mh - right.h) * right.w; } {// left var crossIdx = pc.crossingCount + pc.skylineIndex + 1; var cross = this.skylines[crossIdx]; var ml = (skyline.x + skyline.w - box.w - cross.x); UnityEngine.Debug.Assert(ml >= 0); var lw = (mh - cross.h) * ml; for (int li = crossIdx - 1; li >= 0; --li) { var left = skylines[li]; if (left.h > mh) { if (ml < this.hSpread) { pc.localWaste += lw; } break; } ml += left.w; lw += (mh - left.h) * left.w; } } // upper if (bin.h - mh < this.minHeight) { pc.localWaste += (bin.h - mh) * box.w; } packCandidates.Add(pc); } } }
void UpdateSkylines(PackCandidate candidate, Box box) { UnityEngine.Debug.Assert(candidate.skylineIndex >= 0); UnityEngine.Debug.Assert(candidate.skylineIndex < this.skylines.Count); this.backBuffer.EnsureCap(this.skylines.Capacity); this.backBuffer.Clear(); if (candidate.crossingCount > 0) { for (int i = 0; i < candidate.skylineIndex - 1; ++i) { this.backBuffer.Add(this.skylines[i]); } var skyline = this.skylines[candidate.skylineIndex]; var oldX = skyline.x; skyline.h += box.h; skyline.w = box.w; if (candidate.skylineIndex - 1 >= 0) { if (this.skylines[candidate.skylineIndex - 1].h != skyline.h) { this.backBuffer.Add(this.skylines[candidate.skylineIndex - 1]); } else { skyline.x = this.skylines[candidate.skylineIndex - 1].x; skyline.w += oldX - skyline.x; } } if (this.backBuffer.Count > 0) { var last = this.backBuffer[this.backBuffer.Count - 1]; } var crossedIndex = candidate.skylineIndex + candidate.crossingCount - 1; var crossedLast = this.skylines[crossedIndex]; var crossedEnd = crossedLast.x + crossedLast.w; var newEnd = oldX + box.w; if (crossedEnd != newEnd) { UnityEngine.Debug.Assert(crossedEnd > newEnd); UnityEngine.Debug.Assert(crossedLast.h < skyline.h); this.backBuffer.Add(skyline); crossedLast.w = crossedLast.w - (newEnd - crossedLast.x); crossedLast.x = newEnd; this.backBuffer.Add(crossedLast); if (crossedIndex + 1 < this.skylines.Count) { this.backBuffer.Add(this.skylines[crossedIndex + 1]); } } else { if (crossedIndex + 1 < this.skylines.Count) { var next = this.skylines[crossedIndex + 1]; if (next.h == skyline.h) { skyline.w += next.w; this.backBuffer.Add(skyline); } else { this.backBuffer.Add(skyline); this.backBuffer.Add(next); } } else { this.backBuffer.Add(skyline); } } for (int i = crossedIndex + 2; i < this.skylines.Count; ++i) { this.backBuffer.Add(this.skylines[i]); } } else { UnityEngine.Debug.Assert(candidate.crossingCount < 0); var oldIdx = candidate.skylineIndex + candidate.crossingCount; // + 1 - 1 for (int i = 0; i < oldIdx; ++i) { this.backBuffer.Add(this.skylines[i]); } var skyline = this.skylines[candidate.skylineIndex]; var oldEnd = skyline.x + skyline.w; skyline.h += box.h; skyline.w = box.w; skyline.x = oldEnd - skyline.w; if (oldIdx >= 0) { var old = this.skylines[oldIdx]; if (skyline.x == old.x + old.w && old.h == skyline.h) { skyline.x = old.x; skyline.w += old.w; } else { this.backBuffer.Add(old); } } var crossedIdx = oldIdx + 1; var crossed = this.skylines[crossedIdx]; if (skyline.x != crossed.x) { crossed.w = skyline.x - crossed.x; this.backBuffer.Add(crossed); } var nextIdx = candidate.skylineIndex + 1; if (nextIdx < this.skylines.Count) { var next = this.skylines[nextIdx]; if (next.h == skyline.h) { skyline.w += next.w; this.backBuffer.Add(skyline); } else { this.backBuffer.Add(skyline); this.backBuffer.Add(next); } } else { this.backBuffer.Add(skyline); } for (int i = nextIdx + 1; i < this.skylines.Count; ++i) { this.backBuffer.Add(this.skylines[i]); } } // min gap promotion this.skylines.Clear(); for (int i = 0; i < this.backBuffer.Count; ++i) { var line = this.backBuffer[i]; if (line.w >= this.packDump) { this.skylines.Add(line); continue; } int lh; if (this.skylines.Count > 0) { lh = this.skylines[this.skylines.Count - 1].h; } else { lh = bin.h; } int rh; if (i + 1 < this.backBuffer.Count) { rh = this.backBuffer[i + 1].h; } else { rh = bin.h; } if (lh < line.h || rh < line.h) { this.skylines.Add(line); continue; } if (lh < rh && this.skylines.Count > 0) { this.skylines.Get(this.skylines.Count - 1).w += line.w; continue; } if (i + 1 < this.backBuffer.Count) { this.backBuffer.Get(i + 1).w += line.w; this.backBuffer.Get(i + 1).x = line.x; continue; } else { this.skylines.Add(line); // or should we just dump it } } // var tmp = this.skylines; // this.skylines = this.backBuffer; // this.backBuffer = tmp; // fix lfit/rfit. // TODO: optimize this shouldn't have been an O(n^2) op this.lowestSky = bin.h + 1; this.highestSky = -1; for (int i = 0; i < this.skylines.Count; ++i) { var h = this.skylines.Get(i).h; if (h > this.highestSky) { this.highestSky = h; } if (h < this.lowestSky) { this.lowestSky = h; } this.skylines.Get(i).rFit = this.skylines.Get(i).lFit = this.skylines.Get(i).w; for (int j = i - 1; j >= 0; --j) { if (this.skylines.Get(j).h > this.skylines.Get(i).h) { break; } this.skylines.Get(i).lFit += this.skylines.Get(j).w; } for (int j = i + 1; j < this.skylines.Count; ++j) { if (this.skylines.Get(j).h > this.skylines.Get(i).h) { break; } this.skylines.Get(i).rFit += this.skylines.Get(j).w; } } }