49 lines
1.5 KiB
TypeScript
49 lines
1.5 KiB
TypeScript
/**
|
|
* Y bounds of filled grain volume — matches GrainFillFlat / GrainCableFill fallback volume math.
|
|
*/
|
|
export interface GrainYBounds {
|
|
minY: number;
|
|
maxY: number;
|
|
}
|
|
|
|
export function grainYBoundsFromFillPercent(
|
|
diameter: number,
|
|
sidewallHeight: number,
|
|
hopperHeight: number,
|
|
fillPercent: number
|
|
): GrainYBounds {
|
|
const radius = diameter / 2;
|
|
const sidewallBaseY = -sidewallHeight / 2;
|
|
const hopperTipY = sidewallBaseY - hopperHeight;
|
|
|
|
if (fillPercent <= 0) {
|
|
return { minY: sidewallBaseY, maxY: sidewallBaseY };
|
|
}
|
|
|
|
const cylinderVolume = Math.PI * radius * radius * sidewallHeight;
|
|
const hopperVolume =
|
|
hopperHeight > 0 ? (1 / 3) * Math.PI * radius * radius * hopperHeight : 0;
|
|
const totalVolume = cylinderVolume + hopperVolume;
|
|
const filledVolume = totalVolume * fillPercent;
|
|
|
|
if (hopperHeight > 0 && filledVolume <= hopperVolume) {
|
|
const ratio = filledVolume / hopperVolume;
|
|
const hopperFillHeight = hopperHeight * Math.pow(ratio, 1 / 3);
|
|
return {
|
|
minY: hopperTipY,
|
|
maxY: hopperTipY + hopperFillHeight,
|
|
};
|
|
}
|
|
|
|
const hopperFillHeight = hopperHeight;
|
|
const remainingVolume = filledVolume - hopperVolume;
|
|
const cylinderFillHeight = Math.max(
|
|
0,
|
|
remainingVolume / (Math.PI * radius * radius)
|
|
);
|
|
|
|
return {
|
|
minY: hopperHeight > 0 ? hopperTipY : sidewallBaseY,
|
|
maxY: sidewallBaseY + cylinderFillHeight,
|
|
};
|
|
}
|