fillSpace: reimplement

This commit is contained in:
Peter Fajdiga
2024-10-25 21:35:19 +02:00
parent 2cc716f59e
commit 2f78f9afb1
2 changed files with 57 additions and 45 deletions

View File

@@ -0,0 +1,9 @@
function mapGetOrInit<K, V>(map: Map<K, V>, key: K, defaultItem: V) {
const item = map.get(key);
if (item !== undefined) {
return item;
} else {
map.set(key, defaultItem);
return defaultItem;
}
}

View File

@@ -4,55 +4,58 @@ function fillSpace(availableSpace: number, items: { min: number, max: number }[]
}
function findMeanSpaceFiller(availableSpace: number, items: { min: number, max: number }[]) {
let mean = Math.floor(availableSpace / items.length);
for (let i = 0; true; i++) {
let requiredSpace = 0;
let low = -Infinity;
let high = Infinity;
for (const item of items) {
const size = clamp(mean, item.min, item.max);
requiredSpace += size;
if (mean > item.min) {
if (size > low) {
low = size;
}
}
if (mean < item.max) {
if (size < high) {
high = size;
}
}
const ranges = buildRanges(items);
let requiredSpace = items.reduce((acc, item) => acc + item.min, 0);
for (let i = 0; i < ranges.length; i++) {
const range = ranges[i];
const rangeSize = range.end - range.start;
const maxRequiredSpaceDelta = rangeSize * range.n;
if (requiredSpace + maxRequiredSpaceDelta >= availableSpace || i === ranges.length-1) {
const positionInRange = (availableSpace - requiredSpace) / maxRequiredSpaceDelta;
return Math.floor(range.start + rangeSize * positionInRange);
}
requiredSpace += maxRequiredSpaceDelta;
}
return 0;
const oldMean = mean;
const error = requiredSpace - availableSpace;
if (error > 0) {
// need to decrease mean
let decreasable = 0;
for (const item of items) {
if (mean > item.min && low - error < item.max) {
decreasable++;
}
}
if (decreasable > 0) {
mean = Math.floor(low - error / decreasable);
}
} else if (error < 0) {
// need to increase mean
let increasable = 0;
for (const item of items) {
if (mean < item.max && high - error > item.min) {
increasable++;
}
}
if (increasable > 0) {
mean = Math.floor(high - error / increasable);
}
function buildRanges(items: { min: number, max: number }[]) {
const landmarks = buildLandmarks(items);
const ranges: Range[] = [];
let n = 0;
for (let i = 1; i < landmarks.length; i++) {
const startLandmark = landmarks[i-1];
const endLandmark = landmarks[i];
n = n - startLandmark.nMax + startLandmark.nMin;
ranges.push({
start: startLandmark.value,
end: endLandmark.value,
n: n,
});
}
return ranges;
if (mean === oldMean) {
log(`findMeanSpaceFiller ${i} / ${items.length}`);
return mean;
type Range = {
start: number,
end: number,
n: number,
};
function buildLandmarks(items: { min: number, max: number }[]) {
const landmarks = new Map<number, Landmark>();
for (const item of items) {
mapGetOrInit(landmarks, item.min, { value: item.min, nMin: 0, nMax: 0 }).nMin++;
mapGetOrInit(landmarks, item.max, { value: item.max, nMin: 0, nMax: 0 }).nMax++;
}
const array = Array.from(landmarks.values());
array.sort((a, b) => a.value - b.value);
return array;
type Landmark = {
value: number,
nMin: number,
nMax: number,
}
}
}
}