import * as React from 'react'; import { Orientation } from '../../../Enums/Orientation'; import { Position } from '../../../Enums/Position'; import { DIMENSION_MARGIN, SHOW_BORROWER_DIMENSIONS, SHOW_CHILDREN_DIMENSIONS, SHOW_SELF_DIMENSIONS, SHOW_SELF_MARGINS_DIMENSIONS } from '../../../utils/default'; import { FindContainerById, MakeRecursionDFSIterator, Pairwise } from '../../../utils/itertools'; import { TransformX, TransformY } from '../../../utils/svg'; import { Dimension, type IDimensionStyle } from './Dimension'; import { type IContainerModel } from '../../../Interfaces/IContainerModel'; import { type ISymbolModel } from '../../../Interfaces/ISymbolModel'; interface IDimensionLayerProps { containers: Map symbols: Map root: IContainerModel scale: number } /** * Fonction that call another function given the positions * @param dimMapped Position mapped depending on the Position enum in order: * [0:left, 1:bottom, 2:up, 3:right] * @param positions List of positions * @param horizontalAction Action called when a left or right position is present * @param verticalAction Action called when a down or up position is present * @param params Params for the actions * (the two actions must have the same number of params, and in the same order) */ function ActionByPosition( dimMapped: number[], positions: Position[], horizontalAction: (dim: number, ...params: any[]) => void, verticalAction: (dim: number, isRight: boolean, ...params: any[]) => void, params: any[] ): void { positions.forEach((position: Position) => { const dim = dimMapped[position]; switch (position) { case Position.Left: case Position.Right: { const isRight = position === Position.Right; verticalAction(dim, isRight, ...params); break; } case Position.Down: case Position.Up: horizontalAction(dim, ...params); break; } }); } /** * Returns a list of dimensions of all containers in root * @param param0 Object with the root container and the scale of the svg * @returns A list of dimensions */ function Dimensions({ containers, symbols, root, scale }: IDimensionLayerProps): React.ReactNode[] { const it = MakeRecursionDFSIterator(root, containers, 0, [0, 0]); const dimensions: React.ReactNode[] = []; const topDim = root.properties.y; const leftDim = root.properties.x; const rightDim = root.properties.x + root.properties.width; const bottomDim = root.properties.y + root.properties.height; if (!SHOW_SELF_DIMENSIONS) { return []; } for (const { container, depth, currentTransform } of it) { const offset = (DIMENSION_MARGIN * (depth + 1)) / scale; const containerLeftDim = leftDim - offset; const containerTopDim = topDim - offset; const containerBottomDim = bottomDim + offset; const containerRightDim = rightDim + offset; const dimMapped = [containerLeftDim, containerBottomDim, containerTopDim, containerRightDim]; if (SHOW_SELF_DIMENSIONS && container.properties.dimensionOptions.selfDimensions.positions.length > 0) { ActionByPosition( dimMapped, container.properties.dimensionOptions.selfDimensions.positions, AddHorizontalSelfDimension, AddVerticalSelfDimension, [ container, currentTransform, dimensions, scale ] ); } if (SHOW_SELF_MARGINS_DIMENSIONS && container.properties.dimensionOptions.selfMarginsDimensions.positions.length > 0) { ActionByPosition( dimMapped, container.properties.dimensionOptions.selfMarginsDimensions.positions, AddHorizontalSelfMarginsDimension, AddVerticalSelfMarginDimension, [ container, currentTransform, dimensions, scale ] ); } if (SHOW_BORROWER_DIMENSIONS && container.properties.dimensionOptions.dimensionWithMarks.positions.length > 0) { ActionByPosition( dimMapped, container.properties.dimensionOptions.dimensionWithMarks.positions, AddHorizontalBorrowerDimension, AddVerticalBorrowerDimension, [ containers, container, depth, currentTransform, dimensions, scale ] ); } if (SHOW_CHILDREN_DIMENSIONS && container.properties.dimensionOptions.childrenDimensions.positions.length > 0 && container.children.length >= 2) { ActionByPosition( dimMapped, container.properties.dimensionOptions.childrenDimensions.positions, AddHorizontalChildrenDimension, AddVerticalChildrenDimension, [ containers, container, currentTransform, dimensions, scale ] ); } } let startDepthSymbols: number = 0; for (const symbol of symbols) { if (symbol[1].showDimension) { startDepthSymbols++; AddHorizontalSymbolDimension( symbol[1], dimensions, scale, startDepthSymbols ); } } return dimensions; } function AddHorizontalSymbolDimension( symbol: ISymbolModel, dimensions: React.ReactNode[], scale: number, depth: number ): void { const width = symbol.x + (symbol.width / 2); if (width != null && width > 0) { const id = `dim-y-margin-left${symbol.width.toFixed(0)}-${symbol.id}`; const offset = (DIMENSION_MARGIN * (depth + 1)) / scale; const text = width .toFixed(0) .toString(); // TODO: Put this in default.ts const defaultDimensionSymbolStyle: IDimensionStyle = { color: 'black' }; dimensions.push( ); } } /** * A layer containing all dimension * @param props * @returns */ export function DimensionLayer(props: IDimensionLayerProps): JSX.Element { return ( {Dimensions(props)} ); } /// Dimensions Actions /// function AddHorizontalChildrenDimension( yDim: number, containers: Map, container: IContainerModel, currentTransform: [number, number], dimensions: React.ReactNode[], scale: number ): void { const childrenId = `dim-y${yDim.toFixed(0)}-children-${container.properties.id}`; const style = container.properties.dimensionOptions.childrenDimensions; const lastChildId = container.children[container.children.length - 1]; const lastChild = FindContainerById(containers, lastChildId); if (lastChild === undefined) { return; } let xChildrenStart = TransformX(lastChild.properties.x, lastChild.properties.width, lastChild.properties.positionReference); let xChildrenEnd = TransformX(lastChild.properties.x, lastChild.properties.width, lastChild.properties.positionReference); // Find the min and max for (let i = container.children.length - 2; i >= 0; i--) { const childId = container.children[i]; const child = FindContainerById(containers, childId); if (child === undefined) { continue; } const left = TransformX(child.properties.x, child.properties.width, child.properties.positionReference); if (left < xChildrenStart) { xChildrenStart = left; } const right = TransformX(child.properties.x, child.properties.width, child.properties.positionReference); if (right > xChildrenEnd) { xChildrenEnd = right; } } if (xChildrenStart === xChildrenEnd) { // do not show an empty dimension return; } const textChildren = (xChildrenEnd - xChildrenStart) .toFixed(0) .toString(); const offset = currentTransform[0] + container.properties.x; dimensions.push(); } function AddVerticalChildrenDimension( xDim: number, isRight: boolean, containers: Map, container: IContainerModel, currentTransform: [number, number], dimensions: React.ReactNode[], scale: number ): void { const childrenId = `dim-x${xDim.toFixed(0)}-children-${container.properties.id}`; const style = container.properties.dimensionOptions.childrenDimensions; const lastChildId = container.children[container.children.length - 1]; const lastChild = FindContainerById(containers, lastChildId); if (lastChild === undefined) { return; } let yChildrenStart = TransformY(lastChild.properties.y, lastChild.properties.height, lastChild.properties.positionReference); let yChildrenEnd = TransformY(lastChild.properties.y, lastChild.properties.height, lastChild.properties.positionReference); // Find the min and max for (let i = container.children.length - 2; i >= 0; i--) { const childId = container.children[i]; const child = FindContainerById(containers, childId); if (child === undefined) { continue; } const top = TransformY(child.properties.y, child.properties.height, child.properties.positionReference); if (top < yChildrenStart) { yChildrenStart = top; } const bottom = TransformY(child.properties.y, child.properties.height, child.properties.positionReference); if (bottom > yChildrenEnd) { yChildrenEnd = bottom; } } if (yChildrenStart === yChildrenEnd) { // do not show an empty dimension return; } const textChildren = (yChildrenEnd - yChildrenStart) .toFixed(0) .toString(); const offset = currentTransform[0] + container.properties.x; if (!isRight) { [yChildrenStart, yChildrenEnd] = [yChildrenEnd, yChildrenStart]; } dimensions.push(); } function AddHorizontalBorrowerDimension( yDim: number, containers: Map, container: IContainerModel, depth: number, currentTransform: [number, number], dimensions: React.ReactNode[], scale: number ): void { const style = container.properties.dimensionOptions.dimensionWithMarks; const it = MakeRecursionDFSIterator(container, containers, depth, currentTransform); const marks = []; // list of vertical lines for the dimension for (const { container: childContainer, currentTransform: childCurrentTransform } of it) { const isHidden = !childContainer.properties.dimensionOptions.markPosition.includes(Orientation.Horizontal); if (isHidden) { continue; } const x = TransformX( childContainer.properties.x, childContainer.properties.width, childContainer.properties.positionReference ); const restoredX = x + childCurrentTransform[0]; marks.push( restoredX ); } const restoredX = container.properties.x + currentTransform[0]; marks.push(restoredX); marks.push(restoredX + container.properties.width); marks.sort((a, b) => a - b); let count = 0; for (const { cur, next } of Pairwise(marks)) { const id = `dim-y${yDim.toFixed(0)}-borrow-${container.properties.id}-{${count}}`; const value = next - cur; if (value === 0) { return; } dimensions.push(); count++; } } function AddVerticalBorrowerDimension( xDim: number, isRight: boolean, containers: Map, container: IContainerModel, depth: number, currentTransform: [number, number], dimensions: React.ReactNode[], scale: number ): void { const style = container.properties.dimensionOptions.dimensionWithMarks; const it = MakeRecursionDFSIterator(container, containers, depth, currentTransform); const marks = []; // list of vertical lines for the dimension for (const { container: childContainer, currentTransform: childCurrentTransform } of it) { const isHidden = !childContainer.properties.dimensionOptions.markPosition.includes(Orientation.Vertical); if (isHidden) { continue; } const y = TransformY( childContainer.properties.y, childContainer.properties.height, childContainer.properties.positionReference ); const restoredy = y + childCurrentTransform[1]; marks.push( restoredy ); } const restoredY = container.properties.y + currentTransform[1]; marks.push(restoredY); marks.push(restoredY + container.properties.height); marks.sort((a, b) => a - b); let count = 0; for (let { cur, next } of Pairwise(marks)) { const id = `dim-x${xDim.toFixed(0)}-borrow-${container.properties.id}-{${count}}`; const value = next - cur; if (value === 0) { return; } if (!isRight) { [cur, next] = [next, cur]; } dimensions.push(); count++; } } function AddVerticalSelfDimension( xDim: number, isRight: boolean, container: IContainerModel, currentTransform: [number, number], dimensions: React.ReactNode[], scale: number ): void { const style = container.properties.dimensionOptions.selfDimensions; const height = container.properties.height; const idVert = `dim-x${xDim.toFixed(0)}-${container.properties.id}`; let yStart = container.properties.y + currentTransform[1] + height; let yEnd = container.properties.y + currentTransform[1]; const textVert = height .toFixed(0) .toString(); if (isRight) { [yStart, yEnd] = [yEnd, yStart]; } dimensions.push( ); } function AddHorizontalSelfDimension( yDim: number, container: IContainerModel, currentTransform: [number, number], dimensions: React.ReactNode[], scale: number ): void { const style = container.properties.dimensionOptions.selfDimensions; const width = container.properties.width; const id = `dim-y${yDim.toFixed(0)}-${container.properties.id}`; const xStart = container.properties.x + currentTransform[0]; const xEnd = xStart + width; const text = width .toFixed(0) .toString(); dimensions.push( ); } function AddHorizontalSelfMarginsDimension( yDim: number, container: IContainerModel, currentTransform: [number, number], dimensions: React.ReactNode[], scale: number ): void { const style = container.properties.dimensionOptions.selfMarginsDimensions; const left = container.properties.margin.left; if (left != null) { const id = `dim-y-margin-left${yDim.toFixed(0)}-${container.properties.id}`; const xStart = container.properties.x + currentTransform[0] - left; const xEnd = xStart + left; const text = left .toFixed(0) .toString(); dimensions.push( ); } const right = container.properties.margin.right; if (right != null) { const id = `dim-y-margin-right${yDim.toFixed(0)}-${container.properties.id}`; const xStart = container.properties.x + container.properties.width + currentTransform[0]; const xEnd = xStart + right; const text = right .toFixed(0) .toString(); dimensions.push( ); } } function AddVerticalSelfMarginDimension( xDim: number, isRight: boolean, container: IContainerModel, currentTransform: [number, number], dimensions: React.ReactNode[], scale: number ): void { const style = container.properties.dimensionOptions.selfMarginsDimensions; const top = container.properties.margin.top; if (top != null) { const idVert = `dim-x-margin-top${xDim.toFixed(0)}-${container.properties.id}`; let yStart = container.properties.y + currentTransform[1]; let yEnd = yStart - top; const textVert = top .toFixed(0) .toString(); if (isRight) { [yStart, yEnd] = [yEnd, yStart]; } dimensions.push( ); } const bottom = container.properties.margin.bottom; if (bottom != null) { const idVert = `dim-x-margin-bottom${xDim.toFixed(0)}-${container.properties.id}`; let yStart = container.properties.y + container.properties.height + bottom + currentTransform[1]; let yEnd = yStart - bottom; const textVert = bottom .toFixed(0) .toString(); if (isRight) { [yStart, yEnd] = [yEnd, yStart]; } dimensions.push( ); } }