675 lines
18 KiB
TypeScript
675 lines
18 KiB
TypeScript
import * as React from 'react';
|
|
import { Orientation } from '../../../Enums/Orientation';
|
|
import { Position } from '../../../Enums/Position';
|
|
import {
|
|
DEFAULT_DIMENSION_SYMBOL_STYLE,
|
|
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 } from './Dimension';
|
|
import { type IContainerModel } from '../../../Interfaces/IContainerModel';
|
|
import { type ISymbolModel } from '../../../Interfaces/ISymbolModel';
|
|
|
|
interface IDimensionLayerProps {
|
|
containers: Map<string, IContainerModel>
|
|
symbols: Map<string, ISymbolModel>
|
|
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.Right:
|
|
case Position.Left:
|
|
verticalAction(dim, false, ...params);
|
|
break;
|
|
case Position.Up:
|
|
case Position.Down:
|
|
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,
|
|
AddVerticalSelfMarginsDimension,
|
|
[
|
|
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
|
|
]
|
|
);
|
|
}
|
|
}
|
|
|
|
// TODO: Implement DimensionManager
|
|
symbols.forEach((symbol) => {
|
|
if (symbol.showDimension) {
|
|
if (symbol.isVertical) {
|
|
AddVerticalSymbolDimension(symbol, dimensions, scale, 0);
|
|
} else {
|
|
AddHorizontalSymbolDimension(symbol, dimensions, scale, 0);
|
|
}
|
|
}
|
|
});
|
|
|
|
return dimensions;
|
|
}
|
|
|
|
function AddHorizontalSymbolDimension(symbol: ISymbolModel,
|
|
dimensions: React.ReactNode[],
|
|
scale: number,
|
|
depth: number
|
|
): void {
|
|
const width = TransformX(symbol.offset, symbol.width, symbol.config.PositionReference);
|
|
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();
|
|
dimensions.push(
|
|
<Dimension
|
|
key={id}
|
|
id={id}
|
|
xStart={0}
|
|
yStart={-offset}
|
|
xEnd={width}
|
|
yEnd={-offset}
|
|
text={text}
|
|
scale={scale}
|
|
style={DEFAULT_DIMENSION_SYMBOL_STYLE}/>
|
|
);
|
|
}
|
|
}
|
|
|
|
function AddVerticalSymbolDimension(symbol: ISymbolModel,
|
|
dimensions: React.ReactNode[],
|
|
scale: number,
|
|
depth: number
|
|
): void {
|
|
const height = TransformY(symbol.offset, symbol.height, symbol.config.PositionReference);
|
|
if (height != null && height > 0) {
|
|
const id = `dim-x-margin-left${symbol.height.toFixed(0)}-${symbol.id}`;
|
|
|
|
const offset = (DIMENSION_MARGIN * (depth + 1)) / scale;
|
|
const text = height
|
|
.toFixed(0)
|
|
.toString();
|
|
dimensions.push(
|
|
<Dimension
|
|
key={id}
|
|
id={id}
|
|
xStart={-offset}
|
|
yStart={height}
|
|
xEnd={-offset}
|
|
yEnd={0}
|
|
text={text}
|
|
scale={scale}
|
|
style={DEFAULT_DIMENSION_SYMBOL_STYLE}/>
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A layer containing all dimension
|
|
* @param props
|
|
* @returns
|
|
*/
|
|
export function DimensionLayer(props: IDimensionLayerProps): JSX.Element {
|
|
return (
|
|
<g>
|
|
{Dimensions(props)}
|
|
</g>
|
|
);
|
|
}
|
|
|
|
/// Dimensions Actions ///
|
|
|
|
function AddHorizontalChildrenDimension(
|
|
yDim: number,
|
|
containers: Map<string, IContainerModel>,
|
|
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(<Dimension
|
|
key={childrenId}
|
|
id={childrenId}
|
|
xStart={xChildrenStart + offset}
|
|
xEnd={xChildrenEnd + offset}
|
|
yStart={yDim}
|
|
yEnd={yDim}
|
|
text={textChildren}
|
|
scale={scale}
|
|
style={style}/>);
|
|
}
|
|
|
|
function AddVerticalChildrenDimension(
|
|
xDim: number,
|
|
isRight: boolean,
|
|
containers: Map<string, IContainerModel>,
|
|
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(<Dimension
|
|
key={childrenId}
|
|
id={childrenId}
|
|
xStart={xDim}
|
|
yStart={yChildrenStart + offset}
|
|
xEnd={xDim}
|
|
yEnd={yChildrenEnd + offset}
|
|
text={textChildren}
|
|
scale={scale}
|
|
style={style}
|
|
/>);
|
|
}
|
|
|
|
function AddHorizontalBorrowerDimension(
|
|
yDim: number,
|
|
containers: Map<string, IContainerModel>,
|
|
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(<Dimension
|
|
key={id}
|
|
id={id}
|
|
xStart={cur}
|
|
xEnd={next}
|
|
yStart={yDim}
|
|
yEnd={yDim}
|
|
text={value.toFixed(0)}
|
|
scale={scale}
|
|
style={style}/>);
|
|
count++;
|
|
}
|
|
}
|
|
|
|
function AddVerticalBorrowerDimension(
|
|
xDim: number,
|
|
isRight: boolean,
|
|
containers: Map<string, IContainerModel>,
|
|
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(<Dimension
|
|
key={id}
|
|
id={id}
|
|
xStart={xDim}
|
|
xEnd={xDim}
|
|
yStart={cur}
|
|
yEnd={next}
|
|
text={value.toFixed(0)}
|
|
scale={scale}
|
|
style={style}/>);
|
|
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(
|
|
<Dimension
|
|
key={idVert}
|
|
id={idVert}
|
|
xStart={xDim}
|
|
yStart={yStart}
|
|
xEnd={xDim}
|
|
yEnd={yEnd}
|
|
text={textVert}
|
|
scale={scale}
|
|
style={style}/>
|
|
);
|
|
}
|
|
|
|
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(
|
|
<Dimension
|
|
key={id}
|
|
id={id}
|
|
xStart={xStart}
|
|
yStart={yDim}
|
|
xEnd={xEnd}
|
|
yEnd={yDim}
|
|
text={text}
|
|
scale={scale}
|
|
style={style}/>
|
|
);
|
|
}
|
|
|
|
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 && left > 0) {
|
|
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(
|
|
<Dimension
|
|
key={id}
|
|
id={id}
|
|
xStart={xStart}
|
|
yStart={yDim}
|
|
xEnd={xEnd}
|
|
yEnd={yDim}
|
|
text={text}
|
|
scale={scale}
|
|
style={style}/>
|
|
);
|
|
}
|
|
|
|
const right = container.properties.margin.right;
|
|
if (right != null && right > 0) {
|
|
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(
|
|
<Dimension
|
|
key={id}
|
|
id={id}
|
|
xStart={xStart}
|
|
yStart={yDim}
|
|
xEnd={xEnd}
|
|
yEnd={yDim}
|
|
text={text}
|
|
scale={scale}
|
|
style={style}/>
|
|
);
|
|
}
|
|
}
|
|
|
|
function AddVerticalSelfMarginsDimension(
|
|
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 && top > 0) {
|
|
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(
|
|
<Dimension
|
|
key={idVert}
|
|
id={idVert}
|
|
xStart={xDim}
|
|
yStart={yStart}
|
|
xEnd={xDim}
|
|
yEnd={yEnd}
|
|
text={textVert}
|
|
scale={scale}
|
|
style={style}/>
|
|
);
|
|
}
|
|
const bottom = container.properties.margin.bottom;
|
|
if (bottom != null && bottom > 0) {
|
|
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(
|
|
<Dimension
|
|
key={idVert}
|
|
id={idVert}
|
|
xStart={xDim}
|
|
yStart={yStart}
|
|
xEnd={xDim}
|
|
yEnd={yEnd}
|
|
text={textVert}
|
|
scale={scale}
|
|
style={style}/>
|
|
);
|
|
}
|
|
}
|