mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 19:04:02 +00:00
Release God's Eye View as open source
This commit is contained in:
1 parent
a5aaea7e16
commit
880a672b5e
433 files changed
+219893
-23
No files matched your search
@@ -0,0 +1,149 @@
|
||||
/**
|
||||
* Fits expanded panels into a shared vertical corridor. Natural heights are
|
||||
* retained when they fit; constrained panels keep a usable floor and share
|
||||
* the remaining room in proportion to their unmet height.
|
||||
*
|
||||
* @param {object} input Layout measurements.
|
||||
* @param {number[]} input.naturalHeights Expanded-panel natural heights.
|
||||
* @param {number} input.availableHeight Height available to expanded panels.
|
||||
* @param {number} [input.minimumHeight=96] Preferred usable floor per panel.
|
||||
* @returns {number[]} One allocated height per expanded panel.
|
||||
*/
|
||||
export function allocatePanelStackHeights({
|
||||
naturalHeights,
|
||||
availableHeight,
|
||||
minimumHeight = 96,
|
||||
}) {
|
||||
const natural = naturalHeights.map((height) => Math.max(0, Number(height) || 0));
|
||||
if (!natural.length) return [];
|
||||
|
||||
const available = Math.max(0, Number(availableHeight) || 0);
|
||||
const naturalTotal = natural.reduce((sum, height) => sum + height, 0);
|
||||
if (naturalTotal <= available) return natural;
|
||||
if (available === 0) return natural.map(() => 0);
|
||||
|
||||
const preferredFloor = Math.max(0, Number(minimumHeight) || 0);
|
||||
const base = natural.map((height) => Math.min(height, preferredFloor));
|
||||
const baseTotal = base.reduce((sum, height) => sum + height, 0);
|
||||
if (baseTotal >= available) {
|
||||
const scale = baseTotal > 0 ? available / baseTotal : 0;
|
||||
return base.map((height) => height * scale);
|
||||
}
|
||||
|
||||
const remaining = available - baseTotal;
|
||||
const unmet = natural.map((height, index) => Math.max(0, height - base[index]));
|
||||
const unmetTotal = unmet.reduce((sum, height) => sum + height, 0);
|
||||
if (unmetTotal <= 0) return base;
|
||||
return base.map((height, index) => height + remaining * (unmet[index] / unmetTotal));
|
||||
}
|
||||
|
||||
/**
|
||||
* Solves the accordion corridor's bottom boundary against the obstacles that
|
||||
* still limit it.
|
||||
*
|
||||
* Every painted obstacle limits the corridor. This includes Cockpit CONTACT
|
||||
* and peripheral HUD surfaces, so reopening a map panel cannot cover them.
|
||||
*
|
||||
* @param {object} input Corridor measurements.
|
||||
* @param {number} input.baseBottom Viewport-inset bottom boundary, in px.
|
||||
* @param {Array<{top: number}>} [input.obstacles] Live obstacle tops.
|
||||
* @param {number} [input.safeGap=0] Clearance kept above each obstacle, in px.
|
||||
* @returns {number} Bottom boundary in px.
|
||||
*/
|
||||
export function resolveLeftStackBottomBoundary({
|
||||
baseBottom,
|
||||
obstacles = [],
|
||||
safeGap = 0,
|
||||
}) {
|
||||
let bottom = Number(baseBottom) || 0;
|
||||
const gap = Number(safeGap) || 0;
|
||||
for (const obstacle of obstacles) {
|
||||
const top = Number(obstacle?.top);
|
||||
if (!Number.isFinite(top)) continue;
|
||||
bottom = Math.min(bottom, top - gap);
|
||||
}
|
||||
return bottom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns later expanded panels whose allocation would expose less than the
|
||||
* requested share of their intrinsic height. The first panel always remains
|
||||
* expanded so every lane keeps one useful primary surface.
|
||||
*
|
||||
* @param {object} input Panel height measurements.
|
||||
* @param {number[]} input.naturalHeights Intrinsic expanded heights.
|
||||
* @param {number[]} input.allocatedHeights Allocated expanded heights.
|
||||
* @param {number} [input.minimumVisibleRatio=0.5] Minimum useful height share.
|
||||
* @param {boolean} [input.collapseLaterPanels=false] Collapse every competitor after the primary panel.
|
||||
* @returns {number[]} Candidate indices to present as collapsed controls.
|
||||
*/
|
||||
export function panelStackAutoCollapseIndices({
|
||||
naturalHeights,
|
||||
allocatedHeights,
|
||||
minimumVisibleRatio = 0.5,
|
||||
collapseLaterPanels = false,
|
||||
}) {
|
||||
const threshold = Math.max(0, Number(minimumVisibleRatio) || 0);
|
||||
const collapsed = [];
|
||||
for (let index = 1; index < naturalHeights.length; index += 1) {
|
||||
if (collapseLaterPanels) {
|
||||
collapsed.push(index);
|
||||
continue;
|
||||
}
|
||||
const natural = Math.max(0, Number(naturalHeights[index]) || 0);
|
||||
const allocated = Math.max(0, Number(allocatedHeights[index]) || 0);
|
||||
if (natural > 0 && allocated / natural < threshold) collapsed.push(index);
|
||||
}
|
||||
return collapsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Balance a desktop panel corridor around the viewport midpoint without
|
||||
* crossing its measured obstacle boundaries. If centering would shrink the
|
||||
* lane below its usable minimum, retain the original aligned corridor.
|
||||
*
|
||||
* @param {object} input Corridor measurements.
|
||||
* @param {number} input.viewportHeight Current viewport height.
|
||||
* @param {number} input.safeTop Proposed corridor top.
|
||||
* @param {number} input.safeBottom Proposed corridor bottom.
|
||||
* @param {number} input.obstacleSafeTop Highest obstacle-safe top boundary.
|
||||
* @param {number} input.obstacleSafeBottom Lowest obstacle-safe bottom boundary.
|
||||
* @param {number} input.minimumHeight Minimum useful lane height.
|
||||
* @returns {{ safeTop: number, safeBottom: number }} Bounded corridor.
|
||||
*/
|
||||
export function resolvePanelStackCorridor({
|
||||
viewportHeight,
|
||||
safeTop,
|
||||
safeBottom,
|
||||
obstacleSafeTop,
|
||||
obstacleSafeBottom,
|
||||
minimumHeight,
|
||||
}) {
|
||||
const height = Math.max(1, Number(viewportHeight) || 1);
|
||||
const boundaryTop = Math.max(0, Number(obstacleSafeTop) || 0);
|
||||
const boundaryBottom = Math.max(
|
||||
boundaryTop,
|
||||
Math.min(height, Number(obstacleSafeBottom) || 0),
|
||||
);
|
||||
let top = Math.max(boundaryTop, Math.min(boundaryBottom, Number(safeTop) || 0));
|
||||
let bottom = Math.max(top, Math.min(boundaryBottom, Number(safeBottom) || 0));
|
||||
const minimum = Math.max(0, Number(minimumHeight) || 0);
|
||||
const midpoint = height * 0.5;
|
||||
|
||||
if (top < midpoint && bottom > midpoint) {
|
||||
const centeredHalfHeight = Math.min(midpoint - top, bottom - midpoint);
|
||||
const centeredTop = midpoint - centeredHalfHeight;
|
||||
const centeredBottom = midpoint + centeredHalfHeight;
|
||||
if (centeredBottom - centeredTop >= minimum) {
|
||||
top = centeredTop;
|
||||
bottom = centeredBottom;
|
||||
}
|
||||
}
|
||||
|
||||
if (bottom - top < minimum) {
|
||||
top = Math.max(boundaryTop, bottom - minimum);
|
||||
bottom = Math.min(boundaryBottom, Math.max(bottom, top + minimum));
|
||||
}
|
||||
|
||||
return { safeTop: top, safeBottom: bottom };
|
||||
}
|
||||
Reference in new issue
Block a user