Merged PR 226: Implement associated container and default X, Y position for Symbol

Implement associated container and default X, Y position for Symbol

Related work items: #7537, #7540
This commit is contained in:
Eric Nguyen 2022-11-07 08:59:25 +00:00
parent 0a664752e9
commit 4ff2e0b7fb
9 changed files with 136 additions and 48 deletions

View file

@ -7,13 +7,26 @@ namespace SVGLDLibs.Models
{
[DataMember(EmitDefaultValue = false)]
public string Name { get; set; }
[DataMember(EmitDefaultValue = false)]
public ImageModel Image { get; set; }
[DataMember(EmitDefaultValue = false)]
public PositionReferenceEnumModel PositionReference { get; set; }
public double X { get; set; }
[DataMember(EmitDefaultValue = false)]
public double Y { get; set; }
[DataMember(EmitDefaultValue = false)]
public double Width { get; set; }
[DataMember(EmitDefaultValue = false)]
public double Height { get; set; }
[DataMember(EmitDefaultValue = false)]
public PositionReferenceEnumModel PositionReference { get; set; }
[DataMember(EmitDefaultValue = false)]
public AvailableContainerModel AssociatedContainer { get; set; }
}
}

View file

@ -8,16 +8,22 @@ namespace SVGLDLibs.Models
{
[DataMember(EmitDefaultValue = false)]
public string id { get; set; }
[DataMember(EmitDefaultValue = false)]
public string type { get; set; }
[DataMember(EmitDefaultValue = false)]
public AvailableSymbolModel config { get; set; }
[DataMember(EmitDefaultValue = false)]
public double x { get; set; }
[DataMember(EmitDefaultValue = false)]
public double width { get; set; }
[DataMember(EmitDefaultValue = false)]
public double height { get; set; }
[DataMember(EmitDefaultValue = false)]
public List<string> linkedContainers { get; set; }
}

View file

