Merged PR 196: Implement Vertical orientation + Upgrade Heroicons to 2.0

Implémenter l'orientation verticale

Modifier l'effet de append

Implementer RigidBody

Implementer Flex et simplex

Implémenter Push

Implémenter Swap

Implement MinMaxHeight without behaviors

Fix Margin for Height

Implement PositionReference

Fix dimension vertical position inside children

Add orientation change in form

Implement sortChildren

Implement Anchor

Fix warning message on overlapping

Fix minimap when root container is vertical

#7287
#7288
#7289
#7290
#7291
#7292
#7294
#7295
#7296
#7297
#7298
#7299
#7300
#7301
#7302
This commit is contained in:
Eric Nguyen 2022-09-28 16:07:56 +00:00
parent 459e83a0c8
commit 18cbacaca1
45 changed files with 2112 additions and 1063 deletions

View file

@ -9,6 +9,7 @@ import { IContainerModel, ContainerModel } from '../../../Interfaces/IContainerM
import { IHistoryState } from '../../../Interfaces/IHistoryState';
import { IPattern, GetPattern, ContainerOrPattern } from '../../../Interfaces/IPattern';
import { ISymbolModel } from '../../../Interfaces/ISymbolModel';
import { Orientation } from '../../../Interfaces/Orientation';
import { GetDefaultContainerProps, DEFAULTCHILDTYPE_MAX_DEPTH, DEFAULTCHILDTYPE_ALLOW_CYCLIC } from '../../../utils/default';
import { FindContainerById } from '../../../utils/itertools';
import { ApplyMargin } from '../../../utils/svg';
@ -137,12 +138,12 @@ function AddNewContainerToParent(
let x = containerConfig.X ?? 0;
let y = containerConfig.Y ?? 0;
let width = containerConfig.Width ?? containerConfig.MaxWidth ?? containerConfig.MinWidth ?? parentClone.properties.width;
let height = containerConfig.Height ?? parentClone.properties.height;
let height = containerConfig.Height ?? containerConfig.MaxHeight ?? containerConfig.MinHeight ?? parentClone.properties.height;
({ x, y, width, height } = ApplyMargin(x, y, width, height, left, bottom, top, right));
// Apply an add method (append or insert/replace)
x = ApplyAddMethod(index + typeIndex, containerConfig, parentClone, x);
({ x, y } = ApplyAddMethod(index + typeIndex, containerConfig, parentClone, x, y));
// Set the counter of the object type in order to assign an unique id
UpdateCounters(newCounters, type);
@ -400,8 +401,9 @@ function ApplyAddMethod(
index: number,
containerConfig: IAvailableContainer,
parent: IContainerModel,
x: number
): number {
x: number,
y: number
): { x: number, y: number } {
if (index > 0 && (
containerConfig.AddMethod === undefined ||
containerConfig.AddMethod === null ||
@ -412,8 +414,13 @@ function ApplyAddMethod(
.at(index - 1);
if (lastChild !== undefined) {
x += (lastChild.properties.x + lastChild.properties.width);
const isHorizontal = parent.properties.orientation === Orientation.Horizontal;
if (isHorizontal) {
x += lastChild.properties.x + lastChild.properties.width;
} else {
y += lastChild.properties.y + lastChild.properties.height;
}
}
}
return x;
return { x, y };
}

View file

@ -1,12 +1,13 @@
import { IHistoryState } from '../../../Interfaces/IHistoryState';
import { ContainerModel, IContainerModel } from '../../../Interfaces/IContainerModel';
import { FindContainerById, MakeIterator } from '../../../utils/itertools';
import { FindContainerById, MakeDFSIterator } from '../../../utils/itertools';
import { GetCurrentHistory } from '../Editor';
import { ApplyBehaviors, ApplyBehaviorsOnSiblings, ApplyBehaviorsOnSiblingsChildren } from '../Behaviors/Behaviors';
import { ISymbolModel } from '../../../Interfaces/ISymbolModel';
import Swal from 'sweetalert2';
import { PropertyType } from '../../../Enums/PropertyType';
import { TransformX } from '../../../utils/svg';
import { TransformX, TransformY } from '../../../utils/svg';
import { Orientation } from '../../../Interfaces/Orientation';
/**
* Select a container
@ -133,7 +134,7 @@ function GetSelectedContainerOnDelete(
* @param container Container to unlink
*/
function UnlinkContainerFromSymbols(symbols: Map<string, ISymbolModel>, container: IContainerModel): void {
const it = MakeIterator(container);
const it = MakeDFSIterator(container);
for (const child of it) {
const symbol = symbols.get(child.properties.linkedSymbolId);
if (symbol === undefined) {
@ -194,11 +195,34 @@ export function SortChildren(parentClone: IContainerModel | null | undefined): v
if (parentClone === null || parentClone === undefined) {
return;
}
const isHorizontal = parentClone.properties.orientation === Orientation.Horizontal;
const children = parentClone.children;
if (!isHorizontal) {
parentClone.children.sort(
(a, b) => {
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) {
return -1;
}
if (yB < yA) {
return 1;
}
// xA = xB
const indexA = children.indexOf(a);
const indexB = children.indexOf(b);
return indexA - indexB;
}
);
return;
}
parentClone.children.sort(
(a, b) => {
const xA = TransformX(a.properties.x, a.properties.width, a.properties.xPositionReference);
const xB = TransformX(b.properties.x, b.properties.width, b.properties.xPositionReference);
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) {
return -1;
}

View file

@ -14,6 +14,7 @@
*/
import { IContainerModel } from '../../../Interfaces/IContainerModel';
import { Orientation } from '../../../Interfaces/Orientation';
import { ConstraintBodyInsideUnallocatedWidth } from './RigidBodyBehaviors';
/**
@ -21,17 +22,16 @@ import { ConstraintBodyInsideUnallocatedWidth } from './RigidBodyBehaviors';
* Apply the following modification to the overlapping rigid body container :
* @param container Container to impose its position
*/
export function ApplyAnchor(container: IContainerModel): IContainerModel {
if (container.parent === undefined ||
container.parent === null) {
return container;
}
const rigidBodies = container.parent.children.filter(
export function ApplyAnchor(container: IContainerModel, parent: IContainerModel): IContainerModel {
const rigidBodies = parent.children.filter(
child => !child.properties.isAnchor
);
const overlappingContainers = GetOverlappingContainers(container, rigidBodies);
const isHorizontal = parent.properties.orientation === Orientation.Horizontal;
const overlappingContainers = isHorizontal
? GetHorizontallyOverlappingContainers(container, rigidBodies)
: GetVerticallyOverlappingContainers(container, rigidBodies);
for (const overlappingContainer of overlappingContainers) {
ConstraintBodyInsideUnallocatedWidth(overlappingContainer);
}
@ -44,7 +44,7 @@ export function ApplyAnchor(container: IContainerModel): IContainerModel {
* @param containers A list of containers
* @returns A list of overlapping containers
*/
export function GetOverlappingContainers(
export function GetHorizontallyOverlappingContainers(
container: IContainerModel,
containers: IContainerModel[]
): IContainerModel[] {
@ -68,3 +68,63 @@ export function GetOverlappingContainers(
}
return overlappingContainers;
}
/**
* Returns the overlapping containers with container
* @param container A container
* @param containers A list of containers
* @returns A list of overlapping containers
*/
export function GetVerticallyOverlappingContainers(
container: IContainerModel,
containers: IContainerModel[]
): IContainerModel[] {
const min1 = container.properties.y;
const max1 = container.properties.y + container.properties.height;
const overlappingContainers: IContainerModel[] = [];
for (const other of containers) {
if (other === container) {
continue;
}
const min2 = other.properties.y;
const max2 = other.properties.y + other.properties.height;
const isOverlapping = Math.min(max1, max2) - Math.max(min1, min2) > 0;
if (!isOverlapping) {
continue;
}
overlappingContainers.push(other);
}
return overlappingContainers;
}
export function GetOverlappingContainers(
container: IContainerModel,
containers: IContainerModel[]
): IContainerModel[] {
const overlappingContainers: IContainerModel[] = [];
for (const other of containers) {
if (other === container) {
continue;
}
DoOverlap(container, other) && overlappingContainers.push(other);
}
return overlappingContainers;
}
function DoOverlap(container: IContainerModel, other: IContainerModel): boolean {
if (container.properties.x >= other.properties.x + other.properties.width ||
other.properties.x >= container.properties.x + container.properties.width) {
return false;
}
if (container.properties.y >= other.properties.y + other.properties.height ||
other.properties.y >= container.properties.y + container.properties.height) {
return false;
}
return true;
}

View file

@ -20,18 +20,22 @@ export function ApplyBehaviors(container: IContainerModel, symbols: Map<string,
ApplySymbol(container, symbol);
}
if (container.properties.isAnchor) {
ApplyAnchor(container);
}
if (container.parent !== undefined && container.parent !== null) {
const parent = container.parent;
if (ENABLE_SWAP) {
ApplySwap(container);
}
if (container.properties.isAnchor) {
ApplyAnchor(container, parent);
}
Flex(container);
if (ENABLE_SWAP) {
ApplySwap(container, parent);
}
if (ENABLE_RIGID) {
ApplyRigidBody(container);
Flex(container, parent);
if (ENABLE_RIGID) {
ApplyRigidBody(container, parent);
}
}
if (APPLY_BEHAVIORS_ON_CHILDREN) {
@ -68,13 +72,9 @@ export function ApplyBehaviorsOnSiblingsChildren(newContainer: IContainerModel,
newContainer.parent.children
.forEach((container: IContainerModel) => {
if (container.parent != null) {
const overlappingContainers = GetOverlappingContainers(container, container.parent.children);
if (overlappingContainers.length > 0) {
container.properties.warning = `There are overlapping containers: ${overlappingContainers.map(c => c.properties.id).join(' ')}`;
} else {
container.properties.warning = '';
}
UpdateWarning(container, container.parent);
}
if (container === newContainer) {
return;
}
@ -85,7 +85,6 @@ export function ApplyBehaviorsOnSiblingsChildren(newContainer: IContainerModel,
});
}
/**
* Iterate over the siblings of newContainer and apply the behaviors
* @param newContainer
@ -102,13 +101,9 @@ export function ApplyBehaviorsOnSiblings(newContainer: IContainerModel, symbols:
ApplyBehaviors(container, symbols);
if (container.parent != null) {
const overlappingContainers = GetOverlappingContainers(container, container.parent.children);
if (overlappingContainers.length > 0) {
container.properties.warning = `There are overlapping containers: ${overlappingContainers.map(c => c.properties.id).join(' ')}`;
} else {
container.properties.warning = '';
}
UpdateWarning(container, container.parent);
}
if (container === newContainer) {
return;
}
@ -118,4 +113,11 @@ export function ApplyBehaviorsOnSiblings(newContainer: IContainerModel, symbols:
}
});
}
function UpdateWarning(container: IContainerModel, parent: IContainerModel): void {
const overlappingContainers = GetOverlappingContainers(container, parent.children);
if (overlappingContainers.length > 0) {
container.properties.warning = `There are overlapping containers: ${overlappingContainers.map(c => c.properties.id).join(' ')}`;
} else {
container.properties.warning = '';
}
}

View file

@ -1,4 +1,5 @@
import { IContainerModel } from '../../../Interfaces/IContainerModel';
import { Orientation } from '../../../Interfaces/Orientation';
import { Simplex } from '../../../utils/simplex';
import { ApplyWidthMargin, ApplyXMargin } from '../../../utils/svg';
@ -10,27 +11,111 @@ interface IFlexibleGroup {
/**
* Flex the container and its siblings (mutate)
* @param container Container to flex
* @returns Flexed container
*/
export function Flex(container: IContainerModel): void {
if (container.parent === null || container.parent === undefined) {
export function Flex(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);
for (const flexibleGroup of flexibleGroups) {
FlexGroupVertically(flexibleGroup);
}
return;
}
const flexibleGroups = GetFlexibleGroups(container.parent);
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);
for (const flexibleGroup of flexibleGroups) {
FlexGroup(flexibleGroup);
FlexGroupHorizontally(flexibleGroup);
}
}
/**
* Returns a list of groups of flexible containers
* @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[] {
const flexibleGroups: IFlexibleGroup[] = [];
let group: IContainerModel[] = [];
let offset = 0;
let size = 0;
for (const child of parent.children) {
if (child.properties.isAnchor) {
size = child.properties.x - offset;
const flexibleGroup: IFlexibleGroup = {
group,
offset,
size
};
flexibleGroups.push(flexibleGroup);
offset = child.properties.x + child.properties.width;
group = [];
continue;
}
group.push(child);
}
size = parent.properties.width - offset;
const flexibleGroup: IFlexibleGroup = {
group,
offset,
size
};
flexibleGroups.push(flexibleGroup);
return flexibleGroups;
}
/**
* Returns a list of groups of flexible containers
* @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[] {
const flexibleGroups: IFlexibleGroup[] = [];
let group: IContainerModel[] = [];
let offset = 0;
let size = 0;
for (const child of parent.children) {
if (child.properties.isAnchor) {
size = child.properties.y - offset;
const flexibleGroup: IFlexibleGroup = {
group,
offset,
size
};
flexibleGroups.push(flexibleGroup);
offset = child.properties.y + child.properties.height;
group = [];
continue;
}
group.push(child);
}
size = parent.properties.height - offset;
const flexibleGroup: IFlexibleGroup = {
group,
offset,
size
};
flexibleGroups.push(flexibleGroup);
return flexibleGroups;
}
/**
* Apply flex to the group
* @param flexibleGroup Group that contains a list of flexible containers
* @returns
*/
function FlexGroup(flexibleGroup: IFlexibleGroup): void {
function FlexGroupHorizontally(flexibleGroup: IFlexibleGroup): void {
const children = flexibleGroup.group;
const {
flexibleContainers,
@ -96,6 +181,77 @@ function FlexGroup(flexibleGroup: IFlexibleGroup): void {
}
}
/**
* Apply flex to the group
* @param flexibleGroup Group that contains a list of flexible containers
* @returns
*/
function FlexGroupVertically(flexibleGroup: IFlexibleGroup): void {
const children = flexibleGroup.group;
const {
flexibleContainers,
nonFlexibleContainers
} = SeparateFlexibleContainers(children);
if (flexibleContainers.length === 0) {
return;
}
const minHeights = flexibleContainers
.map(sibling => sibling.properties.minHeight);
const fixedHeight = nonFlexibleContainers
.map(sibling => sibling.properties.height)
.reduce((heightSum, a) => heightSum + a, 0);
const requiredMaxHeight = flexibleGroup.size - fixedHeight;
const minimumPossibleHeight = minHeights.reduce((heightSum, a) => heightSum + a, 0); // sum(minHeights)
const checkSumMinHeightsIsFitting = minimumPossibleHeight > requiredMaxHeight;
if (checkSumMinHeightsIsFitting) {
console.warn('[FlexBehavior] Cannot fit at all even when squeezing all flex containers to the minimum.');
return;
}
const maxMinHeights = Math.max(...minHeights);
if (maxMinHeights * minHeights.length <= requiredMaxHeight) {
const wantedHeight = requiredMaxHeight / minHeights.length;
// it fits, flex with maxMinHeights and fixed height
let right = flexibleGroup.offset;
for (const sibling of children) {
if (!sibling.properties.isFlex) {
sibling.properties.y = right;
right += sibling.properties.height;
continue;
}
sibling.properties.y = ApplyXMargin(right, sibling.properties.margin.top);
sibling.properties.height = ApplyWidthMargin(wantedHeight, sibling.properties.margin.top, sibling.properties.margin.bottom);
right += wantedHeight;
}
return;
}
// does not fit
/// SIMPLEX ///
const maxHeights = flexibleContainers
.map(sibling => sibling.properties.maxHeight);
const solutions: number[] = Simplex(minHeights, maxHeights, requiredMaxHeight);
// apply the solutions
for (let i = 0; i < flexibleContainers.length; i++) {
flexibleContainers[i].properties.height = ApplyWidthMargin(solutions[i], flexibleContainers[i].properties.margin.top, flexibleContainers[i].properties.margin.bottom);
}
// move the containers
let right = flexibleGroup.offset;
for (const sibling of children) {
sibling.properties.y = ApplyXMargin(right, sibling.properties.margin.top);
right += sibling.properties.height;
}
}
function SeparateFlexibleContainers(
containers: IContainerModel[]
): { flexibleContainers: IContainerModel[], nonFlexibleContainers: IContainerModel[] } {
@ -114,41 +270,3 @@ function SeparateFlexibleContainers(
nonFlexibleContainers
};
}
/**
* Returns a list of groups of flexible containers
* @param parent Parent in which the flexible children will be set in groups
* @returns a list of groups of flexible containers
*/
export function GetFlexibleGroups(parent: IContainerModel): IFlexibleGroup[] {
const flexibleGroups: IFlexibleGroup[] = [];
let group: IContainerModel[] = [];
let offset = 0;
let size = 0;
for (const child of parent.children) {
if (child.properties.isAnchor) {
size = child.properties.x - offset;
const flexibleGroup: IFlexibleGroup = {
group,
offset,
size
};
flexibleGroups.push(flexibleGroup);
offset = child.properties.x + child.properties.width;
group = [];
continue;
}
group.push(child);
}
size = parent.properties.width - offset;
const flexibleGroup: IFlexibleGroup = {
group,
offset,
size
};
flexibleGroups.push(flexibleGroup);
return flexibleGroups;
}

View file

@ -1,4 +1,5 @@
import { IContainerModel } from '../../../Interfaces/IContainerModel';
import { Orientation } from '../../../Interfaces/Orientation';
import { ReversePairwise } from '../../../utils/itertools';
import { Flex } from './FlexBehaviors';
@ -7,17 +8,27 @@ import { Flex } from './FlexBehaviors';
* @param container
* @returns
*/
export function PushContainers(container: IContainerModel): IContainerModel {
if (container.parent === null) {
export function ApplyPush(container: IContainerModel, parent: IContainerModel): IContainerModel {
if (parent.children.length <= 1) {
return container;
}
if (container.parent.children.length <= 1) {
return container;
const children = parent.children;
const isHorizontal = parent.properties.orientation === Orientation.Horizontal;
if (isHorizontal) {
PushContainersHorizontally(container, children);
} else {
PushContainersVertically(container, children);
}
const prevIndex = container.parent.children.length - 2;
const prev: IContainerModel = container.parent.children[prevIndex];
Flex(container, parent);
return container;
}
export function PushContainersHorizontally(container: IContainerModel, children: IContainerModel[]): IContainerModel {
const prevIndex = children.length - 2;
const prev: IContainerModel = children[prevIndex];
const isOverlapping = prev.properties.x + prev.properties.width > container.properties.x;
if (!isOverlapping) {
return container;
@ -32,7 +43,7 @@ export function PushContainers(container: IContainerModel): IContainerModel {
// FIXME: A fix was applied using toFixed(2).
// FIXME: A coverture check must be done to ensure that all scenarios are covered
const it = ReversePairwise<IContainerModel>(container.parent.children.filter(child => child !== container));
const it = ReversePairwise<IContainerModel>(children.filter(child => child !== container));
for (const { cur, next } of it) {
const hasSpaceBetween = next.properties.x + next.properties.width < cur.properties.x;
@ -48,9 +59,9 @@ export function PushContainers(container: IContainerModel): IContainerModel {
break;
}
const indexLastContainer = container.parent.children.indexOf(lastContainer);
for (let i = indexLastContainer; i <= container.parent.children.length - 2; i++) {
const sibling = container.parent.children[i];
const indexLastContainer = children.indexOf(lastContainer);
for (let i = indexLastContainer; i <= children.length - 2; i++) {
const sibling = children[i];
sibling.properties.x -= space;
}
}
@ -58,16 +69,71 @@ export function PushContainers(container: IContainerModel): IContainerModel {
const hasNoSpaceBetween = lastContainer === null;
if (hasNoSpaceBetween) {
// test gap between the left of the parent and the first container
space = container.parent.children[0].properties.x;
space = children[0].properties.x;
if (space > 0) {
for (let i = 0; i <= container.parent.children.length - 2; i++) {
const sibling = container.parent.children[i];
for (let i = 0; i <= children.length - 2; i++) {
const sibling = children[i];
sibling.properties.x -= space;
}
return container;
}
}
Flex(container);
return container;
}
export function PushContainersVertically(container: IContainerModel, children: IContainerModel[]): IContainerModel {
const prevIndex = children.length - 2;
const prev: IContainerModel = children[prevIndex];
const isOverlapping = prev.properties.y + prev.properties.height > container.properties.y;
if (!isOverlapping) {
return container;
}
// find hole
let lastContainer: IContainerModel | null = null;
let space: number = 0;
while (space.toFixed(2) < container.properties.height.toFixed(2)) {
// FIXME: possible infinite loop due to floating point
// FIXME: A fix was applied using toFixed(2).
// FIXME: A coverture check must be done to ensure that all scenarios are covered
const it = ReversePairwise<IContainerModel>(children.filter(child => child !== container));
for (const { cur, next } of it) {
const hasSpaceBetween = next.properties.y + next.properties.height < cur.properties.y;
if (hasSpaceBetween) {
lastContainer = cur;
space = cur.properties.y - (next.properties.y + next.properties.height);
break;
}
}
if (lastContainer === null) {
// no space between
break;
}
const indexLastContainer = children.indexOf(lastContainer);
for (let i = indexLastContainer; i <= children.length - 2; i++) {
const sibling = children[i];
sibling.properties.y -= space;
}
}
const hasNoSpaceBetween = lastContainer === null;
if (hasNoSpaceBetween) {
// test gap between the left of the parent and the first container
space = children[0].properties.y;
if (space > 0) {
for (let i = 0; i <= children.length - 2; i++) {
const sibling = children[i];
sibling.properties.y -= space;
}
return container;
}
}
return container;
}

View file

@ -8,6 +8,7 @@
import { IContainerModel } from '../../../Interfaces/IContainerModel';
import { ISizePointer } from '../../../Interfaces/ISizePointer';
import { Orientation } from '../../../Interfaces/Orientation';
import { ENABLE_HARD_RIGID } from '../../../utils/default';
/**
@ -20,9 +21,10 @@ import { ENABLE_HARD_RIGID } from '../../../utils/default';
* @returns A rigid body container
*/
export function ApplyRigidBody(
container: IContainerModel
container: IContainerModel,
parent: IContainerModel
): IContainerModel {
container = ConstraintBodyInsideParent(container);
container = ConstraintBodyInsideParent(container, parent);
if (ENABLE_HARD_RIGID) {
container = ConstraintBodyInsideUnallocatedWidth(container);
@ -40,13 +42,10 @@ export function ApplyRigidBody(
* @returns Updated container
*/
function ConstraintBodyInsideParent(
container: IContainerModel
container: IContainerModel,
parent: IContainerModel
): IContainerModel {
if (container.parent === null || container.parent === undefined) {
return container;
}
const parentProperties = container.parent.properties;
const parentProperties = parent.properties;
const parentWidth = parentProperties.width;
const parentHeight = parentProperties.height;
@ -125,9 +124,15 @@ export function ConstraintBodyInsideUnallocatedWidth(
}
// Get the available spaces of the parent
const availableWidths = GetAvailableWidths(container.parent, container);
const containerX = container.properties.x;
const containerWidth = container.properties.width;
const isHorizontal =
container.parent.properties.orientation === Orientation.Horizontal;
const availableWidths = GetAvailableWidths(
0,
container.parent.properties.width,
container.parent.children,
container,
isHorizontal
);
// Check if there is still some space
if (availableWidths.length === 0) {
@ -136,6 +141,103 @@ export function ConstraintBodyInsideUnallocatedWidth(
);
}
const containerId = container.properties.id;
if (!isHorizontal) {
const containerY = container.properties.y;
const containerHeight = container.properties.height;
const containerMinHeight = container.properties.minHeight;
SortAvailableWidthsByClosest(containerY, containerHeight, availableWidths);
// Check if the container actually fit inside
// It will usually fit if it was alrady fitting
const availableWidthFound = availableWidths.find((width) =>
IsFitting(containerHeight, width)
);
if (availableWidthFound === undefined) {
const { x, width } = TrySqueeze(containerY, containerHeight, containerMinHeight, containerId, availableWidths);
container.properties.y = x;
container.properties.height = width;
return container;
}
ConstraintBodyInsideSpace(
container,
0,
availableWidthFound.x,
container.parent.properties.width,
availableWidthFound.width
);
return container;
}
const containerX = container.properties.x;
const containerWidth = container.properties.width;
const containerMinWidth = container.properties.minWidth;
SortAvailableWidthsByClosest(containerX, containerWidth, availableWidths);
// Check if the container actually fit inside
// It will usually fit if it was alrady fitting
const availableWidthFound = availableWidths.find((width) =>
IsFitting(containerWidth, width)
);
if (availableWidthFound === undefined) {
const { x, width } = TrySqueeze(containerX, containerWidth, containerMinWidth, containerId, availableWidths);
container.properties.x = x;
container.properties.width = width;
return container;
}
ConstraintBodyInsideSpace(
container,
availableWidthFound.x,
0,
availableWidthFound.width,
container.parent.properties.height
);
return container;
}
function TrySqueeze(
containerX: number,
containerWidth: number,
containerMinWidth: number,
containerId: string,
availableWidths: ISizePointer[]
): { x: number, width: number } {
// Otherwise, it is possible that it does not fit
// There is two way to reach this part of the code
// 1) Enable isRigidBody such as width > availableWidth.width
// 2) Resize a container such as width > availableWidth.width
// We want the container to fit automatically inside the available space
// even if it means to resize the container
const availableWidth: ISizePointer | undefined = availableWidths.find((width) => {
return IsFitting(containerMinWidth, width);
});
if (availableWidth === undefined) {
console.debug(`Container ${containerId} cannot fit in any space due to its minimum width being to large.`);
return {
x: containerX,
width: containerWidth
};
}
return {
x: availableWidth.x,
width: availableWidth.width
};
}
function SortAvailableWidthsByClosest(containerX: number, containerWidth: number, availableWidths: ISizePointer[]): void {
const middle = containerX + containerWidth / 2;
// Sort the available width to find the space with the closest position
availableWidths.sort(
@ -153,42 +255,6 @@ export function ConstraintBodyInsideUnallocatedWidth(
return Math.abs(compared1X - middle) - Math.abs(compared2X - middle);
}
);
// Check if the container actually fit inside
// It will usually fit if it was alrady fitting
const availableWidthFound = availableWidths.find((width) =>
IsFitting(container.properties.width, width)
);
if (availableWidthFound === undefined) {
// Otherwise, it is possible that it does not fit
// There is two way to reach this part of the code
// 1) Enable isRigidBody such as width > availableWidth.width
// 2) Resize a container such as width > availableWidth.width
// We want the container to fit automatically inside the available space
// even if it means to resize the container
const availableWidth: ISizePointer | undefined = availableWidths.find((width) => {
return IsFitting(container.properties.minWidth, width);
});
if (availableWidth === undefined) {
console.debug(`Container ${container.properties.id} cannot fit in any space due to its minimum width being to large.`);
return container;
}
container.properties.x = availableWidth.x;
container.properties.width = availableWidth.width;
return container;
}
return ConstraintBodyInsideSpace(
container,
availableWidthFound.x,
0,
availableWidthFound.width,
container.parent.properties.height
);
}
/**
@ -212,16 +278,17 @@ function IsFitting(containerWidth: number,
* @returns {ISizePointer[]} Array of unallocated widths (x=position of the unallocated space, width=size of the allocated space)
*/
function GetAvailableWidths(
container: IContainerModel,
exception: IContainerModel
x: number,
width: number,
children: IContainerModel[],
exception: IContainerModel,
isHorizontal: boolean
): ISizePointer[] {
// Initialize the first size pointer
// which takes full width of the available space
const x = 0;
const width = container.properties.width;
let unallocatedSpaces: ISizePointer[] = [{ x, width }];
for (const child of container.children) {
for (const child of children) {
if (unallocatedSpaces.length < 1) {
return unallocatedSpaces;
}
@ -231,8 +298,8 @@ function GetAvailableWidths(
if (child === exception) {
continue;
}
const childX = child.properties.x;
const childWidth = child.properties.width;
const childX = isHorizontal ? child.properties.x : child.properties.y;
const childWidth = isHorizontal ? child.properties.width : child.properties.height;
// get the space of the child that is inside the parent
let newUnallocatedSpace: ISizePointer[] = [];

View file

@ -3,15 +3,23 @@
*/
import { IContainerModel } from '../../../Interfaces/IContainerModel';
import { GetOverlappingContainers } from './AnchorBehaviors';
import { Orientation } from '../../../Interfaces/Orientation';
import { GetHorizontallyOverlappingContainers, GetVerticallyOverlappingContainers } from './AnchorBehaviors';
export function ApplySwap(container: IContainerModel): void {
if (container.parent === null || container.parent === undefined) {
export function ApplySwap(container: IContainerModel, parent: IContainerModel): void {
const children = parent.children;
const isVertical = parent.properties.orientation === Orientation.Vertical;
if (isVertical) {
SwapVertically(container, children);
return;
}
const children = container.parent.children;
const overlappingContainers = GetOverlappingContainers(container, children);
SwapHorizontally(container, children);
}
export function SwapHorizontally(container: IContainerModel, children: IContainerModel[]): void {
const overlappingContainers = GetHorizontallyOverlappingContainers(container, children);
if (overlappingContainers.length > 1 || overlappingContainers.length === 0) {
return;
@ -29,3 +37,23 @@ export function ApplySwap(container: IContainerModel): void {
const indexOverlapping = children.indexOf(overlappingContainer);
[children[indexContainer], children[indexOverlapping]] = [children[indexOverlapping], children[indexContainer]];
}
export function SwapVertically(container: IContainerModel, children: IContainerModel[]): void {
const overlappingContainers = GetVerticallyOverlappingContainers(container, children);
if (overlappingContainers.length > 1 || overlappingContainers.length === 0) {
return;
}
const overlappingContainer = overlappingContainers.pop();
if (overlappingContainer === null || overlappingContainer === undefined) {
return;
}
// swap positions
[overlappingContainer.properties.y, container.properties.y] = [container.properties.y, overlappingContainer.properties.y];
const indexContainer = children.indexOf(container);
const indexOverlapping = children.indexOf(overlappingContainer);
[children[indexContainer], children[indexOverlapping]] = [children[indexOverlapping], children[indexContainer]];
}

View file

@ -5,7 +5,7 @@ import { RestoreX, TransformX } from '../../../utils/svg';
export function ApplySymbol(container: IContainerModel, symbol: ISymbolModel): IContainerModel {
container.properties.x = TransformX(symbol.x, symbol.width, symbol.config.XPositionReference);
container.properties.x = RestoreX(container.properties.x, container.properties.width, container.properties.xPositionReference);
container.properties.x = RestoreX(container.properties.x, container.properties.width, container.properties.positionReference);
const [x] = ApplyParentTransform(container.parent, container.properties.x, 0);
container.properties.x = x;
return container;