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:
Eric Nguyen 2022-10-12 09:39:54 +00:00
parent 466ef2b08b
commit c256a76e01
45 changed files with 775 additions and 450 deletions

View file

@ -24,7 +24,8 @@ export function SelectContainer(
history.push({
lastAction: `Select ${containerId}`,
mainContainer: structuredClone(current.mainContainer),
mainContainer: current.mainContainer,
containers: structuredClone(current.containers),
selectedContainerId: containerId,
typeCounters: Object.assign({}, current.typeCounters),
symbols: structuredClone(current.symbols),
@ -48,8 +49,9 @@ export function DeleteContainer(
const history = GetCurrentHistory(fullHistory, historyCurrentStep);
const current = history[history.length - 1];
const mainContainerClone: IContainerModel = structuredClone(current.mainContainer);
const container = FindContainerById(mainContainerClone, containerId);
const containers = structuredClone(current.containers);
const mainContainerClone: IContainerModel | undefined = FindContainerById(containers, current.mainContainer);
const container = FindContainerById(containers, containerId);
if (container === undefined) {
throw new Error(`[DeleteContainer] Tried to delete a container that is not present in the main container: ${containerId}`);
@ -71,21 +73,22 @@ export function DeleteContainer(
}
const newSymbols = structuredClone(current.symbols);
UnlinkContainerFromSymbols(newSymbols, container);
UnlinkContainerFromSymbols(containers, newSymbols, container);
const index = container.parent.children.indexOf(container);
if (index > -1) {
const index = container.parent.children.indexOf(container.properties.id);
const success = containers.delete(container.properties.id);
if (index > -1 && success) {
container.parent.children.splice(index, 1);
} else {
throw new Error('[DeleteContainer] Could not find container among parent\'s children');
}
ApplyBehaviorsOnSiblings(container, current.symbols);
ApplyBehaviorsOnSiblings(containers, container, current.symbols);
// Select the previous container
// or select the one above
const selectedContainerId = GetSelectedContainerOnDelete(
mainContainerClone,
containers,
current.selectedContainerId,
container.parent,
index
@ -93,7 +96,8 @@ export function DeleteContainer(
history.push({
lastAction: `Delete ${containerId}`,
mainContainer: mainContainerClone,
mainContainer: current.mainContainer,
containers,
selectedContainerId,
typeCounters: Object.assign({}, current.typeCounters),
symbols: newSymbols,
@ -115,16 +119,15 @@ export function DeleteContainer(
* @returns {IContainerModel} Next selected container
*/
function GetSelectedContainerOnDelete(
mainContainerClone: IContainerModel,
containers: Map<string, IContainerModel>,
selectedContainerId: string,
parent: IContainerModel,
index: number
): string {
const newSelectedContainer = FindContainerById(mainContainerClone, selectedContainerId) ??
const newSelectedContainerId = FindContainerById(containers, selectedContainerId)?.properties.id ??
parent.children.at(index) ??
parent.children.at(index - 1) ??
parent;
const newSelectedContainerId = newSelectedContainer.properties.id;
parent.properties.id;
return newSelectedContainerId;
}
@ -134,8 +137,12 @@ function GetSelectedContainerOnDelete(
* @param symbols Symbols to update
* @param container Container to unlink
*/
function UnlinkContainerFromSymbols(symbols: Map<string, ISymbolModel>, container: IContainerModel): void {
const it = MakeDFSIterator(container);
function UnlinkContainerFromSymbols(
containers: Map<string, IContainerModel>,
symbols: Map<string, ISymbolModel>,
container: IContainerModel
): void {
const it = MakeDFSIterator(container, containers);
for (const child of it) {
const symbol = symbols.get(child.properties.linkedSymbolId);
if (symbol === undefined) {
@ -167,18 +174,19 @@ export function OnPropertyChange(
throw new Error('[OnPropertyChange] Property was changed before selecting a Container');
}
const mainContainerClone: IContainerModel = structuredClone(current.mainContainer);
const container: ContainerModel | undefined = FindContainerById(mainContainerClone, selected.properties.id);
const containers = structuredClone(current.containers);
const container: ContainerModel | undefined = FindContainerById(containers, selected.properties.id);
if (container === null || container === undefined) {
throw new Error('[OnPropertyChange] Container model was not found among children of the main container!');
}
SetContainer(container, key, value, type, current.symbols);
SetContainer(containers, container, key, value, type, current.symbols);
history.push({
lastAction: `Change ${key} of ${container.properties.id}`,
mainContainer: mainContainerClone,
mainContainer: current.mainContainer,
containers,
selectedContainerId: container.properties.id,
typeCounters: Object.assign({}, current.typeCounters),
symbols: structuredClone(current.symbols),
@ -189,20 +197,30 @@ export function OnPropertyChange(
/**
* Sort the parent children by x
* @param parentClone The clone used for the sort
* @param parent The clone used for the sort
* @returns void
*/
export function SortChildren(parentClone: IContainerModel | null | undefined): void {
if (parentClone === null || parentClone === undefined) {
export function SortChildren(
containers: Map<string, IContainerModel>,
parent: IContainerModel | null | undefined
): void {
if (parent === null || parent === undefined) {
return;
}
const isHorizontal = parentClone.properties.orientation === Orientation.Horizontal;
const children = parentClone.children;
const isHorizontal = parent.properties.orientation === Orientation.Horizontal;
const children = parent.children;
if (!isHorizontal) {
parentClone.children.sort(
(a, b) => {
parent.children.sort(
(aId, bId) => {
const a = FindContainerById(containers, aId);
const b = FindContainerById(containers, bId);
if (a === undefined || b === undefined) {
return 0;
}
const yA = TransformY(a.properties.y, a.properties.height, a.properties.positionReference);
const yB = TransformY(b.properties.y, b.properties.height, b.properties.positionReference);
if (yA < yB) {
@ -212,16 +230,23 @@ export function SortChildren(parentClone: IContainerModel | null | undefined): v
return 1;
}
// xA = xB
const indexA = children.indexOf(a);
const indexB = children.indexOf(b);
const indexA = children.indexOf(aId);
const indexB = children.indexOf(bId);
return indexA - indexB;
}
);
return;
}
parentClone.children.sort(
(a, b) => {
parent.children.sort(
(aId, bId) => {
const a = FindContainerById(containers, aId);
const b = FindContainerById(containers, bId);
if (a === undefined || b === undefined) {
return 0;
}
const xA = TransformX(a.properties.x, a.properties.width, a.properties.positionReference);
const xB = TransformX(b.properties.x, b.properties.width, b.properties.positionReference);
if (xA < xB) {
@ -231,8 +256,8 @@ export function SortChildren(parentClone: IContainerModel | null | undefined): v
return 1;
}
// xA = xB
const indexA = children.indexOf(a);
const indexB = children.indexOf(b);
const indexA = children.indexOf(aId);
const indexB = children.indexOf(bId);
return indexA - indexB;
}
);
@ -247,6 +272,7 @@ export function SortChildren(parentClone: IContainerModel | null | undefined): v
* @param symbols Current list of symbols
*/
function SetContainer(
containers: Map<string, IContainerModel>,
container: ContainerModel,
key: string, value: string | number | boolean | number[],
type: PropertyType,
@ -267,13 +293,13 @@ function SetContainer(
);
// sort the children list by their position
SortChildren(container.parent);
SortChildren(containers, container.parent);
// Apply special behaviors: rigid, flex, symbol, anchor
ApplyBehaviors(container, symbols);
ApplyBehaviors(containers, container, symbols);
// Apply special behaviors on siblings
ApplyBehaviorsOnSiblingsChildren(container, symbols);
ApplyBehaviorsOnSiblingsChildren(containers, container, symbols);
}
/**