Skip to content

Commit

Permalink
feat(draw): add component componentBox and template shape for uml #WI…
Browse files Browse the repository at this point in the history
…K-15712 (#910)
  • Loading branch information
MissLixf authored Jun 6, 2024
1 parent 1ee86dc commit be8a3d2
Show file tree
Hide file tree
Showing 8 changed files with 332 additions and 9 deletions.
5 changes: 5 additions & 0 deletions .changeset/olive-starfishes-hunt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@plait/draw': minor
---

add component componentBox and template shape for uml
17 changes: 9 additions & 8 deletions packages/draw/src/constants/geometry.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
import { ACTIVE_STROKE_WIDTH } from '@plait/core';
import { Alignment } from '@plait/text';
import {
BasicShapes,
FlowchartSymbols,
GeometryShapes,
MultipleTextGeometryCommonTextKeys,
UMLSymbols
} from '../interfaces';
import { BasicShapes, FlowchartSymbols, GeometryShapes, MultipleTextGeometryCommonTextKeys, UMLSymbols } from '../interfaces';

export const ShapeDefaultSpace = {
rectangleAndText: 4
Expand Down Expand Up @@ -153,6 +147,11 @@ export const DefaultObjectProperty = {
height: 60
};

export const DefaultComponentBoxProperty = {
width: 200,
height: 150
};

export const DefaultDeletionProperty = {
width: 40,
height: 40
Expand Down Expand Up @@ -257,6 +256,9 @@ export const DefaultUMLPropertyMap = {
[UMLSymbols.deletion]: DefaultDeletionProperty,
[UMLSymbols.activityClass]: DefaultObjectProperty,
[UMLSymbols.simpleClass]: DefaultObjectProperty,
[UMLSymbols.component]: DefaultMultiDocumentProperty,
[UMLSymbols.template]: DefaultMultiDocumentProperty,
[UMLSymbols.componentBox]: DefaultComponentBoxProperty,
[UMLSymbols.port]: DefaultPortProperty,
[UMLSymbols.branchMerge]: DefaultDeletionProperty
};
Expand All @@ -266,7 +268,6 @@ export const MultipleTextGeometryTextKeys: { [key in GeometryShapes]?: string[]
[UMLSymbols.combinedFragment]: Object.keys(MultipleTextGeometryCommonTextKeys)
};


export const LINE_HIT_GEOMETRY_BUFFER = 10;

export const LINE_SNAPPING_BUFFER = 6;
Expand Down
6 changes: 6 additions & 0 deletions packages/draw/src/engines/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ import { CombinedFragmentEngine } from './uml/combined-fragment';
import { DeletionEngine } from './uml/deletion';
import { ActiveClassEngine } from './uml/activity-class';
import { NoteEngine } from './uml/note';
import { ComponentEngine } from './uml/component';
import { ComponentBoxEngine } from './uml/component-box';
import { TemplateEngine } from './uml/template';

const ShapeEngineMap: Record<DrawShapes, ShapeEngine<any, any, any>> = {
[BasicShapes.rectangle]: RectangleEngine,
Expand Down Expand Up @@ -121,6 +124,9 @@ const ShapeEngineMap: Record<DrawShapes, ShapeEngine<any, any, any>> = {
[UMLSymbols.deletion]: DeletionEngine,
[UMLSymbols.activityClass]: ActiveClassEngine,
[UMLSymbols.simpleClass]: RectangleEngine,
[UMLSymbols.component]: ComponentEngine,
[UMLSymbols.componentBox]: ComponentBoxEngine,
[UMLSymbols.template]: TemplateEngine,
[UMLSymbols.port]: RectangleEngine,
[UMLSymbols.branchMerge]: DiamondEngine
};
Expand Down
76 changes: 76 additions & 0 deletions packages/draw/src/engines/uml/component-box.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import {
PlaitBoard,
Point,
PointOfRectangle,
RectangleClient,
getNearestPointBetweenPointAndSegments,
setStrokeLinecap
} from '@plait/core';
import { PlaitGeometry, ShapeEngine } from '../../interfaces';
import { Options } from 'roughjs/bin/core';
import { getPolygonEdgeByConnectionPoint } from '../../utils/polygon';
import { RectangleEngine } from '../basic-shapes/rectangle';
import { getStrokeWidthByElement } from '../../utils';
import { ShapeDefaultSpace } from '../../constants';
import { ComponentEngine } from './component';

export const ComponentBoxEngine: ShapeEngine = {
draw(board: PlaitBoard, rectangle: RectangleClient, options: Options) {
const rs = PlaitBoard.getRoughSVG(board);
const componentWidth = rectangle.width - 45 * 2 - 18 > 1 ? 45 : rectangle.width * 0.25;
const componentHeight = rectangle.height - 30 - 8 * 2 > 1 ? 30 : rectangle.height * 0.2;

const componentRectangle = {
x: rectangle.x + rectangle.width - 18 - componentWidth,
y: rectangle.y + 8,
width: componentWidth,
height: componentHeight
};
const shape = rs.path(
`M${rectangle.x} ${rectangle.y}
H${rectangle.x + rectangle.width}
V${rectangle.y + rectangle.height}
H${rectangle.x} Z
`,
{ ...options, fillStyle: 'solid' }
);

const componentShape = ComponentEngine.draw(board, componentRectangle, options);
shape.append(componentShape);
setStrokeLinecap(shape, 'round');

return shape;
},
isInsidePoint(rectangle: RectangleClient, point: Point) {
const rangeRectangle = RectangleClient.getRectangleByPoints([point, point]);
return RectangleClient.isHit(rectangle, rangeRectangle);
},
getCornerPoints(rectangle: RectangleClient) {
return RectangleClient.getCornerPoints(rectangle);
},
getNearestPoint(rectangle: RectangleClient, point: Point) {
return getNearestPointBetweenPointAndSegments(point, RectangleEngine.getCornerPoints(rectangle));
},
getEdgeByConnectionPoint(rectangle: RectangleClient, pointOfRectangle: PointOfRectangle): [Point, Point] | null {
const corners = RectangleEngine.getCornerPoints(rectangle);
const point = RectangleClient.getConnectionPoint(rectangle, pointOfRectangle);
return getPolygonEdgeByConnectionPoint(corners, point);
},
getConnectorPoints(rectangle: RectangleClient) {
return RectangleClient.getEdgeCenterPoints(rectangle);
},
getTextRectangle(element: PlaitGeometry) {
const elementRectangle = RectangleClient.getRectangleByPoints(element.points!);
const strokeWidth = getStrokeWidthByElement(element);
const height = element.textHeight!;
const componentWidth = elementRectangle.width - 45 * 2 - 18 > 1 ? 45 : elementRectangle.width * 0.25;
const width = elementRectangle.width - 18 - componentWidth - ShapeDefaultSpace.rectangleAndText - strokeWidth * 2;
return {
height,
width: width > 0 ? width : 0,
x: elementRectangle.x + ShapeDefaultSpace.rectangleAndText + strokeWidth,
y: elementRectangle.y + (elementRectangle.height - height) / 2
};
}
};
93 changes: 93 additions & 0 deletions packages/draw/src/engines/uml/component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import {
PlaitBoard,
Point,
PointOfRectangle,
RectangleClient,
getNearestPointBetweenPointAndSegments,
setStrokeLinecap
} from '@plait/core';
import { PlaitGeometry, ShapeEngine } from '../../interfaces';
import { Options } from 'roughjs/bin/core';
import { RectangleEngine } from '../basic-shapes/rectangle';
import { getStrokeWidthByElement } from '../../utils';
import { ShapeDefaultSpace } from '../../constants';
import { getUnitVectorByPointAndPoint } from '@plait/common';

export const ComponentEngine: ShapeEngine = {
draw(board: PlaitBoard, rectangle: RectangleClient, options: Options) {
const rs = PlaitBoard.getRoughSVG(board);
const boxSize = {
with: rectangle.width > 70 ? 24 : rectangle.width * 0.2,
height: rectangle.height - 28 - rectangle.height * 0.35 > 1 ? 14 : rectangle.height * 0.175
};
const shape = rs.path(
`M${rectangle.x + 12} ${rectangle.y}
v${rectangle.height * 0.175}
h${boxSize.with / 2} v${boxSize.height} h${-boxSize.with} v${-boxSize.height} h${boxSize.with / 2}
M${rectangle.x + 12} ${rectangle.y + rectangle.height * 0.175 + boxSize.height}
v${rectangle.height - rectangle.height * 0.35 - boxSize.height * 2}
h${boxSize.with / 2} v${boxSize.height} h${-boxSize.with} v${-boxSize.height} h${boxSize.with / 2}
M${rectangle.x + 12} ${rectangle.y + rectangle.height - rectangle.height * 0.175}
V${rectangle.y + rectangle.height}
H${rectangle.x + rectangle.width}
v${-rectangle.height}
h${-(rectangle.width - 12)}
`,
{ ...options, fillStyle: 'solid' }
);
setStrokeLinecap(shape, 'round');

return shape;
},
isInsidePoint(rectangle: RectangleClient, point: Point) {
const rangeRectangle = RectangleClient.getRectangleByPoints([point, point]);
return RectangleClient.isHit(rectangle, rangeRectangle);
},
getCornerPoints(rectangle: RectangleClient) {
return RectangleClient.getCornerPoints(rectangle);
},
getNearestPoint(rectangle: RectangleClient, point: Point) {
let nearestPoint = getNearestPointBetweenPointAndSegments(point, RectangleEngine.getCornerPoints(rectangle));
if (nearestPoint[1] === rectangle.y + rectangle.height / 2) {
nearestPoint = getNearestPointBetweenPointAndSegments(
point,
[
[rectangle.x + 12, rectangle.y + rectangle.height * 0.175 + 14],
[rectangle.x + 12, rectangle.y + rectangle.height - rectangle.height * 0.175 - 14]
],
false
);
}
return nearestPoint;
},

getTangentVectorByConnectionPoint(rectangle: RectangleClient, pointOfRectangle: PointOfRectangle) {
const connectionPoint = RectangleClient.getConnectionPoint(rectangle, pointOfRectangle);
return getUnitVectorByPointAndPoint(
[rectangle.x + 12, rectangle.y + rectangle.height - rectangle.height * 0.175 - 14],
connectionPoint
);
},
getConnectorPoints(rectangle: RectangleClient) {
return [
[rectangle.x + rectangle.width / 2, rectangle.y],
[rectangle.x + rectangle.width, rectangle.y + rectangle.height / 2],
[rectangle.x + rectangle.width / 2, rectangle.y + rectangle.height],
[rectangle.x + 12, rectangle.y + rectangle.height / 2]
] as [Point, Point, Point, Point];
},
getTextRectangle(element: PlaitGeometry) {
const elementRectangle = RectangleClient.getRectangleByPoints(element.points!);
const strokeWidth = getStrokeWidthByElement(element);
const height = element.textHeight!;
const width = elementRectangle.width - 24 - ShapeDefaultSpace.rectangleAndText * 2 - strokeWidth * 2;
return {
height,
width: width > 0 ? width : 0,
x: elementRectangle.x + 24 + ShapeDefaultSpace.rectangleAndText + strokeWidth,
y: elementRectangle.y + (elementRectangle.height - height) / 2
};
}
};
66 changes: 66 additions & 0 deletions packages/draw/src/engines/uml/template.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import {
PlaitBoard,
Point,
PointOfRectangle,
RectangleClient,
drawRoundRectangle,
getNearestPointBetweenPointAndSegments
} from '@plait/core';
import { PlaitGeometry, ShapeEngine } from '../../interfaces';
import { Options } from 'roughjs/bin/core';
import { getPolygonEdgeByConnectionPoint } from '../../utils/polygon';
import { RectangleEngine } from '../basic-shapes/rectangle';
import { getStrokeWidthByElement } from '../../utils';
import { ShapeDefaultSpace } from '../../constants';

export const TemplateEngine: ShapeEngine = {
draw(board: PlaitBoard, rectangle: RectangleClient, options: Options) {
const rs = PlaitBoard.getRoughSVG(board);

return drawRoundRectangle(
rs,
rectangle.x,
rectangle.y,
rectangle.x + rectangle.width,
rectangle.y + rectangle.height,
{
...options,
fillStyle: 'solid',
dashGap: 10,
strokeLineDash: [10, 10]
},
false,
4
);
},
isInsidePoint(rectangle: RectangleClient, point: Point) {
const rangeRectangle = RectangleClient.getRectangleByPoints([point, point]);
return RectangleClient.isHit(rectangle, rangeRectangle);
},
getCornerPoints(rectangle: RectangleClient) {
return RectangleClient.getCornerPoints(rectangle);
},
getNearestPoint(rectangle: RectangleClient, point: Point) {
return getNearestPointBetweenPointAndSegments(point, RectangleEngine.getCornerPoints(rectangle));
},
getEdgeByConnectionPoint(rectangle: RectangleClient, pointOfRectangle: PointOfRectangle): [Point, Point] | null {
const corners = RectangleEngine.getCornerPoints(rectangle);
const point = RectangleClient.getConnectionPoint(rectangle, pointOfRectangle);
return getPolygonEdgeByConnectionPoint(corners, point);
},
getConnectorPoints(rectangle: RectangleClient) {
return RectangleClient.getEdgeCenterPoints(rectangle);
},
getTextRectangle(element: PlaitGeometry) {
const elementRectangle = RectangleClient.getRectangleByPoints(element.points!);
const strokeWidth = getStrokeWidthByElement(element);
const height = element.textHeight!;
const width = elementRectangle.width - ShapeDefaultSpace.rectangleAndText * 2 - strokeWidth * 2;
return {
height,
width: width > 0 ? width : 0,
x: elementRectangle.x + ShapeDefaultSpace.rectangleAndText + strokeWidth,
y: elementRectangle.y + (elementRectangle.height - height) / 2
};
}
};
4 changes: 3 additions & 1 deletion packages/draw/src/interfaces/geometry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ export enum FlowchartSymbols {
display = 'display'
}


export enum UMLSymbols {
actor = 'actor',
useCase = 'useCase',
Expand All @@ -69,6 +68,9 @@ export enum UMLSymbols {
class = 'class',
interface = 'interface',
object = 'object',
component = 'component',
componentBox = 'componentBox',
template = 'template',
activation = 'activation',
deletion = 'deletion'
}
Expand Down
Loading

0 comments on commit be8a3d2

Please sign in to comment.