Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ They had been made public by mistake, and had been considered internal since the
So, remove it without replacement.
- `Client.VERSION` moved to `constants.VERSION`. VERSION is supposed to be immutable as it represents the actual version of maxGraph.
When it was stored in Client, it was a static property that could be modified. Moving it to the constants module ensures that it cannot be modified.
- `ManhattanConnector` is now configured with the global `ManhattanConnectorConfig` object.
The following properties that were previously on `EdgeStyle` have moved to `ManhattanConnectorConfig`:
- `MANHATTAN_END_DIRECTIONS` to `endDirections`
- `MANHATTAN_MAX_ALLOWED_DIRECTION_CHANGE` to `maxAllowedDirectionChange`
- `MANHATTAN_MAXIMUM_LOOPS` to `maxLoops`
- `MANHATTAN_START_DIRECTIONS` to `startDirections`
- `MANHATTAN_STEP` to `step`
- `OrthConnector` is now configured with the global `OrthConnectorConfig` object.
The following properties that were previously on `EdgeStyle` have moved to `OrthConnectorConfig`:
- `orthBuffer` to `buffer`
Expand Down
41 changes: 39 additions & 2 deletions packages/core/__tests__/view/style/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,14 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

import { expect, test } from '@jest/globals';
import { OrthConnectorConfig, resetOrthConnectorConfig } from '../../../src';
import { describe, expect, test } from '@jest/globals';
import {
ManhattanConnectorConfig,
OrthConnectorConfig,
resetManhattanConnectorConfig,
resetOrthConnectorConfig,
} from '../../../src';
import { DIRECTION } from '../../../src/util/Constants';

