@lightningjs/renderer
Version:
Lightning 3 Renderer
171 lines • 6 kB
JavaScript
/*
* If not stated otherwise in this file or this component's LICENSE file the
* following copyright and licenses apply:
*
* Copyright 2023 Comcast Cable Communications Management, LLC.
*
* Licensed under the Apache License, Version 2.0 (the License);
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Matrix3d } from './lib/Matrix3d.js';
/**
* Creates an autosize manager for efficient child bounds calculation
*
* @remarks
* This function creates a closure-based manager that tracks child transform
* changes and calculates parent dimensions based on children's bounding boxes.
* It's optimized for performance with minimal allocations and fast lookups.
*
* @param parentNode - The autosize parent node
* @returns Object with autosize management methods
*/
export function createAutosizeManager(parentNode) {
const childDataMap = new Map();
let isActive = true;
let lastWidth = 0;
let lastHeight = 0;
let lastHasChanged = false;
const corners = [
{ x: 0, y: 0 },
{ x: 0, y: 0 },
{ x: 0, y: 0 },
{ x: 0, y: 0 },
];
/**
* Add or update a child node in the autosize calculation chain
* @param child - Child node to add or update
*/
const addOrUpdateChild = (child) => {
if (isActive === false)
return;
if (child === parentNode)
return; // Avoid circular references
const currentTransform = child.localTransform || Matrix3d.identity();
const existingData = childDataMap.get(child);
if (existingData !== undefined) {
// Update existing child
Matrix3d.copy(currentTransform, existingData.localTransform);
existingData.width = child.w;
existingData.height = child.h;
existingData.hasChanged = true;
}
else {
// Add new child
childDataMap.set(child, {
localTransform: Matrix3d.copy(currentTransform),
hasChanged: true,
width: child.w,
height: child.h,
});
}
// Mark parent for recalculation
parentNode.autosizeNeedsUpdate = true;
};
/**
* Remove a child node from the autosize calculation chain
* @param child - Child node to remove
*/
const removeChild = (child) => {
if (childDataMap.delete(child) === true) {
parentNode.autosizeNeedsUpdate = true;
}
};
/**
* Calculate the autosize dimensions based on all child bounds
* @returns Autosize calculation result
*/
const calculateAutosize = () => {
if (isActive === false || childDataMap.size === 0) {
return { width: 0, height: 0, hasChanged: false };
}
let hasAnyChildChanged = false;
for (const childData of childDataMap.values()) {
if (childData.hasChanged) {
hasAnyChildChanged = true;
break;
}
}
if (hasAnyChildChanged === false) {
return {
width: lastWidth,
height: lastHeight,
hasChanged: lastHasChanged,
};
}
let minX = Infinity;
let minY = Infinity;
let maxX = -Infinity;
let maxY = -Infinity;
for (const [child, childData] of childDataMap) {
if (!child.isRenderable)
continue;
const transform = child.localTransform || Matrix3d.identity();
const width = childData.width;
const height = childData.height;
corners[0].x = 0;
corners[0].y = 0;
corners[1].x = width;
corners[1].y = 0;
corners[2].x = width;
corners[2].y = height;
corners[3].x = 0;
corners[3].y = height;
for (let i = 0; i < 4; i++) {
const corner = corners[i];
const localX = transform.ta * corner.x + transform.tb * corner.y + transform.tx;
const localY = transform.tc * corner.x + transform.td * corner.y + transform.ty;
if (localX < minX)
minX = localX;
if (localY < minY)
minY = localY;
if (localX > maxX)
maxX = localX;
if (localY > maxY)
maxY = localY;
}
childData.hasChanged = false;
}
// Calculate container size based on maximum extent from container origin (0,0)
// This ensures the container position remains fixed and only size adjusts
const newWidth = maxX > 0 ? maxX : 0;
const newHeight = maxY > 0 ? maxY : 0;
const dimensionsChanged = lastWidth !== newWidth || lastHeight !== newHeight;
lastWidth = newWidth;
lastHeight = newHeight;
lastHasChanged = dimensionsChanged;
return {
width: newWidth,
height: newHeight,
hasChanged: dimensionsChanged,
};
};
/**
* Deactivate this autosize manager and clean up resources
*/
const deactivate = () => {
isActive = false;
childDataMap.clear();
};
return {
addOrUpdateChild,
removeChild,
calculateAutosize,
deactivate,
get active() {
return isActive;
},
get childCount() {
return childDataMap.size;
},
};
}
//# sourceMappingURL=AutosizeManager.js.map