mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-10-25 08:54:07 +02:00
81 lines
2.6 KiB
TypeScript
81 lines
2.6 KiB
TypeScript
import rough from 'roughjs';
|
|
import { log } from '../../../logger.js';
|
|
import type { Bounds, D3Selection, Point } from '../../../types.js';
|
|
import { handleUndefinedAttr } from '../../../utils.js';
|
|
import type { Node } from '../../types.js';
|
|
import intersect from '../intersect/index.js';
|
|
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
|
import { getNodeClasses, labelHelper, updateNodeBounds } from './util.js';
|
|
|
|
export async function cloud<T extends SVGGraphicsElement>(parent: D3Selection<T>, node: Node) {
|
|
const { labelStyles, nodeStyles } = styles2String(node);
|
|
node.labelStyle = labelStyles;
|
|
|
|
const { shapeSvg, bbox, halfPadding, label } = await labelHelper(
|
|
parent,
|
|
node,
|
|
getNodeClasses(node)
|
|
);
|
|
|
|
const w = bbox.width + 2 * halfPadding;
|
|
const h = bbox.height + 2 * halfPadding;
|
|
|
|
// Cloud radii
|
|
const r1 = 0.15 * w;
|
|
const r2 = 0.25 * w;
|
|
const r3 = 0.35 * w;
|
|
const r4 = 0.2 * w;
|
|
|
|
const { cssStyles } = node;
|
|
let cloudElem;
|
|
|
|
// Cloud path
|
|
const path = `M0 0
|
|
a${r1},${r1} 0 0,1 ${w * 0.25},${-1 * w * 0.1}
|
|
a${r3},${r3} 1 0,1 ${w * 0.4},${-1 * w * 0.1}
|
|
a${r2},${r2} 1 0,1 ${w * 0.35},${w * 0.2}
|
|
|
|
a${r1},${r1} 1 0,1 ${w * 0.15},${h * 0.35}
|
|
a${r4},${r4} 1 0,1 ${-1 * w * 0.15},${h * 0.65}
|
|
|
|
a${r2},${r1} 1 0,1 ${-1 * w * 0.25},${w * 0.15}
|
|
a${r3},${r3} 1 0,1 ${-1 * w * 0.5},0
|
|
a${r1},${r1} 1 0,1 ${-1 * w * 0.25},${-1 * w * 0.15}
|
|
|
|
a${r1},${r1} 1 0,1 ${-1 * w * 0.1},${-1 * h * 0.35}
|
|
a${r4},${r4} 1 0,1 ${w * 0.1},${-1 * h * 0.65}
|
|
H0 V0 Z`;
|
|
|
|
if (node.look === 'handDrawn') {
|
|
// @ts-expect-error -- Passing a D3.Selection seems to work for some reason
|
|
const rc = rough.svg(shapeSvg);
|
|
const options = userNodeOverrides(node, {});
|
|
const roughNode = rc.path(path, options);
|
|
cloudElem = shapeSvg.insert(() => roughNode, ':first-child');
|
|
cloudElem.attr('class', 'basic label-container').attr('style', handleUndefinedAttr(cssStyles));
|
|
} else {
|
|
cloudElem = shapeSvg
|
|
.insert('path', ':first-child')
|
|
.attr('class', 'basic label-container')
|
|
.attr('style', nodeStyles)
|
|
.attr('d', path);
|
|
}
|
|
|
|
label.attr('transform', `translate(${-bbox.width / 2}, ${-bbox.height / 2})`);
|
|
|
|
// Center the shape
|
|
cloudElem.attr('transform', `translate(${-w / 2}, ${-h / 2})`);
|
|
|
|
updateNodeBounds(node, cloudElem);
|
|
|
|
node.calcIntersect = function (bounds: Bounds, point: Point) {
|
|
return intersect.rect(bounds, point);
|
|
};
|
|
node.intersect = function (point) {
|
|
log.info('Cloud intersect', node, point);
|
|
return intersect.rect(node, point);
|
|
};
|
|
|
|
return shapeSvg;
|
|
}
|