test('resetOrthConnectorConfig', () => {
// Keep track of original default values
Expand All @@ -32,3 +38,34 @@ test('resetOrthConnectorConfig', () => {
expect(OrthConnectorConfig.pointsFallback).toBeTruthy();
expect(OrthConnectorConfig).toStrictEqual(originalConfig);
});

describe('resetManhattanConnectorConfig', () => {
test('simple values', () => {
// Keep track of original default values
const originalConfig = { ...ManhattanConnectorConfig };

// Change some values
ManhattanConnectorConfig.maxLoops = 100;
ManhattanConnectorConfig.step = 60;

resetManhattanConnectorConfig();

// Ensure that the values have correctly been reset
expect(ManhattanConnectorConfig.maxLoops).toBe(2000);
expect(ManhattanConnectorConfig.step).toBe(12);
expect(ManhattanConnectorConfig).toStrictEqual(originalConfig);
});
test('original direction arrays are kept untouched', () => {
const originalEndDirections = [...ManhattanConnectorConfig.endDirections];
const originalStartDirections = [...ManhattanConnectorConfig.startDirections];

// Change some values
ManhattanConnectorConfig.endDirections = [DIRECTION.NORTH, DIRECTION.SOUTH];
ManhattanConnectorConfig.startDirections.push(DIRECTION.NORTH, DIRECTION.SOUTH);

resetManhattanConnectorConfig();

expect(ManhattanConnectorConfig.endDirections).toEqual(originalEndDirections);
expect(ManhattanConnectorConfig.startDirections).toEqual(originalStartDirections);
});
});
10 changes: 8 additions & 2 deletions packages/core/src/util/cloneUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export const clone = function _clone(
/**
* Shallow copies properties from the source object to the target object.
*
* **WARNING**: This function performs only a **shallow** copy i.e. there is no deep copy of the properties that are objects.
* **WARNING**: This function performs only a **shallow** copy i.e. there is no deep copy of the properties that are objects, expect for arrays.
*
* @template T The type of the objects.
*
Expand All @@ -74,7 +74,13 @@ export const shallowCopy = <T extends object>(source: T, target: T): void => {
for (const key in source) {
// attempt to prevent prototype pollution
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
const sourceValue = source[key];
if (Array.isArray(sourceValue)) {
// @ts-ignore source and target are of the same type
target[key] = [...sourceValue];
} else {
target[key] = sourceValue;
}
}
}
};
70 changes: 18 additions & 52 deletions packages/core/src/view/style/EdgeStyle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,15 @@ import {
import Rectangle from '../geometry/Rectangle';
import Geometry from '../geometry/Geometry';
import { scaleCellState, scalePointArray } from './edge/shared';
import type { EdgeStyleFunction } from '../../types';
import type { DirectionValue, EdgeStyleFunction } from '../../types';

import { ElbowConnector as ElbowConnectorFunction } from './edge/Elbow';
import { EntityRelation as EntityRelationFunction } from './edge/EntityRelation';
import { Loop as LoopFunction } from './edge/Loop';
import { SegmentConnector as SegmentConnectorFunction } from './edge/Segment';
import { SideToSide as SideToSideFunction } from './edge/SideToSide';
import { TopToBottom as TopToBottomFunction } from './edge/TopToBottom';
import { OrthConnectorConfig } from './config';
import { ManhattanConnectorConfig, OrthConnectorConfig } from './config';

/**
* Provides various edge styles to be used as the values for `edgeStyle` in a cell style.
Expand Down Expand Up @@ -788,39 +788,6 @@ class EdgeStyle {
}
};

// Size of the step to find a route
static MANHATTAN_STEP = 12;

// If number of route finding loops exceed the maximum, stops searching and returns
// fallback route
static MANHATTAN_MAXIMUM_LOOPS = 2000;

// Possible starting directions from an element
static MANHATTAN_START_DIRECTIONS: DIRECTION[] = [
DIRECTION.NORTH,
DIRECTION.EAST,
DIRECTION.SOUTH,
DIRECTION.WEST,
];

// Possible ending directions to an element
static MANHATTAN_END_DIRECTIONS: DIRECTION[] = [
DIRECTION.NORTH,
DIRECTION.EAST,
DIRECTION.SOUTH,
DIRECTION.WEST,
];

// Limit for directions change when searching route
static MANHATTAN_MAX_ALLOWED_DIRECTION_CHANGE = 90;

static MANHATTAN_PADDING_BOX = new Geometry(
-this.MANHATTAN_STEP,
-this.MANHATTAN_STEP,
this.MANHATTAN_STEP * 2,
this.MANHATTAN_STEP * 2
);

/**
* ManhattanConnector code is based on code from https://github.com/mwangm/mxgraph-manhattan-connector
*
Expand Down Expand Up @@ -929,11 +896,11 @@ class EdgeStyle {
return res;
}

const mStep = EdgeStyle.MANHATTAN_STEP;
const mStep = ManhattanConnectorConfig.step;

const config = {
// Padding applied on the element bounding boxes
paddingBox: EdgeStyle.MANHATTAN_PADDING_BOX,
paddingBox: new Geometry(-mStep, -mStep, mStep * 2, mStep * 2),

// An array of directions to find next points on the route
directions: [
Expand Down Expand Up @@ -972,8 +939,7 @@ class EdgeStyle {

// A penalty received for direction change
penaltiesGenerator: (angle: number) => {
if (angle == 45 || angle == 90 || angle == 180)
return EdgeStyle.MANHATTAN_STEP / 2;
if (angle == 45 || angle == 90 || angle == 180) return mStep / 2;
return 0;
},
// If a function is provided, it's used to route the link while dragging an end
Expand Down Expand Up @@ -1131,13 +1097,13 @@ class EdgeStyle {

function getRectPoints(
bbox: Rectangle,
directionList: DIRECTION[],
directions: DirectionValue[],
opt: typeof config
): Point[] {
const step = EdgeStyle.MANHATTAN_STEP;
const step = ManhattanConnectorConfig.step;
const center = getRectangleCenter(bbox);
const res: Point[] = [];
for (const direction of directionList) {
for (const direction of directions) {
const directionPoint = opt.directionMap[direction];

const x = (directionPoint.x * bbox.width) / 2;
Expand Down Expand Up @@ -1189,10 +1155,10 @@ class EdgeStyle {

const y = isSourceCell ? edgeState.style.exitY : edgeState.style.entryY;
const onlyHorizontalDirections = isSourceCell
? EdgeStyle.MANHATTAN_START_DIRECTIONS.every(
? ManhattanConnectorConfig.startDirections.every(
(d) => d != DIRECTION.NORTH && d != DIRECTION.SOUTH
)
: EdgeStyle.MANHATTAN_END_DIRECTIONS.every(
: ManhattanConnectorConfig.endDirections.every(
(d) => d != DIRECTION.NORTH && d != DIRECTION.SOUTH
);

Expand All @@ -1206,10 +1172,10 @@ class EdgeStyle {

const x = isSourceCell ? edgeState.style.exitX : edgeState.style.entryX;
const onlyVerticalDirections = isSourceCell
? EdgeStyle.MANHATTAN_START_DIRECTIONS.every(
? ManhattanConnectorConfig.startDirections.every(
(d) => d != DIRECTION.WEST && d != DIRECTION.EAST
)
: EdgeStyle.MANHATTAN_END_DIRECTIONS.every(
: ManhattanConnectorConfig.endDirections.every(
(d) => d != DIRECTION.WEST && d != DIRECTION.EAST
);
if (x != undefined && onlyVerticalDirections) {
Expand All @@ -1227,18 +1193,18 @@ class EdgeStyle {
obstacleMap: ObstacleMap,
opt: typeof config
) {
// Caculate start points and end points
const step = EdgeStyle.MANHATTAN_STEP;
// Calculate start points and end points
const step = ManhattanConnectorConfig.step;
const startPoints = getRectPoints(
start,
EdgeStyle.MANHATTAN_START_DIRECTIONS,
ManhattanConnectorConfig.startDirections,
opt
).filter((p) => obstacleMap.isPointAccessible(p));

const startCenter = snapPointToGrid(getRectangleCenter(start), step);
const endPoints = getRectPoints(
end,
EdgeStyle.MANHATTAN_END_DIRECTIONS,
ManhattanConnectorConfig.endDirections,
opt
).filter((p) => obstacleMap.isPointAccessible(p));
const endCenter = snapPointToGrid(getRectangleCenter(end), step);
Expand All @@ -1255,7 +1221,7 @@ class EdgeStyle {
openSet.add(key, estimateCost(p, endPoints));
costs[key] = 0;
});
let loopsRemain = EdgeStyle.MANHATTAN_MAXIMUM_LOOPS;
let loopsRemain = ManhattanConnectorConfig.maxLoops;
const endPointsKeys = endPoints.map((p) => pointToString(p));
let currentDirectionAngle: number | undefined;
let previousDirectionAngle: number | undefined;
Expand Down Expand Up @@ -1297,7 +1263,7 @@ class EdgeStyle {
);
if (
previousDirectionAngle &&
directionChangedAngle > EdgeStyle.MANHATTAN_MAX_ALLOWED_DIRECTION_CHANGE
directionChangedAngle > ManhattanConnectorConfig.maxAllowedDirectionChange
) {
continue;
}
Expand Down
61 changes: 60 additions & 1 deletion packages/core/src/view/style/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

import { ENTITY_SEGMENT } from '../../util/Constants';
import { DIRECTION, ENTITY_SEGMENT } from '../../util/Constants';
import { shallowCopy } from '../../util/cloneUtils';
import type { DirectionValue } from '../../types';

/**
* Configure the `Entity Relation connector` defaults for maxGraph.
Expand Down Expand Up @@ -81,3 +82,61 @@ const originalOrthConnectorConfig = { ...OrthConnectorConfig };
export const resetOrthConnectorConfig = (): void => {
shallowCopy(originalOrthConnectorConfig, OrthConnectorConfig);
};

export type ManhattanConnectorConfigType = {
/**
* Limit for directions change when searching route.
* @default 90
*/
maxAllowedDirectionChange: number;
/**
* If number of route finding loops exceed the maximum, stops searching and returns fallback route
*/
maxLoops: number;
/**
* Possible ending directions from an element.
*
* @default all directions
*/
endDirections: DirectionValue[];
/**
* Possible starting directions from an element.
*
* @default all directions
*/
startDirections: DirectionValue[];
/**
* Size of the step to find a route.
*
* @default 12
*/
step: number;
};

/**
* Configure the {@link ManhattanConnector}.
*
* @experimental subject to change or removal. maxGraph's global configuration may be modified in the future without prior notice.
* @since 0.16.0
* @category Configuration
*/
export const ManhattanConnectorConfig: ManhattanConnectorConfigType = {
maxAllowedDirectionChange: 90,
maxLoops: 2000,
endDirections: Object.values(DIRECTION),
startDirections: Object.values(DIRECTION),
step: 12,
};

const originalManhattanConnectorConfig = {} as typeof ManhattanConnectorConfig;
shallowCopy(ManhattanConnectorConfig, originalManhattanConnectorConfig);
/**
* Resets {@link ManhattanConnectorConfig} to default values.
*
* @experimental Subject to change or removal. maxGraph's global configuration may be modified in the future without prior notice.
* @since 0.16.0
* @category Configuration
*/
export const resetManhattanConnectorConfig = (): void => {
shallowCopy(originalManhattanConnectorConfig, ManhattanConnectorConfig);
};
2 changes: 2 additions & 0 deletions packages/html/.storybook/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
resetEdgeHandlerConfig,
resetEntityRelationConnectorConfig,
resetHandleConfig,
resetManhattanConnectorConfig,
resetOrthConnectorConfig,
resetStyleDefaultsConfig,
resetVertexHandlerConfig,
Expand All @@ -23,6 +24,7 @@ const resetMaxGraphConfigs = (): void => {
resetEdgeHandlerConfig();
resetEntityRelationConnectorConfig();
resetHandleConfig();
resetManhattanConnectorConfig();
resetOrthConnectorConfig();
resetStyleDefaultsConfig();
resetVertexHandlerConfig();
Expand Down
Loading