Merged PR 212: Optimize FindChildrenById from O(n) to O(1)
Optimize FindChildrenById from O(n) to O(1): - Deprecate FindContainerByIdDFS - Container: Replace Children to string[] - Add HashMap to IHistoryState that contains all containers To access a container by id now cost O(1) without any additional cost + Implement CICD for SVGLibs
This commit is contained in:
parent
466ef2b08b
commit
c256a76e01
45 changed files with 775 additions and 450 deletions
|
@ -16,16 +16,28 @@
|
|||
import { IContainerModel } from '../../../Interfaces/IContainerModel';
|
||||
import { Orientation } from '../../../Enums/Orientation';
|
||||
import { ConstraintBodyInsideUnallocatedWidth } from './RigidBodyBehaviors';
|
||||
import { FindContainerById } from '../../../utils/itertools';
|
||||
|
||||
/**
|
||||
* Impose the container position to its siblings
|
||||
* Apply the following modification to the overlapping rigid body container :
|
||||
* @param container Container to impose its position
|
||||
*/
|
||||
export function ApplyAnchor(container: IContainerModel, parent: IContainerModel): IContainerModel {
|
||||
const rigidBodies = parent.children.filter(
|
||||
child => !child.properties.isAnchor
|
||||
);
|
||||
export function ApplyAnchor(containers: Map<string, IContainerModel>, container: IContainerModel, parent: IContainerModel): IContainerModel {
|
||||
const rigidBodies: IContainerModel[] = [];
|
||||
parent.children.forEach(
|
||||
childId => {
|
||||
const child = FindContainerById(containers, childId);
|
||||
|
||||
if (child === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (child.properties.isAnchor) {
|
||||
return;
|
||||
}
|
||||
rigidBodies.push(child);
|
||||
});
|
||||
|
||||
const isHorizontal = parent.properties.orientation === Orientation.Horizontal;
|
||||
const overlappingContainers = isHorizontal
|
||||
|
@ -33,7 +45,7 @@ export function ApplyAnchor(container: IContainerModel, parent: IContainerModel)
|
|||
: GetVerticallyOverlappingContainers(container, rigidBodies);
|
||||
|
||||
for (const overlappingContainer of overlappingContainers) {
|
||||
ConstraintBodyInsideUnallocatedWidth(overlappingContainer);
|
||||
ConstraintBodyInsideUnallocatedWidth(containers, overlappingContainer);
|
||||
}
|
||||
return container;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { IContainerModel } from '../../../Interfaces/IContainerModel';
|
||||
import { ISymbolModel } from '../../../Interfaces/ISymbolModel';
|
||||
import { APPLY_BEHAVIORS_ON_CHILDREN, ENABLE_RIGID, ENABLE_SWAP } from '../../../utils/default';
|
||||
import { FindContainerById, MakeChildrenIterator } from '../../../utils/itertools';
|
||||
import { ApplyAnchor, GetOverlappingContainers } from './AnchorBehaviors';
|
||||
import { Flex } from './FlexBehaviors';
|
||||
import { ApplyRigidBody } from './RigidBodyBehaviors';
|
||||
|
@ -13,7 +14,7 @@ import { ApplySymbol } from './SymbolBehaviors';
|
|||
* @param container Container to recalculate its positions
|
||||
* @returns Updated container
|
||||
*/
|
||||
export function ApplyBehaviors(container: IContainerModel, symbols: Map<string, ISymbolModel>): IContainerModel {
|
||||
export function ApplyBehaviors(containers: Map<string, IContainerModel>, container: IContainerModel, symbols: Map<string, ISymbolModel>): IContainerModel {
|
||||
try {
|
||||
const symbol = symbols.get(container.properties.linkedSymbolId);
|
||||
if (container.properties.linkedSymbolId !== '' && symbol !== undefined) {
|
||||
|
@ -24,24 +25,24 @@ export function ApplyBehaviors(container: IContainerModel, symbols: Map<string,
|
|||
const parent = container.parent;
|
||||
|
||||
if (container.properties.isAnchor) {
|
||||
ApplyAnchor(container, parent);
|
||||
ApplyAnchor(containers, container, parent);
|
||||
}
|
||||
|
||||
if (ENABLE_SWAP) {
|
||||
ApplySwap(container, parent);
|
||||
ApplySwap(containers, container, parent);
|
||||
}
|
||||
|
||||
Flex(container, parent);
|
||||
Flex(containers, container, parent);
|
||||
|
||||
if (ENABLE_RIGID) {
|
||||
ApplyRigidBody(container, parent);
|
||||
ApplyRigidBody(containers, container, parent);
|
||||
}
|
||||
}
|
||||
|
||||
if (APPLY_BEHAVIORS_ON_CHILDREN) {
|
||||
// Apply DFS by recursion
|
||||
for (const child of container.children) {
|
||||
ApplyBehaviors(child, symbols);
|
||||
for (const child of MakeChildrenIterator(containers, container.children)) {
|
||||
ApplyBehaviors(containers, child, symbols);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
|
@ -64,23 +65,32 @@ export function ApplyBehaviors(container: IContainerModel, symbols: Map<string,
|
|||
* @param symbols
|
||||
* @returns
|
||||
*/
|
||||
export function ApplyBehaviorsOnSiblingsChildren(newContainer: IContainerModel, symbols: Map<string, ISymbolModel>): void {
|
||||
export function ApplyBehaviorsOnSiblingsChildren(
|
||||
containers: Map<string, IContainerModel>,
|
||||
newContainer: IContainerModel,
|
||||
symbols: Map<string, ISymbolModel>): void {
|
||||
if (newContainer.parent === null || newContainer.parent === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
newContainer.parent.children
|
||||
.forEach((container: IContainerModel) => {
|
||||
if (container.parent != null) {
|
||||
UpdateWarning(container, container.parent);
|
||||
.forEach((containerId: string) => {
|
||||
const container = FindContainerById(containers, containerId);
|
||||
|
||||
if (container === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (container.parent !== null) {
|
||||
UpdateWarning(containers, container, container.parent);
|
||||
}
|
||||
|
||||
if (container === newContainer) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const child of container.children) {
|
||||
ApplyBehaviors(child, symbols);
|
||||
for (const child of MakeChildrenIterator(containers, container.children)) {
|
||||
ApplyBehaviors(containers, child, symbols);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -91,30 +101,47 @@ export function ApplyBehaviorsOnSiblingsChildren(newContainer: IContainerModel,
|
|||
* @param symbols
|
||||
* @returns
|
||||
*/
|
||||
export function ApplyBehaviorsOnSiblings(newContainer: IContainerModel, symbols: Map<string, ISymbolModel>): void {
|
||||
export function ApplyBehaviorsOnSiblings(containers: Map<string, IContainerModel>, newContainer: IContainerModel, symbols: Map<string, ISymbolModel>): void {
|
||||
if (newContainer.parent === null || newContainer.parent === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
newContainer.parent.children
|
||||
.forEach((container: IContainerModel) => {
|
||||
ApplyBehaviors(container, symbols);
|
||||
.forEach((containerId: string) => {
|
||||
const container = FindContainerById(containers, containerId);
|
||||
|
||||
if (container === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
ApplyBehaviors(containers, container, symbols);
|
||||
|
||||
if (container.parent != null) {
|
||||
UpdateWarning(container, container.parent);
|
||||
UpdateWarning(containers, container, container.parent);
|
||||
}
|
||||
|
||||
if (container === newContainer) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const child of container.children) {
|
||||
ApplyBehaviors(child, symbols);
|
||||
for (const child of MakeChildrenIterator(containers, container.children)) {
|
||||
ApplyBehaviors(containers, child, symbols);
|
||||
}
|
||||
});
|
||||
}
|
||||
function UpdateWarning(container: IContainerModel, parent: IContainerModel): void {
|
||||
const overlappingContainers = GetOverlappingContainers(container, parent.children);
|
||||
function UpdateWarning(containers: Map<string, IContainerModel>, container: IContainerModel, parent: IContainerModel): void {
|
||||
const targetContainers: IContainerModel[] = [];
|
||||
|
||||
parent.children.forEach((child) => {
|
||||
const targetContainer = FindContainerById(containers, child);
|
||||
|
||||
if (targetContainer === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
targetContainers.push(targetContainer);
|
||||
});
|
||||
const overlappingContainers = GetOverlappingContainers(container, targetContainers);
|
||||
if (overlappingContainers.length > 0) {
|
||||
container.properties.warning = `There are overlapping containers: ${overlappingContainers.map(c => c.properties.id).join(' ')}`;
|
||||
} else {
|
||||
|
|
|
@ -2,6 +2,7 @@ import { IContainerModel } from '../../../Interfaces/IContainerModel';
|
|||
import { Orientation } from '../../../Enums/Orientation';
|
||||
import { Simplex } from '../../../utils/simplex';
|
||||
import { ApplyWidthMargin, ApplyXMargin } from '../../../utils/svg';
|
||||
import { MakeChildrenIterator } from '../../../utils/itertools';
|
||||
|
||||
interface IFlexibleGroup {
|
||||
group: IContainerModel[]
|
||||
|
@ -13,13 +14,13 @@ interface IFlexibleGroup {
|
|||
* Flex the container and its siblings (mutate)
|
||||
* @returns Flexed container
|
||||
*/
|
||||
export function Flex(container: IContainerModel, parent: IContainerModel): void {
|
||||
export function Flex(containers: Map<string, IContainerModel>, container: IContainerModel, parent: IContainerModel): void {
|
||||
const isVertical = parent.properties.orientation === Orientation.Vertical;
|
||||
|
||||
if (isVertical) {
|
||||
const wantedWidth = Math.min(container.properties.maxWidth, parent.properties.width);
|
||||
container.properties.width = ApplyWidthMargin(wantedWidth, container.properties.margin.left, container.properties.margin.right);
|
||||
const flexibleGroups = GetVerticalFlexibleGroups(parent);
|
||||
const flexibleGroups = GetVerticalFlexibleGroups(containers, parent);
|
||||
for (const flexibleGroup of flexibleGroups) {
|
||||
FlexGroupVertically(flexibleGroup);
|
||||
}
|
||||
|
@ -28,7 +29,7 @@ export function Flex(container: IContainerModel, parent: IContainerModel): void
|
|||
|
||||
const wantedHeight = Math.min(container.properties.maxHeight, parent.properties.height);
|
||||
container.properties.height = ApplyWidthMargin(wantedHeight, container.properties.margin.top, container.properties.margin.bottom);
|
||||
const flexibleGroups = GetHorizontalFlexibleGroups(parent);
|
||||
const flexibleGroups = GetHorizontalFlexibleGroups(containers, parent);
|
||||
for (const flexibleGroup of flexibleGroups) {
|
||||
FlexGroupHorizontally(flexibleGroup);
|
||||
}
|
||||
|
@ -39,12 +40,12 @@ export function Flex(container: IContainerModel, parent: IContainerModel): void
|
|||
* @param parent Parent in which the flexible children will be set in groups
|
||||
* @returns a list of groups of flexible containers
|
||||
*/
|
||||
export function GetHorizontalFlexibleGroups(parent: IContainerModel): IFlexibleGroup[] {
|
||||
export function GetHorizontalFlexibleGroups(containers: Map<string, IContainerModel>, parent: IContainerModel): IFlexibleGroup[] {
|
||||
const flexibleGroups: IFlexibleGroup[] = [];
|
||||
let group: IContainerModel[] = [];
|
||||
let offset = 0;
|
||||
let size = 0;
|
||||
for (const child of parent.children) {
|
||||
for (const child of MakeChildrenIterator(containers, parent.children)) {
|
||||
if (child.properties.isAnchor) {
|
||||
size = child.properties.x - offset;
|
||||
const flexibleGroup: IFlexibleGroup = {
|
||||
|
@ -77,12 +78,15 @@ export function GetHorizontalFlexibleGroups(parent: IContainerModel): IFlexibleG
|
|||
* @param parent Parent in which the flexible children will be set in groups
|
||||
* @returns a list of groups of flexible containers
|
||||
*/
|
||||
export function GetVerticalFlexibleGroups(parent: IContainerModel): IFlexibleGroup[] {
|
||||
export function GetVerticalFlexibleGroups(
|
||||
containers: Map<string, IContainerModel>,
|
||||
parent: IContainerModel
|
||||
): IFlexibleGroup[] {
|
||||
const flexibleGroups: IFlexibleGroup[] = [];
|
||||
let group: IContainerModel[] = [];
|
||||
let offset = 0;
|
||||
let size = 0;
|
||||
for (const child of parent.children) {
|
||||
for (const child of MakeChildrenIterator(containers, parent.children)) {
|
||||
if (child.properties.isAnchor) {
|
||||
size = child.properties.y - offset;
|
||||
const flexibleGroup: IFlexibleGroup = {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { IContainerModel } from '../../../Interfaces/IContainerModel';
|
||||
import { Orientation } from '../../../Enums/Orientation';
|
||||
import { ReversePairwise } from '../../../utils/itertools';
|
||||
import { MakeChildrenIterator, ReversePairwise } from '../../../utils/itertools';
|
||||
import { Flex } from './FlexBehaviors';
|
||||
|
||||
/**
|
||||
|
@ -8,12 +8,17 @@ import { Flex } from './FlexBehaviors';
|
|||
* @param container
|
||||
* @returns
|
||||
*/
|
||||
export function ApplyPush(container: IContainerModel, parent: IContainerModel): IContainerModel {
|
||||
export function ApplyPush(
|
||||
containers: Map<string, IContainerModel>,
|
||||
container: IContainerModel,
|
||||
parent: IContainerModel
|
||||
): IContainerModel {
|
||||
if (parent.children.length <= 1) {
|
||||
return container;
|
||||
}
|
||||
|
||||
const children = parent.children;
|
||||
const children: IContainerModel[] = [...MakeChildrenIterator(containers, parent.children)];
|
||||
|
||||
const isHorizontal = parent.properties.orientation === Orientation.Horizontal;
|
||||
|
||||
if (isHorizontal) {
|
||||
|
@ -22,7 +27,7 @@ export function ApplyPush(container: IContainerModel, parent: IContainerModel):
|
|||
PushContainersVertically(container, children);
|
||||
}
|
||||
|
||||
Flex(container, parent);
|
||||
Flex(containers, container, parent);
|
||||
return container;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import { IContainerModel } from '../../../Interfaces/IContainerModel';
|
|||
import { ISizePointer } from '../../../Interfaces/ISizePointer';
|
||||
import { Orientation } from '../../../Enums/Orientation';
|
||||
import { ENABLE_HARD_RIGID } from '../../../utils/default';
|
||||
import { MakeChildrenIterator } from '../../../utils/itertools';
|
||||
|
||||
/**
|
||||
* "Transform the container into a rigid body"
|
||||
|
@ -21,13 +22,14 @@ import { ENABLE_HARD_RIGID } from '../../../utils/default';
|
|||
* @returns A rigid body container
|
||||
*/
|
||||
export function ApplyRigidBody(
|
||||
containers: Map<string, IContainerModel>,
|
||||
container: IContainerModel,
|
||||
parent: IContainerModel
|
||||
): IContainerModel {
|
||||
container = ConstraintBodyInsideParent(container, parent);
|
||||
|
||||
if (ENABLE_HARD_RIGID) {
|
||||
container = ConstraintBodyInsideUnallocatedWidth(container);
|
||||
container = ConstraintBodyInsideUnallocatedWidth(containers, container);
|
||||
}
|
||||
|
||||
return container;
|
||||
|
@ -117,6 +119,7 @@ function ConstraintBodyInsideSpace(
|
|||
* @returns Updated container
|
||||
*/
|
||||
export function ConstraintBodyInsideUnallocatedWidth(
|
||||
containers: Map<string, IContainerModel>,
|
||||
container: IContainerModel
|
||||
): IContainerModel {
|
||||
if (container.parent === null || container.parent === undefined) {
|
||||
|
@ -126,10 +129,11 @@ export function ConstraintBodyInsideUnallocatedWidth(
|
|||
// Get the available spaces of the parent
|
||||
const isHorizontal =
|
||||
container.parent.properties.orientation === Orientation.Horizontal;
|
||||
const children: IContainerModel[] = [...MakeChildrenIterator(containers, container.parent.children)];
|
||||
const availableWidths = GetAvailableWidths(
|
||||
0,
|
||||
container.parent.properties.width,
|
||||
container.parent.children,
|
||||
children,
|
||||
container,
|
||||
isHorizontal
|
||||
);
|
||||
|
|
|
@ -5,9 +5,13 @@
|
|||
import { IContainerModel } from '../../../Interfaces/IContainerModel';
|
||||
import { Orientation } from '../../../Enums/Orientation';
|
||||
import { GetHorizontallyOverlappingContainers, GetVerticallyOverlappingContainers } from './AnchorBehaviors';
|
||||
import { MakeChildrenIterator } from '../../../utils/itertools';
|
||||
|
||||
export function ApplySwap(container: IContainerModel, parent: IContainerModel): void {
|
||||
const children = parent.children;
|
||||
export function ApplySwap(
|
||||
containers: Map<string, IContainerModel>,
|
||||
container: IContainerModel,
|
||||
parent: IContainerModel): void {
|
||||
const children = [...MakeChildrenIterator(containers, parent.children)];
|
||||
|
||||
const isVertical = parent.properties.orientation === Orientation.Vertical;
|
||||
if (isVertical) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue