From 97cc2c2500b0b80e337d88bf85b15c684885bc80 Mon Sep 17 00:00:00 2001 From: Thomas Bouffard <27200110+tbouffard@users.noreply.github.com> Date: Wed, 19 Nov 2025 12:32:26 +0100 Subject: [PATCH 1/6] refactor: introduce AbstractPathShape base class for shape hierarchy Replace ActorShape as the base class for CloudShape, HexagonShape, and TriangleShape. These classes had no functional relation with ActorShape - the inheritance was only for technical convenience. Introduce AbstractPathShape as a dedicated abstract base class to make the hierarchy more explicit and semantically clear. ActorShape now extends AbstractPathShape alongside the other shapes. Changes: - Add new AbstractPathShape abstract class - Refactor ActorShape to extend AbstractPathShape - Update CloudShape, HexagonShape, and TriangleShape to extend AbstractPathShape --- packages/core/src/view/shape/Shape.ts | 4 +- .../src/view/shape/node/AbstractPathShape.ts | 83 +++++++++++++++++++ .../core/src/view/shape/node/ActorShape.ts | 53 ++---------- .../core/src/view/shape/node/CloudShape.ts | 22 ++--- .../core/src/view/shape/node/HexagonShape.ts | 17 ++-- .../core/src/view/shape/node/TriangleShape.ts | 17 ++-- 6 files changed, 130 insertions(+), 66 deletions(-) create mode 100644 packages/core/src/view/shape/node/AbstractPathShape.ts diff --git a/packages/core/src/view/shape/Shape.ts b/packages/core/src/view/shape/Shape.ts index c9903d63e9..34d1c580c6 100644 --- a/packages/core/src/view/shape/Shape.ts +++ b/packages/core/src/view/shape/Shape.ts @@ -1016,8 +1016,8 @@ class Shape { } /** - * Updates the for this shape using and - * and stores the result in . + * Updates the {@link boundingBox} for this shape using {@link createBoundingBox} and + * {@link augmentBoundingBox} and stores the result in {@link boundingBox}. */ updateBoundingBox() { // Tries to get bounding box from SVG subsystem diff --git a/packages/core/src/view/shape/node/AbstractPathShape.ts b/packages/core/src/view/shape/node/AbstractPathShape.ts new file mode 100644 index 0000000000..04a6725610 --- /dev/null +++ b/packages/core/src/view/shape/node/AbstractPathShape.ts @@ -0,0 +1,83 @@ +/* +Copyright 2021-present The maxGraph project Contributors +Copyright (c) 2006-2015, JGraph Ltd +Copyright (c) 2006-2015, Gaudenz Alder + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import type Rectangle from '../../geometry/Rectangle.js'; +import Shape from '../Shape.js'; +import type AbstractCanvas2D from '../../canvas/AbstractCanvas2D.js'; +import type { ColorValue } from '../../../types.js'; +import { NONE } from '../../../util/Constants.js'; + +/** + * Base {@link Shape} class to implement a shape with a specific . + * + * If a custom shape with one filled area is needed, then this shape's {@link redrawPath} method should be overridden like in the following example: + * + * ```typescript + * class SampleShape extends AbstractPathShape { + * redrawPath(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) { + * path.moveTo(0, 0); + * path.lineTo(w, h); + * // ... + * path.close(); + * } + * } + * ``` + * + * @category Vertex Shapes + */ +export abstract class AbstractPathShape extends Shape { + protected constructor( + bounds: Rectangle | null = null, + fill: ColorValue = NONE, + stroke: ColorValue = NONE, + strokeWidth = 1 + ) { + super(); + this.bounds = bounds; + this.fill = fill; + this.stroke = stroke; + this.strokeWidth = strokeWidth; + } + + /** + * Redirects to redrawPath for subclasses to work. + */ + override paintVertexShape( + c: AbstractCanvas2D, + x: number, + y: number, + w: number, + h: number + ) { + c.translate(x, y); + c.begin(); + this.redrawPath(c, x, y, w, h); + c.fillAndStroke(); + } + + /** + * Draws the path for this shape. + */ + abstract redrawPath( + c: AbstractCanvas2D, + x: number, + y: number, + w: number, + h: number + ): void; +} diff --git a/packages/core/src/view/shape/node/ActorShape.ts b/packages/core/src/view/shape/node/ActorShape.ts index 0c00454400..3f35d64d2d 100644 --- a/packages/core/src/view/shape/node/ActorShape.ts +++ b/packages/core/src/view/shape/node/ActorShape.ts @@ -17,66 +17,31 @@ limitations under the License. */ import type Rectangle from '../../geometry/Rectangle.js'; -import Shape from '../Shape.js'; +import { AbstractPathShape } from './AbstractPathShape.js'; import type AbstractCanvas2D from '../../canvas/AbstractCanvas2D.js'; -import { ColorValue } from '../../../types.js'; -import { NONE } from '../../../util/Constants.js'; +import type { ColorValue } from '../../../types.js'; /** * Extends {@link Shape} to implement an actor shape. * * This shape is registered under `actor` in {@link CellRenderer} when using {@link Graph} or calling {@link registerDefaultShapes}. * - * If a custom shape with one filled area is needed, then this shape's {@link redrawPath} method should be overridden - * like in the following example: - * - * ```typescript - * class SampleShape extends ActorShape { - * redrawPath(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) { - * path.moveTo(0, 0); - * path.lineTo(w, h); - * // ... - * path.close(); - * } - * } - * ``` - * * @category Vertex Shapes */ -class ActorShape extends Shape { +class ActorShape extends AbstractPathShape { constructor( - bounds: Rectangle | null = null, - fill: ColorValue = NONE, - stroke: ColorValue = NONE, - strokeWidth = 1 - ) { - super(); - this.bounds = bounds; - this.fill = fill; - this.stroke = stroke; - this.strokeWidth = strokeWidth; - } - - /** - * Redirects to redrawPath for subclasses to work. - */ - override paintVertexShape( - c: AbstractCanvas2D, - x: number, - y: number, - w: number, - h: number + bounds?: Rectangle | null, + fill?: ColorValue, + stroke?: ColorValue, + strokeWidth?: number ) { - c.translate(x, y); - c.begin(); - this.redrawPath(c, x, y, w, h); - c.fillAndStroke(); + super(bounds, fill, stroke, strokeWidth); } /** * Draws the path for this shape. */ - redrawPath(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) { + override redrawPath(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) { const width = w / 3; c.moveTo(0, h); c.curveTo(0, (3 * h) / 5, 0, (2 * h) / 5, w / 2, (2 * h) / 5); diff --git a/packages/core/src/view/shape/node/CloudShape.ts b/packages/core/src/view/shape/node/CloudShape.ts index d57bdf2fc2..f8066a6d4c 100644 --- a/packages/core/src/view/shape/node/CloudShape.ts +++ b/packages/core/src/view/shape/node/CloudShape.ts @@ -16,9 +16,10 @@ See the License for the specific language governing permissions and limitations under the License. */ -import ActorShape from './ActorShape.js'; -import AbstractCanvas2D from '../../canvas/AbstractCanvas2D.js'; -import Rectangle from '../../geometry/Rectangle.js'; +import { AbstractPathShape } from './AbstractPathShape.js'; +import type AbstractCanvas2D from '../../canvas/AbstractCanvas2D.js'; +import type Rectangle from '../../geometry/Rectangle.js'; +import type { ColorValue } from '../../../types.js'; /** * Extends {@link ActorShape} to implement a cloud shape. @@ -27,13 +28,14 @@ import Rectangle from '../../geometry/Rectangle.js'; * * @category Vertex Shapes */ -class CloudShape extends ActorShape { - constructor(bounds: Rectangle, fill: string, stroke: string, strokeWidth = 1) { - super(); - this.bounds = bounds; - this.fill = fill; - this.stroke = stroke; - this.strokeWidth = strokeWidth; +class CloudShape extends AbstractPathShape { + constructor( + bounds?: Rectangle | null, + fill?: ColorValue, + stroke?: ColorValue, + strokeWidth?: number + ) { + super(bounds, fill, stroke, strokeWidth); } /** diff --git a/packages/core/src/view/shape/node/HexagonShape.ts b/packages/core/src/view/shape/node/HexagonShape.ts index ca7723392e..12fb5dad14 100644 --- a/packages/core/src/view/shape/node/HexagonShape.ts +++ b/packages/core/src/view/shape/node/HexagonShape.ts @@ -16,9 +16,11 @@ See the License for the specific language governing permissions and limitations under the License. */ -import ActorShape from './ActorShape.js'; +import { AbstractPathShape } from './AbstractPathShape.js'; import Point from '../../geometry/Point.js'; -import AbstractCanvas2D from '../../canvas/AbstractCanvas2D.js'; +import type AbstractCanvas2D from '../../canvas/AbstractCanvas2D.js'; +import type Rectangle from '../../geometry/Rectangle.js'; +import type { ColorValue } from '../../../types.js'; /** * Implementation of the hexagon shape. @@ -27,9 +29,14 @@ import AbstractCanvas2D from '../../canvas/AbstractCanvas2D.js'; * * @category Vertex Shapes */ -class HexagonShape extends ActorShape { - constructor() { - super(); +class HexagonShape extends AbstractPathShape { + constructor( + bounds?: Rectangle | null, + fill?: ColorValue, + stroke?: ColorValue, + strokeWidth?: number + ) { + super(bounds, fill, stroke, strokeWidth); } /** diff --git a/packages/core/src/view/shape/node/TriangleShape.ts b/packages/core/src/view/shape/node/TriangleShape.ts index 87df6c938e..22a0884b71 100644 --- a/packages/core/src/view/shape/node/TriangleShape.ts +++ b/packages/core/src/view/shape/node/TriangleShape.ts @@ -17,8 +17,10 @@ limitations under the License. */ import Point from '../../geometry/Point.js'; -import ActorShape from './ActorShape.js'; -import AbstractCanvas2D from '../../canvas/AbstractCanvas2D.js'; +import { AbstractPathShape } from './AbstractPathShape.js'; +import type AbstractCanvas2D from '../../canvas/AbstractCanvas2D.js'; +import type Rectangle from '../../geometry/Rectangle.js'; +import type { ColorValue } from '../../../types.js'; /** * Implementation of the triangle shape. @@ -27,9 +29,14 @@ import AbstractCanvas2D from '../../canvas/AbstractCanvas2D.js'; * * @category Vertex Shapes */ -class TriangleShape extends ActorShape { - constructor() { - super(); +class TriangleShape extends AbstractPathShape { + constructor( + bounds?: Rectangle | null, + fill?: ColorValue, + stroke?: ColorValue, + strokeWidth?: number + ) { + super(bounds, fill, stroke, strokeWidth); } /** From b6fc4823c326272d989b26e11f7fe4434c36695e Mon Sep 17 00:00:00 2001 From: Thomas Bouffard <27200110+tbouffard@users.noreply.github.com> Date: Wed, 19 Nov 2025 12:53:31 +0100 Subject: [PATCH 2/6] AbstractPathShape.ts: fix jsdoc --- packages/core/src/view/shape/node/AbstractPathShape.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/core/src/view/shape/node/AbstractPathShape.ts b/packages/core/src/view/shape/node/AbstractPathShape.ts index 04a6725610..90b2eda167 100644 --- a/packages/core/src/view/shape/node/AbstractPathShape.ts +++ b/packages/core/src/view/shape/node/AbstractPathShape.ts @@ -23,17 +23,17 @@ import type { ColorValue } from '../../../types.js'; import { NONE } from '../../../util/Constants.js'; /** - * Base {@link Shape} class to implement a shape with a specific . + * Base {@link Shape} for vertex shapes rendered from a single path. * - * If a custom shape with one filled area is needed, then this shape's {@link redrawPath} method should be overridden like in the following example: + * If a custom shape with one filled area is needed, override {@link redrawPath} as in the following example: * * ```typescript * class SampleShape extends AbstractPathShape { * redrawPath(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) { - * path.moveTo(0, 0); - * path.lineTo(w, h); + * c.moveTo(0, 0); + * c.lineTo(w, h); * // ... - * path.close(); + * c.close(); * } * } * ``` From 0344fd3708ea433ab7acc79cc8642d4fb28bd3c9 Mon Sep 17 00:00:00 2001 From: Thomas Bouffard <27200110+tbouffard@users.noreply.github.com> Date: Mon, 24 Nov 2025 11:34:26 +0100 Subject: [PATCH 3/6] ActorShape: improve jsdoc --- packages/core/src/view/shape/node/ActorShape.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/view/shape/node/ActorShape.ts b/packages/core/src/view/shape/node/ActorShape.ts index 3f35d64d2d..0fa38ed8d2 100644 --- a/packages/core/src/view/shape/node/ActorShape.ts +++ b/packages/core/src/view/shape/node/ActorShape.ts @@ -22,7 +22,7 @@ import type AbstractCanvas2D from '../../canvas/AbstractCanvas2D.js'; import type { ColorValue } from '../../../types.js'; /** - * Extends {@link Shape} to implement an actor shape. + * Path-based actor vertex shape built on {@link AbstractPathShape}. * * This shape is registered under `actor` in {@link CellRenderer} when using {@link Graph} or calling {@link registerDefaultShapes}. * From 859089641567ae1c9639a7c91cd10a753e04a23f Mon Sep 17 00:00:00 2001 From: Thomas Bouffard <27200110+tbouffard@users.noreply.github.com> Date: Mon, 24 Nov 2025 11:46:16 +0100 Subject: [PATCH 4/6] remove extra constructors --- packages/core/src/view/shape/node/ActorShape.ts | 11 ----------- packages/core/src/view/shape/node/CloudShape.ts | 11 ----------- packages/core/src/view/shape/node/HexagonShape.ts | 11 ----------- packages/core/src/view/shape/node/TriangleShape.ts | 11 ----------- 4 files changed, 44 deletions(-) diff --git a/packages/core/src/view/shape/node/ActorShape.ts b/packages/core/src/view/shape/node/ActorShape.ts index 0fa38ed8d2..e6922ab9e9 100644 --- a/packages/core/src/view/shape/node/ActorShape.ts +++ b/packages/core/src/view/shape/node/ActorShape.ts @@ -16,10 +16,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -import type Rectangle from '../../geometry/Rectangle.js'; import { AbstractPathShape } from './AbstractPathShape.js'; import type AbstractCanvas2D from '../../canvas/AbstractCanvas2D.js'; -import type { ColorValue } from '../../../types.js'; /** * Path-based actor vertex shape built on {@link AbstractPathShape}. @@ -29,15 +27,6 @@ import type { ColorValue } from '../../../types.js'; * @category Vertex Shapes */ class ActorShape extends AbstractPathShape { - constructor( - bounds?: Rectangle | null, - fill?: ColorValue, - stroke?: ColorValue, - strokeWidth?: number - ) { - super(bounds, fill, stroke, strokeWidth); - } - /** * Draws the path for this shape. */ diff --git a/packages/core/src/view/shape/node/CloudShape.ts b/packages/core/src/view/shape/node/CloudShape.ts index f8066a6d4c..71bfb6a843 100644 --- a/packages/core/src/view/shape/node/CloudShape.ts +++ b/packages/core/src/view/shape/node/CloudShape.ts @@ -18,8 +18,6 @@ limitations under the License. import { AbstractPathShape } from './AbstractPathShape.js'; import type AbstractCanvas2D from '../../canvas/AbstractCanvas2D.js'; -import type Rectangle from '../../geometry/Rectangle.js'; -import type { ColorValue } from '../../../types.js'; /** * Extends {@link ActorShape} to implement a cloud shape. @@ -29,15 +27,6 @@ import type { ColorValue } from '../../../types.js'; * @category Vertex Shapes */ class CloudShape extends AbstractPathShape { - constructor( - bounds?: Rectangle | null, - fill?: ColorValue, - stroke?: ColorValue, - strokeWidth?: number - ) { - super(bounds, fill, stroke, strokeWidth); - } - /** * Draws the path for this shape. */ diff --git a/packages/core/src/view/shape/node/HexagonShape.ts b/packages/core/src/view/shape/node/HexagonShape.ts index 12fb5dad14..2a8f8f236c 100644 --- a/packages/core/src/view/shape/node/HexagonShape.ts +++ b/packages/core/src/view/shape/node/HexagonShape.ts @@ -19,8 +19,6 @@ limitations under the License. import { AbstractPathShape } from './AbstractPathShape.js'; import Point from '../../geometry/Point.js'; import type AbstractCanvas2D from '../../canvas/AbstractCanvas2D.js'; -import type Rectangle from '../../geometry/Rectangle.js'; -import type { ColorValue } from '../../../types.js'; /** * Implementation of the hexagon shape. @@ -30,15 +28,6 @@ import type { ColorValue } from '../../../types.js'; * @category Vertex Shapes */ class HexagonShape extends AbstractPathShape { - constructor( - bounds?: Rectangle | null, - fill?: ColorValue, - stroke?: ColorValue, - strokeWidth?: number - ) { - super(bounds, fill, stroke, strokeWidth); - } - /** * Draws the path for this shape. */ diff --git a/packages/core/src/view/shape/node/TriangleShape.ts b/packages/core/src/view/shape/node/TriangleShape.ts index 22a0884b71..e5c5c772e8 100644 --- a/packages/core/src/view/shape/node/TriangleShape.ts +++ b/packages/core/src/view/shape/node/TriangleShape.ts @@ -19,8 +19,6 @@ limitations under the License. import Point from '../../geometry/Point.js'; import { AbstractPathShape } from './AbstractPathShape.js'; import type AbstractCanvas2D from '../../canvas/AbstractCanvas2D.js'; -import type Rectangle from '../../geometry/Rectangle.js'; -import type { ColorValue } from '../../../types.js'; /** * Implementation of the triangle shape. @@ -30,15 +28,6 @@ import type { ColorValue } from '../../../types.js'; * @category Vertex Shapes */ class TriangleShape extends AbstractPathShape { - constructor( - bounds?: Rectangle | null, - fill?: ColorValue, - stroke?: ColorValue, - strokeWidth?: number - ) { - super(bounds, fill, stroke, strokeWidth); - } - /** * Adds roundable support. * @returns {boolean} From f0c0aa8e3d1db60b36cf18da8066def872eaf87a Mon Sep 17 00:00:00 2001 From: Thomas Bouffard <27200110+tbouffard@users.noreply.github.com> Date: Mon, 24 Nov 2025 11:49:50 +0100 Subject: [PATCH 5/6] make AbstractPathShape constructor public to allow to construct subclasses not redefining constructors --- packages/core/src/view/shape/node/AbstractPathShape.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/view/shape/node/AbstractPathShape.ts b/packages/core/src/view/shape/node/AbstractPathShape.ts index 90b2eda167..707b2808b6 100644 --- a/packages/core/src/view/shape/node/AbstractPathShape.ts +++ b/packages/core/src/view/shape/node/AbstractPathShape.ts @@ -41,7 +41,7 @@ import { NONE } from '../../../util/Constants.js'; * @category Vertex Shapes */ export abstract class AbstractPathShape extends Shape { - protected constructor( + constructor( bounds: Rectangle | null = null, fill: ColorValue = NONE, stroke: ColorValue = NONE, From 59aaf029926343651d664c0008dbc4f95ae2cc8c Mon Sep 17 00:00:00 2001 From: Thomas Bouffard <27200110+tbouffard@users.noreply.github.com> Date: Mon, 24 Nov 2025 11:51:25 +0100 Subject: [PATCH 6/6] fix jsdoc [skip ci] --- packages/core/src/view/shape/node/CloudShape.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/view/shape/node/CloudShape.ts b/packages/core/src/view/shape/node/CloudShape.ts index 71bfb6a843..e89b182df3 100644 --- a/packages/core/src/view/shape/node/CloudShape.ts +++ b/packages/core/src/view/shape/node/CloudShape.ts @@ -20,7 +20,7 @@ import { AbstractPathShape } from './AbstractPathShape.js'; import type AbstractCanvas2D from '../../canvas/AbstractCanvas2D.js'; /** - * Extends {@link ActorShape} to implement a cloud shape. + * Path-based cloud vertex shape built on {@link AbstractPathShape}. * * This shape is registered under `cloud` in {@link CellRenderer} when using {@link Graph} or calling {@link registerDefaultShapes}. *