@ -12,7 +12,7 @@ import { ISymbolModel } from '../../../Interfaces/ISymbolModel';
import { Orientation } from '../../../Enums/Orientation';
import { GetDefaultContainerProps } from '../../../utils/default';
import { FindContainerById } from '../../../utils/itertools';
import { ApplyMargin, RestoreX, RestoreY, TransformX } from '../../../utils/svg';
import { ApplyMargin, RestoreX, RestoreY } from '../../../utils/svg';
import { ApplyBehaviors, ApplyBehaviorsOnSiblingsChildren } from '../Behaviors/Behaviors';
import { GetCurrentHistory, UpdateCounters } from '../Editor';
import { SortChildren } from './ContainerOperations';
@ -59,7 +59,10 @@ export function AddContainers(
configuration: IConfiguration,
fullHistory: IHistoryState[],
historyCurrentStep: number
): IHistoryState[] {
): {
history: IHistoryState[]
newContainers: IContainerModel[]
} {
const history = GetCurrentHistory(fullHistory, historyCurrentStep);
const current = history[history.length - 1];
@ -77,18 +80,27 @@ export function AddContainers(
// Deep clone the counters
const newCounters = Object.assign({}, current.typeCounters);
// containerIds is used for logging purpose (see setHistory below)
const containerIds: string[] = [];
// Iterate over the containers
const newContainers: IContainerModel[] = [];
availableContainers.forEach((availableContainer, typeIndex) => {
// Get the preset properties from the API
AddNewContainerToParent(availableContainer, configuration, containers, parentClone, index, typeIndex, newCounters, current.symbols, containerIds);
const newContainer = AddNewContainerToParent(
availableContainer,
configuration,
containers,
parentClone,
index,
typeIndex,
newCounters,
current.symbols
);
newContainers.push(newContainer);
});
// Update the state
const containersIds = newContainers.map(container => container.properties.id);
history.push({
lastAction: `Add [${containerIds.join(', ')}] in ${parentClone.properties.id}`,
lastAction: `Add [${containersIds.join(', ')}] in ${parentClone.properties.id}`,
mainContainer: current.mainContainer,
selectedContainerId: parentClone.properties.id,
containers,
@ -97,7 +109,10 @@ export function AddContainers(
selectedSymbolId: current.selectedSymbolId
});
return history;
return {
history,
newContainers
};
}
function AddNewContainerToParent(
@ -109,7 +124,6 @@ function AddNewContainerToParent(
typeIndex: number,
newCounters: Record<string, number>,
symbols: Map<string, ISymbolModel>,
containerIds: string[] = [],
initChilds: boolean = true
): ContainerModel {
const type = availableContainer.Type;
@ -207,9 +221,6 @@ function AddNewContainerToParent(
}
}
// Add to the list of container id for logging purpose
containerIds.push(newContainer.properties.id);
return newContainer;
}
@ -234,7 +245,7 @@ export function AddContainer(
historyCurrentStep: number
): IHistoryState[] {
// just call AddContainers with an array on a single element
return AddContainers(
const { history } = AddContainers(
index,
// eslint-disable-next-line @typescript-eslint/naming-convention
[{ Type: type }],
@ -243,6 +254,7 @@ export function AddContainer(
fullHistory,
historyCurrentStep
);
return history;
}
/**
@ -432,7 +444,6 @@ function AddContainerInLevel(
0, 0,
newCounters,
symbols,
undefined,
false
);

View file

@ -282,11 +282,12 @@ function SetContainer(
AssignProperty(container, key, value, type);
// link the symbol if it exists
const oldSymbol = symbols.get(oldSymbolId);
const newSymbol = symbols.get(container.properties.linkedSymbolId);
LinkSymbol(
container.properties.id,
oldSymbolId,
container.properties.linkedSymbolId,
symbols
oldSymbol,
newSymbol
);
// Apply special behaviors: rigid, flex, symbol, anchor
@ -357,15 +358,11 @@ function AssignProperty(container: ContainerModel, key: string, value: string |
* @param symbols Current list of symbols
* @returns
*/
function LinkSymbol(
export function LinkSymbol(
containerId: string,
oldSymbolId: string,
newSymbolId: string,
symbols: Map<string, ISymbolModel>
oldSymbol: ISymbolModel | undefined,
newSymbol: ISymbolModel | undefined
): void {
const oldSymbol = symbols.get(oldSymbolId);
const newSymbol = symbols.get(newSymbolId);
if (newSymbol === undefined) {
if (oldSymbol !== undefined) {
oldSymbol.linkedContainers.delete(containerId);

View file

@ -203,20 +203,21 @@ function HandleSetContainerList(
const containers = current.containers;
switch (addingBehavior) {
case AddMethod.Insert:
case AddMethod.Append:
case AddMethod.Append: {
response.Containers.forEach(config => {
config.AddMethod = config.AddMethod ?? addingBehavior;
});
setNewHistory(
AddContainers(
const { history: newHistory } = AddContainers(
selectedContainer.children.length,
response.Containers,
selectedContainer.properties.id,
configuration,
history,
historyCurrentStep
));
historyCurrentStep);
setNewHistory(newHistory);
break;
}
case AddMethod.Replace:
setNewHistory(
HandleReplace(
@ -275,7 +276,7 @@ function HandleReplace(
historyCurrentStep
);
const newHistoryBeforeDelete = AddContainers(
const { history: newHistoryBeforeDelete } = AddContainers(
index,
response.Containers,
selectedContainer.properties.parentId,
@ -318,7 +319,7 @@ function HandleInsert(
historyCurrentStep
);
const newHistoryBeforeDelete = AddContainers(
const { history: newHistoryBeforeDelete } = AddContainers(
index,
response.Containers,
selectedContainer.properties.parentId,

View file

@ -6,7 +6,9 @@ import { GetDefaultSymbolModel } from '../../../utils/default';
import { FindContainerById } from '../../../utils/itertools';
import { RestoreX } from '../../../utils/svg';
import { ApplyBehaviors, ApplyBehaviorsOnSiblingsChildren } from '../Behaviors/Behaviors';
import { GetCurrentHistory, UpdateCounters } from '../Editor';
import { GetCurrentHistory, GetCurrentHistoryState, UpdateCounters } from '../Editor';
import { AddContainers } from './AddContainer';
import { LinkSymbol } from './ContainerOperations';
export function AddSymbol(
name: string,
@ -14,7 +16,7 @@ export function AddSymbol(
fullHistory: IHistoryState[],
historyCurrentStep: number
): IHistoryState[] {
const history = GetCurrentHistory(fullHistory, historyCurrentStep);
let history = GetCurrentHistory(fullHistory, historyCurrentStep);
const current = history[history.length - 1];
const symbolConfig = configuration.AvailableSymbols
@ -24,24 +26,48 @@ export function AddSymbol(
throw new Error('[AddSymbol] Symbol could not be found in the config');
}
const type = `symbol-${name}`;
const newCounters = structuredClone(current.typeCounters);
UpdateCounters(newCounters, type);
const typeCounters = structuredClone(current.typeCounters);
UpdateCounters(typeCounters, type);
const newSymbols = structuredClone(current.symbols);
const newSymbol: ISymbolModel = GetDefaultSymbolModel(name, newCounters, type, symbolConfig);
const newSymbol: ISymbolModel = GetDefaultSymbolModel(name, typeCounters, type, symbolConfig);
const containers = structuredClone(current.containers);
newSymbol.x = RestoreX(newSymbol.x, newSymbol.width, newSymbol.config.PositionReference);
newSymbols.set(newSymbol.id, newSymbol);
history.push({
lastAction: `Add ${name}`,
mainContainer: structuredClone(current.mainContainer),
containers: structuredClone(current.containers),
mainContainer: current.mainContainer,
containers,
selectedContainerId: current.selectedContainerId,
typeCounters: newCounters,
typeCounters,
symbols: newSymbols,
selectedSymbolId: newSymbol.id
});
if (symbolConfig.AssociatedContainer !== undefined) {
const {
history: newHistory,
newContainers
} = AddContainers(
0,
[symbolConfig.AssociatedContainer],
current.mainContainer,
configuration,
history,
historyCurrentStep + 1
);
history = newHistory;
const newCurrent = GetCurrentHistoryState(newHistory, historyCurrentStep + 2);
const newerSymbol = newCurrent.symbols.get(newSymbol.id);
newContainers.forEach((newContainer) => {
LinkContainer(newerSymbol, newContainer, newSymbols);
});
}
return history;
}
@ -55,7 +81,7 @@ export function SelectSymbol(
history.push({
lastAction: `Select ${symbolId}`,
mainContainer: structuredClone(current.mainContainer),
mainContainer: current.mainContainer,
containers: structuredClone(current.containers),
selectedContainerId: current.selectedContainerId,
typeCounters: structuredClone(current.typeCounters),
@ -166,3 +192,20 @@ export function OnPropertyChange(
});
return history;
}
/**
* Link a container to a symbol.
* If symbol is undefined, unlink the previous symbol of the container
* @param symbol
* @param container
* @param symbols
*/
function LinkContainer(
symbol: ISymbolModel | undefined,
container: IContainerModel,
symbols: Map<string, ISymbolModel>
): void {
const oldSymbol = symbols.get(container.properties.linkedSymbolId);
LinkSymbol(container.properties.id, oldSymbol, symbol);
container.properties.linkedSymbolId = symbol !== undefined ? symbol.id : '';
}

View file

@ -1,13 +1,25 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { PositionReference } from '../Enums/PositionReference';
import { IAvailableContainer } from './IAvailableContainer';
import { IImage } from './IImage';
/**
* Model of available symbol to configure the application */
export interface IAvailableSymbol {
Name: string
Image: IImage
X?: number
Y?: number
Width?: number
Height?: number
PositionReference?: PositionReference
/** An existing or new available container */
AssociatedContainer?: IAvailableContainer
}

View file

@ -13,6 +13,8 @@ export interface ISymbolModel {
/** Horizontal offset */
x: number
// TODO: Implement Y and verticality
/** Width */
width: number

View file

@ -283,7 +283,10 @@ const GetSVGLayoutConfiguration = () => {
Url: 'https://www.manutan.fr/img/S/GRP/ST/AIG3930272.jpg'
},
Name: 'Poteau structure',
PositionReference: 1
PositionReference: 1,
AssociatedContainer: {
Type: 'Montant'
}
},
{
Width: 32,