mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-09-18 23:09:49 +02:00
Merge from Upstream new-shapes branch
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
import { imgSnapshotTest } from '../../helpers/util.ts';
|
import { imgSnapshotTest } from '../../helpers/util.ts';
|
||||||
|
|
||||||
const looks = ['classic'] as const;
|
const looks = ['classic', 'handDrawn'] as const;
|
||||||
const directions = ['TB'] as const;
|
const directions = ['TB', 'BT', 'LR', 'RL'] as const;
|
||||||
const newShapesSet1 = [
|
const newShapesSet1 = [
|
||||||
'triangle',
|
'triangle',
|
||||||
'sloped-rectangle',
|
'sloped-rectangle',
|
||||||
@@ -68,7 +68,7 @@ looks.forEach((look) => {
|
|||||||
it(`with label`, () => {
|
it(`with label`, () => {
|
||||||
let flowchartCode = `flowchart ${direction}\n`;
|
let flowchartCode = `flowchart ${direction}\n`;
|
||||||
newShapesSet.forEach((newShape, index) => {
|
newShapesSet.forEach((newShape, index) => {
|
||||||
flowchartCode += ` n${index} --> n${index}${index}{ shape: ${newShape}, label: 'This is a label for ${newShape} shape' }\n`;
|
flowchartCode += ` n${index} --> n${index}${index}@{ shape: ${newShape}, label: 'This is a label for ${newShape} shape' }\n`;
|
||||||
});
|
});
|
||||||
imgSnapshotTest(flowchartCode, { look });
|
imgSnapshotTest(flowchartCode, { look });
|
||||||
});
|
});
|
||||||
@@ -76,7 +76,7 @@ looks.forEach((look) => {
|
|||||||
it(`connect all shapes with each other`, () => {
|
it(`connect all shapes with each other`, () => {
|
||||||
let flowchartCode = `flowchart ${direction}\n`;
|
let flowchartCode = `flowchart ${direction}\n`;
|
||||||
newShapesSet.forEach((newShape, index) => {
|
newShapesSet.forEach((newShape, index) => {
|
||||||
flowchartCode += ` n${index}${index}{ shape: ${newShape}, label: 'This is a label for ${newShape} shape' }\n`;
|
flowchartCode += ` n${index}${index}@{ shape: ${newShape}, label: 'This is a label for ${newShape} shape' }\n`;
|
||||||
});
|
});
|
||||||
for (let i = 0; i < newShapesSet.length; i++) {
|
for (let i = 0; i < newShapesSet.length; i++) {
|
||||||
for (let j = i + 1; j < newShapesSet.length; j++) {
|
for (let j = i + 1; j < newShapesSet.length; j++) {
|
||||||
@@ -89,7 +89,7 @@ looks.forEach((look) => {
|
|||||||
it(`with very long label`, () => {
|
it(`with very long label`, () => {
|
||||||
let flowchartCode = `flowchart ${direction}\n`;
|
let flowchartCode = `flowchart ${direction}\n`;
|
||||||
newShapesSet.forEach((newShape, index) => {
|
newShapesSet.forEach((newShape, index) => {
|
||||||
flowchartCode += ` n${index} --> n${index}${index}{ shape: ${newShape}, label: 'This is a very very very very very long long long label for ${newShape} shape' }\n`;
|
flowchartCode += ` n${index} --> n${index}${index}@{ shape: ${newShape}, label: 'This is a very very very very very long long long label for ${newShape} shape' }\n`;
|
||||||
});
|
});
|
||||||
imgSnapshotTest(flowchartCode, { look });
|
imgSnapshotTest(flowchartCode, { look });
|
||||||
});
|
});
|
||||||
@@ -97,7 +97,7 @@ looks.forEach((look) => {
|
|||||||
it(`with markdown htmlLabels:true`, () => {
|
it(`with markdown htmlLabels:true`, () => {
|
||||||
let flowchartCode = `flowchart ${direction}\n`;
|
let flowchartCode = `flowchart ${direction}\n`;
|
||||||
newShapesSet.forEach((newShape, index) => {
|
newShapesSet.forEach((newShape, index) => {
|
||||||
flowchartCode += ` n${index} --> n${index}${index}{ shape: ${newShape}, label: 'This is **bold** </br>and <strong>strong</strong> for ${newShape} shape' }\n`;
|
flowchartCode += ` n${index} --> n${index}${index}@{ shape: ${newShape}, label: 'This is **bold** </br>and <strong>strong</strong> for ${newShape} shape' }\n`;
|
||||||
});
|
});
|
||||||
imgSnapshotTest(flowchartCode, { look });
|
imgSnapshotTest(flowchartCode, { look });
|
||||||
});
|
});
|
||||||
@@ -105,7 +105,7 @@ looks.forEach((look) => {
|
|||||||
it(`with markdown htmlLabels:false`, () => {
|
it(`with markdown htmlLabels:false`, () => {
|
||||||
let flowchartCode = `flowchart ${direction}\n`;
|
let flowchartCode = `flowchart ${direction}\n`;
|
||||||
newShapesSet.forEach((newShape, index) => {
|
newShapesSet.forEach((newShape, index) => {
|
||||||
flowchartCode += ` n${index} --> n${index}${index}{ shape: ${newShape}, label: 'This is **bold** </br>and <strong>strong</strong> for ${newShape} shape' }\n`;
|
flowchartCode += ` n${index} --> n${index}${index}@{ shape: ${newShape}, label: 'This is **bold** </br>and <strong>strong</strong> for ${newShape} shape' }\n`;
|
||||||
});
|
});
|
||||||
imgSnapshotTest(flowchartCode, {
|
imgSnapshotTest(flowchartCode, {
|
||||||
look,
|
look,
|
||||||
@@ -117,7 +117,7 @@ looks.forEach((look) => {
|
|||||||
it(`with styles`, () => {
|
it(`with styles`, () => {
|
||||||
let flowchartCode = `flowchart ${direction}\n`;
|
let flowchartCode = `flowchart ${direction}\n`;
|
||||||
newShapesSet.forEach((newShape, index) => {
|
newShapesSet.forEach((newShape, index) => {
|
||||||
flowchartCode += ` n${index} --> n${index}${index}{ shape: ${newShape}, label: 'new ${newShape} shape' }\n`;
|
flowchartCode += ` n${index} --> n${index}${index}@{ shape: ${newShape}, label: 'new ${newShape} shape' }\n`;
|
||||||
flowchartCode += ` style n${index}${index} fill:#f9f,stroke:#333,stroke-width:4px \n`;
|
flowchartCode += ` style n${index}${index} fill:#f9f,stroke:#333,stroke-width:4px \n`;
|
||||||
});
|
});
|
||||||
imgSnapshotTest(flowchartCode, { look });
|
imgSnapshotTest(flowchartCode, { look });
|
||||||
@@ -127,7 +127,7 @@ looks.forEach((look) => {
|
|||||||
let flowchartCode = `flowchart ${direction}\n`;
|
let flowchartCode = `flowchart ${direction}\n`;
|
||||||
flowchartCode += ` classDef customClazz fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5\n`;
|
flowchartCode += ` classDef customClazz fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5\n`;
|
||||||
newShapesSet.forEach((newShape, index) => {
|
newShapesSet.forEach((newShape, index) => {
|
||||||
flowchartCode += ` n${index} --> n${index}${index}{ shape: ${newShape}, label: 'new ${newShape} shape' }\n`;
|
flowchartCode += ` n${index} --> n${index}${index}@{ shape: ${newShape}, label: 'new ${newShape} shape' }\n`;
|
||||||
flowchartCode += ` n${index}${index}:::customClazz\n`;
|
flowchartCode += ` n${index}${index}:::customClazz\n`;
|
||||||
});
|
});
|
||||||
imgSnapshotTest(flowchartCode, { look });
|
imgSnapshotTest(flowchartCode, { look });
|
||||||
|
@@ -1,41 +1,26 @@
|
|||||||
import { labelHelper, updateNodeBounds, getNodeClasses, createPathFromPoints } from './util.js';
|
import {
|
||||||
|
labelHelper,
|
||||||
|
updateNodeBounds,
|
||||||
|
getNodeClasses,
|
||||||
|
createPathFromPoints,
|
||||||
|
generateCirclePoints,
|
||||||
|
} from './util.js';
|
||||||
import intersect from '../intersect/index.js';
|
import intersect from '../intersect/index.js';
|
||||||
import type { Node } from '../../types.d.ts';
|
import type { Node } from '../../types.d.ts';
|
||||||
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
||||||
import rough from 'roughjs';
|
import rough from 'roughjs';
|
||||||
|
|
||||||
function createCurvedTrapezoidPathD(
|
|
||||||
x: number,
|
|
||||||
y: number,
|
|
||||||
totalWidth: number,
|
|
||||||
totalHeight: number,
|
|
||||||
radius: number
|
|
||||||
) {
|
|
||||||
const w = totalWidth - radius;
|
|
||||||
const tw = totalHeight / 4;
|
|
||||||
const points = [
|
|
||||||
{ x: x + w, y },
|
|
||||||
{ x: x + tw, y },
|
|
||||||
{ x: x, y: y + totalHeight / 2 },
|
|
||||||
{ x: x + tw, y: y + totalHeight },
|
|
||||||
{ x: x + w, y: y + totalHeight },
|
|
||||||
];
|
|
||||||
const rectPath = createPathFromPoints(points);
|
|
||||||
const arcPath = `M ${w},0 A ${totalHeight / 2} ${totalHeight / 2} 0 0 1 ${w} ${totalHeight}`;
|
|
||||||
const finalPath = `${rectPath} ${arcPath}`.replace('Z', '');
|
|
||||||
return finalPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const curvedTrapezoid = async (parent: SVGAElement, node: Node) => {
|
export const curvedTrapezoid = async (parent: SVGAElement, node: Node) => {
|
||||||
const { labelStyles, nodeStyles } = styles2String(node);
|
const { labelStyles, nodeStyles } = styles2String(node);
|
||||||
node.labelStyle = labelStyles;
|
node.labelStyle = labelStyles;
|
||||||
const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node));
|
const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node));
|
||||||
const widthMultiplier = bbox.width < 15 ? 2 : 1.25;
|
const minWidth = 80,
|
||||||
|
minHeight = 20;
|
||||||
const nodePadding = node.padding ?? 0;
|
const nodePadding = node.padding ?? 0;
|
||||||
const labelPaddingX = node.look === 'neo' ? nodePadding * 2 : nodePadding;
|
const labelPaddingX = node.look === 'neo' ? nodePadding * 2 : nodePadding;
|
||||||
const labelPaddingY = node.look === 'neo' ? nodePadding * 1 : nodePadding;
|
const labelPaddingY = node.look === 'neo' ? nodePadding * 1 : nodePadding;
|
||||||
const w = Math.max((bbox.width + labelPaddingX) * widthMultiplier, node?.width ?? 500);
|
const w = Math.max(minWidth, (bbox.width + (labelPaddingX ?? 0) * 2) * 1.25, node?.width ?? 0);
|
||||||
const h = Math.max(bbox.height + labelPaddingY, node?.height ?? 100);
|
const h = Math.max(minHeight, bbox.height + (labelPaddingY ?? 0) * 2, node?.height ?? 0);
|
||||||
const radius = h / 2;
|
const radius = h / 2;
|
||||||
|
|
||||||
const { cssStyles } = node;
|
const { cssStyles } = node;
|
||||||
@@ -48,7 +33,21 @@ export const curvedTrapezoid = async (parent: SVGAElement, node: Node) => {
|
|||||||
options.fillStyle = 'solid';
|
options.fillStyle = 'solid';
|
||||||
}
|
}
|
||||||
|
|
||||||
const pathData = createCurvedTrapezoidPathD(0, 0, w, h, radius);
|
const totalWidth = w,
|
||||||
|
totalHeight = h;
|
||||||
|
const rw = totalWidth - radius;
|
||||||
|
const tw = totalHeight / 4;
|
||||||
|
|
||||||
|
const points = [
|
||||||
|
{ x: rw, y: 0 },
|
||||||
|
{ x: tw, y: 0 },
|
||||||
|
{ x: 0, y: totalHeight / 2 },
|
||||||
|
{ x: tw, y: totalHeight },
|
||||||
|
{ x: rw, y: totalHeight },
|
||||||
|
...generateCirclePoints(-rw, -totalHeight / 2, radius, 50, 270, 90),
|
||||||
|
];
|
||||||
|
|
||||||
|
const pathData = createPathFromPoints(points);
|
||||||
const shapeNode = rc.path(pathData, options);
|
const shapeNode = rc.path(pathData, options);
|
||||||
|
|
||||||
const polygon = shapeSvg.insert(() => shapeNode, ':first-child');
|
const polygon = shapeSvg.insert(() => shapeNode, ':first-child');
|
||||||
@@ -67,29 +66,7 @@ export const curvedTrapezoid = async (parent: SVGAElement, node: Node) => {
|
|||||||
updateNodeBounds(node, polygon);
|
updateNodeBounds(node, polygon);
|
||||||
|
|
||||||
node.intersect = function (point) {
|
node.intersect = function (point) {
|
||||||
const pos = intersect.rect(node, point);
|
const pos = intersect.polygon(node, points, point);
|
||||||
const rx = h / 2;
|
|
||||||
const ry = h / 2;
|
|
||||||
const y = pos.y - (node.y ?? 0);
|
|
||||||
|
|
||||||
if (
|
|
||||||
ry != 0 &&
|
|
||||||
(Math.abs(y) < (node.height ?? 0) / 2 ||
|
|
||||||
(Math.abs(y) == (node.height ?? 0) / 2 &&
|
|
||||||
Math.abs(pos.x - (node.x ?? 0)) > (node.width ?? 0) / 2 - rx))
|
|
||||||
) {
|
|
||||||
let x = rx * rx * (1 - (y * y) / (ry * ry));
|
|
||||||
if (x != 0) {
|
|
||||||
x = Math.sqrt(x);
|
|
||||||
}
|
|
||||||
x = rx - x;
|
|
||||||
if (point.x - (node.x ?? 0) > 0) {
|
|
||||||
x = -x;
|
|
||||||
}
|
|
||||||
|
|
||||||
pos.x += x;
|
|
||||||
}
|
|
||||||
|
|
||||||
return pos;
|
return pos;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -55,13 +55,14 @@ export const cylinder = async (parent: SVGAElement, node: Node) => {
|
|||||||
const { labelStyles, nodeStyles } = styles2String(node);
|
const { labelStyles, nodeStyles } = styles2String(node);
|
||||||
node.labelStyle = labelStyles;
|
node.labelStyle = labelStyles;
|
||||||
const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node));
|
const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node));
|
||||||
|
|
||||||
const nodePadding = node.padding ?? 0;
|
const nodePadding = node.padding ?? 0;
|
||||||
const labelPaddingX = node.look === 'neo' ? nodePadding * 2 : nodePadding;
|
const labelPaddingX = node.look === 'neo' ? nodePadding * 2 : nodePadding;
|
||||||
const labelPaddingY = node.look === 'neo' ? nodePadding * 1 : nodePadding;
|
const labelPaddingY = node.look === 'neo' ? nodePadding * 1 : nodePadding;
|
||||||
const w = bbox.width + labelPaddingY;
|
const w = Math.max(bbox.width + labelPaddingY, node.width ?? 0);
|
||||||
const rx = w / 2;
|
const rx = w / 2;
|
||||||
const ry = rx / (2.5 + w / 50);
|
const ry = rx / (2.5 + w / 50);
|
||||||
const h = bbox.height + ry + labelPaddingX;
|
const h = Math.max(bbox.height + ry + labelPaddingX, node.height ?? 0);
|
||||||
|
|
||||||
let cylinder: d3.Selection<SVGPathElement | SVGGElement, unknown, null, undefined>;
|
let cylinder: d3.Selection<SVGPathElement | SVGGElement, unknown, null, undefined>;
|
||||||
const { cssStyles } = node;
|
const { cssStyles } = node;
|
||||||
@@ -109,7 +110,7 @@ export const cylinder = async (parent: SVGAElement, node: Node) => {
|
|||||||
|
|
||||||
label.attr(
|
label.attr(
|
||||||
'transform',
|
'transform',
|
||||||
`translate(${-bbox.width / 2 - (bbox.x - (bbox.left ?? 0))}, ${h / 2 - bbox.height - (bbox.y - (bbox.top ?? 0))})`
|
`translate(${-(bbox.width / 2) - (bbox.x - (bbox.left ?? 0))}, ${-(bbox.height / 2) + (node.padding ?? 0) / 1.5 - (bbox.y - (bbox.top ?? 0))})`
|
||||||
);
|
);
|
||||||
|
|
||||||
node.intersect = function (point) {
|
node.intersect = function (point) {
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { labelHelper, updateNodeBounds, getNodeClasses, createPathFromPoints } from './util.js';
|
import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js';
|
||||||
import intersect from '../intersect/index.js';
|
import intersect from '../intersect/index.js';
|
||||||
import type { Node } from '../../types.d.ts';
|
import type { Node } from '../../types.d.ts';
|
||||||
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
||||||
@@ -25,23 +25,22 @@ export const dividedRectangle = async (parent: SVGAElement, node: Node) => {
|
|||||||
options.fillStyle = 'solid';
|
options.fillStyle = 'solid';
|
||||||
}
|
}
|
||||||
|
|
||||||
const outerPathPoints = [
|
const pts = [
|
||||||
{ x, y: y },
|
{ x, y: y + rectOffset },
|
||||||
{ x, y: -y },
|
{ x: -x, y: y + rectOffset },
|
||||||
{ x: -x, y: -y },
|
{ x: -x, y: -y },
|
||||||
{ x: -x, y: y },
|
{ x, y: -y },
|
||||||
];
|
{ x, y },
|
||||||
const innerPathPoints = [
|
{ x: -x, y },
|
||||||
{ x: x, y: y + rectOffset },
|
|
||||||
{ x: -x, y: y + rectOffset },
|
{ x: -x, y: y + rectOffset },
|
||||||
];
|
];
|
||||||
const outerPathData = createPathFromPoints(outerPathPoints);
|
|
||||||
const outerNode = rc.path(outerPathData, options);
|
|
||||||
const innerPathData = createPathFromPoints(innerPathPoints);
|
|
||||||
const innerNode = rc.path(innerPathData, options);
|
|
||||||
|
|
||||||
const polygon = shapeSvg.insert(() => outerNode, ':first-child');
|
const poly = rc.polygon(
|
||||||
polygon.insert(() => innerNode);
|
pts.map((p) => [p.x, p.y]),
|
||||||
|
options
|
||||||
|
);
|
||||||
|
|
||||||
|
const polygon = shapeSvg.insert(() => poly, ':first-child');
|
||||||
polygon.attr('class', 'basic label-container');
|
polygon.attr('class', 'basic label-container');
|
||||||
|
|
||||||
if (cssStyles && node.look !== 'handDrawn') {
|
if (cssStyles && node.look !== 'handDrawn') {
|
||||||
@@ -60,7 +59,7 @@ export const dividedRectangle = async (parent: SVGAElement, node: Node) => {
|
|||||||
updateNodeBounds(node, polygon);
|
updateNodeBounds(node, polygon);
|
||||||
|
|
||||||
node.intersect = function (point) {
|
node.intersect = function (point) {
|
||||||
const pos = intersect.polygon(node, outerPathPoints, point);
|
const pos = intersect.rect(node, point);
|
||||||
return pos;
|
return pos;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -1,27 +1,25 @@
|
|||||||
import { log } from '../../../logger.js';
|
import { log } from '../../../logger.js';
|
||||||
import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js';
|
import {
|
||||||
|
labelHelper,
|
||||||
|
updateNodeBounds,
|
||||||
|
getNodeClasses,
|
||||||
|
createPathFromPoints,
|
||||||
|
generateCirclePoints,
|
||||||
|
} from './util.js';
|
||||||
import intersect from '../intersect/index.js';
|
import intersect from '../intersect/index.js';
|
||||||
import type { Node } from '../../types.d.ts';
|
import type { Node } from '../../types.d.ts';
|
||||||
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
||||||
import rough from 'roughjs';
|
import rough from 'roughjs';
|
||||||
|
|
||||||
function createHalfRoundedRectShapePathD(h: number, w: number, rx: number, ry: number) {
|
|
||||||
return ` M ${w} ${h}
|
|
||||||
L ${0} ${h}
|
|
||||||
L ${0} ${0}
|
|
||||||
L ${w} ${0}
|
|
||||||
A ${rx} ${ry} 0 0 1 ${w} ${h}Z`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const halfRoundedRectangle = async (parent: SVGAElement, node: Node) => {
|
export const halfRoundedRectangle = async (parent: SVGAElement, node: Node) => {
|
||||||
const { labelStyles, nodeStyles } = styles2String(node);
|
const { labelStyles, nodeStyles } = styles2String(node);
|
||||||
node.labelStyle = labelStyles;
|
node.labelStyle = labelStyles;
|
||||||
const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node));
|
const minWidth = 80,
|
||||||
const w = Math.max(bbox.width + (node.padding ?? 0) * 2, node?.width ?? 0);
|
minHeight = 50;
|
||||||
const h = Math.max(bbox.height + (node.padding ?? 0) * 2, node?.height ?? 0);
|
const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node));
|
||||||
|
const w = Math.max(minWidth, bbox.width + (node.padding ?? 0) * 2, node?.width ?? 0);
|
||||||
|
const h = Math.max(minHeight, bbox.height + (node.padding ?? 0) * 2, node?.height ?? 0);
|
||||||
const radius = h / 2;
|
const radius = h / 2;
|
||||||
const rx = radius;
|
|
||||||
const ry = radius;
|
|
||||||
const { cssStyles } = node;
|
const { cssStyles } = node;
|
||||||
|
|
||||||
// @ts-ignore - rough is not typed
|
// @ts-ignore - rough is not typed
|
||||||
@@ -33,7 +31,15 @@ export const halfRoundedRectangle = async (parent: SVGAElement, node: Node) => {
|
|||||||
options.fillStyle = 'solid';
|
options.fillStyle = 'solid';
|
||||||
}
|
}
|
||||||
|
|
||||||
const pathData = createHalfRoundedRectShapePathD(h, w, rx, ry);
|
const points = [
|
||||||
|
{ x: -w / 2, y: -h / 2 },
|
||||||
|
{ x: w / 2 - radius, y: -h / 2 },
|
||||||
|
...generateCirclePoints(-w / 2 + radius, 0, radius, 50, 90, 270),
|
||||||
|
{ x: w / 2 - radius, y: h / 2 },
|
||||||
|
{ x: -w / 2, y: h / 2 },
|
||||||
|
];
|
||||||
|
|
||||||
|
const pathData = createPathFromPoints(points);
|
||||||
const shapeNode = rc.path(pathData, options);
|
const shapeNode = rc.path(pathData, options);
|
||||||
const polygon = shapeSvg.insert(() => shapeNode, ':first-child');
|
const polygon = shapeSvg.insert(() => shapeNode, ':first-child');
|
||||||
polygon.attr('class', 'basic label-container');
|
polygon.attr('class', 'basic label-container');
|
||||||
@@ -46,38 +52,17 @@ export const halfRoundedRectangle = async (parent: SVGAElement, node: Node) => {
|
|||||||
polygon.selectChildren('path').attr('style', nodeStyles);
|
polygon.selectChildren('path').attr('style', nodeStyles);
|
||||||
}
|
}
|
||||||
|
|
||||||
polygon.attr('transform', `translate(${-w / 2 - h / 4}, ${-h / 2})`);
|
// label.attr(
|
||||||
label.attr(
|
// 'transform',
|
||||||
'transform',
|
// `translate(${-w / 2 + (node.padding ?? 0) - (bbox.x - (bbox.left ?? 0))}, ${-h / 2 + (node.padding ?? 0) - (bbox.y - (bbox.top ?? 0))})`
|
||||||
`translate(${-w / 2 + (node.padding ?? 0) - (bbox.x - (bbox.left ?? 0))}, ${-h / 2 + (node.padding ?? 0) - (bbox.y - (bbox.top ?? 0))})`
|
// );
|
||||||
);
|
|
||||||
|
|
||||||
updateNodeBounds(node, polygon);
|
updateNodeBounds(node, polygon);
|
||||||
|
|
||||||
node.intersect = function (point) {
|
node.intersect = function (point) {
|
||||||
log.info('Pill intersect', node, { radius, point });
|
log.info('Pill intersect', node, { radius, point });
|
||||||
const pos = intersect.rect(node, point);
|
const pos = intersect.polygon(node, points, point);
|
||||||
const y = pos.y - (node.y ?? 0);
|
|
||||||
if (
|
|
||||||
ry != 0 &&
|
|
||||||
pos.x > (node.x ?? 0) &&
|
|
||||||
(Math.abs(y) < (node.height ?? 0) / 2 ||
|
|
||||||
(Math.abs(y) == (node.height ?? 0) / 2 &&
|
|
||||||
Math.abs(pos.x - (node.x ?? 0)) > (node.width ?? 0) / 2 - rx))
|
|
||||||
) {
|
|
||||||
let x = rx * rx * (1 - (y * y) / (ry * ry));
|
|
||||||
if (x != 0) {
|
|
||||||
x = Math.sqrt(x);
|
|
||||||
}
|
|
||||||
x = rx - x;
|
|
||||||
if (point.x - (node.x ?? 0) > 0) {
|
|
||||||
x = -x;
|
|
||||||
}
|
|
||||||
|
|
||||||
pos.x += x;
|
|
||||||
}
|
|
||||||
return pos;
|
return pos;
|
||||||
};
|
};
|
||||||
|
|
||||||
return shapeSvg;
|
return shapeSvg;
|
||||||
};
|
};
|
||||||
|
@@ -49,7 +49,7 @@ export const icon = async (
|
|||||||
|
|
||||||
const outerNode = rc.rectangle(-outerWidth / 2, -outerHeight / 2, outerWidth, outerHeight, {
|
const outerNode = rc.rectangle(-outerWidth / 2, -outerHeight / 2, outerWidth, outerHeight, {
|
||||||
...options,
|
...options,
|
||||||
fill: 'none',
|
fill: 'transparent',
|
||||||
stroke: 'none',
|
stroke: 'none',
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -68,9 +68,9 @@ export const icon = async (
|
|||||||
const iconY = iconBBox.y;
|
const iconY = iconBBox.y;
|
||||||
iconElem.attr(
|
iconElem.attr(
|
||||||
'transform',
|
'transform',
|
||||||
`translate(${-iconWidth / 2 - iconX},${topLabel ? outerHeight / 2 - iconHeight - iconY : -outerHeight / 2 - iconY})`
|
`translate(${-iconWidth / 2 - iconX},${topLabel ? outerHeight / 2 - iconHeight - iconY : outerHeight / 2 - iconHeight - iconY - bbox.height - labelPadding})`
|
||||||
);
|
);
|
||||||
iconElem.selectAll('path').attr('fill', stylesMap.get('stroke') || nodeBorder);
|
iconElem.selectAll('path').attr('fill', stylesMap.get('stroke') ?? nodeBorder);
|
||||||
iconElem.attr('class', 'icon');
|
iconElem.attr('class', 'icon');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -30,7 +30,7 @@ export const iconCircle = async (
|
|||||||
const { stylesMap } = compileStyles(node);
|
const { stylesMap } = compileStyles(node);
|
||||||
// @ts-ignore - rough is not typed
|
// @ts-ignore - rough is not typed
|
||||||
const rc = rough.svg(shapeSvg);
|
const rc = rough.svg(shapeSvg);
|
||||||
const options = userNodeOverrides(node, { stroke: stylesMap.get('fill') || mainBkg });
|
const options = userNodeOverrides(node, { stroke: stylesMap.get('fill') ?? mainBkg });
|
||||||
|
|
||||||
if (node.look !== 'handDrawn') {
|
if (node.look !== 'handDrawn') {
|
||||||
options.roughness = 0;
|
options.roughness = 0;
|
||||||
@@ -57,7 +57,7 @@ export const iconCircle = async (
|
|||||||
|
|
||||||
const outerNode = rc.rectangle(-outerWidth / 2, -outerHeight / 2, outerWidth, outerHeight, {
|
const outerNode = rc.rectangle(-outerWidth / 2, -outerHeight / 2, outerWidth, outerHeight, {
|
||||||
...options,
|
...options,
|
||||||
fill: 'none',
|
fill: 'transparent',
|
||||||
stroke: 'none',
|
stroke: 'none',
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -65,9 +65,9 @@ export const iconCircle = async (
|
|||||||
const outerShape = shapeSvg.insert(() => outerNode);
|
const outerShape = shapeSvg.insert(() => outerNode);
|
||||||
iconElem.attr(
|
iconElem.attr(
|
||||||
'transform',
|
'transform',
|
||||||
`translate(${-iconWidth / 2 - iconX},${topLabel ? diameter / 2 - iconHeight - padding + bbox.height / 2 - iconY + labelPadding / 2 : -diameter / 2 + padding - bbox.height / 2 - labelPadding / 2 - iconY})`
|
`translate(${-iconWidth / 2 - iconX},${topLabel ? outerHeight / 2 - iconHeight - iconY - padding : outerHeight / 2 - iconHeight - iconY - padding - bbox.height - labelPadding})`
|
||||||
);
|
);
|
||||||
iconElem.selectAll('path').attr('fill', stylesMap.get('stroke') || nodeBorder);
|
iconElem.selectAll('path').attr('fill', stylesMap.get('stroke') ?? nodeBorder);
|
||||||
iconElem.attr('class', 'icon');
|
iconElem.attr('class', 'icon');
|
||||||
label.attr(
|
label.attr(
|
||||||
'transform',
|
'transform',
|
||||||
|
@@ -42,7 +42,7 @@ export const iconRounded = async (
|
|||||||
|
|
||||||
// @ts-ignore - rough is not typed
|
// @ts-ignore - rough is not typed
|
||||||
const rc = rough.svg(shapeSvg);
|
const rc = rough.svg(shapeSvg);
|
||||||
const options = userNodeOverrides(node, { stroke: stylesMap.get('fill') || mainBkg });
|
const options = userNodeOverrides(node, { stroke: stylesMap.get('fill') ?? mainBkg });
|
||||||
|
|
||||||
if (node.look !== 'handDrawn') {
|
if (node.look !== 'handDrawn') {
|
||||||
options.roughness = 0;
|
options.roughness = 0;
|
||||||
@@ -56,7 +56,7 @@ export const iconRounded = async (
|
|||||||
|
|
||||||
const outerNode = rc.rectangle(-outerWidth / 2, -outerHeight / 2, outerWidth, outerHeight, {
|
const outerNode = rc.rectangle(-outerWidth / 2, -outerHeight / 2, outerWidth, outerHeight, {
|
||||||
...options,
|
...options,
|
||||||
fill: 'none',
|
fill: 'transparent',
|
||||||
stroke: 'none',
|
stroke: 'none',
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -75,9 +75,9 @@ export const iconRounded = async (
|
|||||||
const iconY = iconBBox.y;
|
const iconY = iconBBox.y;
|
||||||
iconElem.attr(
|
iconElem.attr(
|
||||||
'transform',
|
'transform',
|
||||||
`translate(${-iconWidth / 2 - iconX},${topLabel ? outerHeight / 2 - iconHeight - padding - iconY : -outerHeight / 2 + padding - iconY})`
|
`translate(${-iconWidth / 2 - iconX},${topLabel ? outerHeight / 2 - iconHeight - iconY - halfPadding : outerHeight / 2 - iconHeight - iconY - halfPadding - bbox.height - labelPadding})`
|
||||||
);
|
);
|
||||||
iconElem.selectAll('path').attr('fill', stylesMap.get('stroke') || nodeBorder);
|
iconElem.selectAll('path').attr('fill', stylesMap.get('stroke') ?? nodeBorder);
|
||||||
iconElem.attr('class', 'icon');
|
iconElem.attr('class', 'icon');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -41,7 +41,7 @@ export const iconSquare = async (
|
|||||||
|
|
||||||
// @ts-ignore - rough is not typed
|
// @ts-ignore - rough is not typed
|
||||||
const rc = rough.svg(shapeSvg);
|
const rc = rough.svg(shapeSvg);
|
||||||
const options = userNodeOverrides(node, { stroke: stylesMap.get('fill') || mainBkg });
|
const options = userNodeOverrides(node, { stroke: stylesMap.get('fill') ?? mainBkg });
|
||||||
|
|
||||||
if (node.look !== 'handDrawn') {
|
if (node.look !== 'handDrawn') {
|
||||||
options.roughness = 0;
|
options.roughness = 0;
|
||||||
@@ -55,7 +55,7 @@ export const iconSquare = async (
|
|||||||
|
|
||||||
const outerNode = rc.rectangle(-outerWidth / 2, -outerHeight / 2, outerWidth, outerHeight, {
|
const outerNode = rc.rectangle(-outerWidth / 2, -outerHeight / 2, outerWidth, outerHeight, {
|
||||||
...options,
|
...options,
|
||||||
fill: 'none',
|
fill: 'transparent',
|
||||||
stroke: 'none',
|
stroke: 'none',
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -74,9 +74,9 @@ export const iconSquare = async (
|
|||||||
const iconY = iconBBox.y;
|
const iconY = iconBBox.y;
|
||||||
iconElem.attr(
|
iconElem.attr(
|
||||||
'transform',
|
'transform',
|
||||||
`translate(${-iconWidth / 2 - iconX},${topLabel ? outerHeight / 2 - iconHeight - padding - iconY : -outerHeight / 2 + padding - iconY})`
|
`translate(${-iconWidth / 2 - iconX},${topLabel ? outerHeight / 2 - iconHeight - iconY - halfPadding : outerHeight / 2 - iconHeight - iconY - halfPadding - bbox.height - labelPadding})`
|
||||||
);
|
);
|
||||||
iconElem.selectAll('path').attr('fill', stylesMap.get('stroke') || nodeBorder);
|
iconElem.selectAll('path').attr('fill', stylesMap.get('stroke') ?? nodeBorder);
|
||||||
iconElem.attr('class', 'icon');
|
iconElem.attr('class', 'icon');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -3,7 +3,6 @@ import {
|
|||||||
updateNodeBounds,
|
updateNodeBounds,
|
||||||
getNodeClasses,
|
getNodeClasses,
|
||||||
generateFullSineWavePoints,
|
generateFullSineWavePoints,
|
||||||
createPathFromPoints,
|
|
||||||
} from './util.js';
|
} from './util.js';
|
||||||
import intersect from '../intersect/index.js';
|
import intersect from '../intersect/index.js';
|
||||||
import type { Node } from '../../types.d.ts';
|
import type { Node } from '../../types.d.ts';
|
||||||
@@ -30,6 +29,7 @@ export const linedWaveEdgedRect = async (parent: SVGAElement, node: Node) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const points = [
|
const points = [
|
||||||
|
{ x: -w / 2 - (w / 2) * 0.1, y: -finalH / 2 },
|
||||||
{ x: -w / 2 - (w / 2) * 0.1, y: finalH / 2 },
|
{ x: -w / 2 - (w / 2) * 0.1, y: finalH / 2 },
|
||||||
...generateFullSineWavePoints(
|
...generateFullSineWavePoints(
|
||||||
-w / 2 - (w / 2) * 0.1,
|
-w / 2 - (w / 2) * 0.1,
|
||||||
@@ -41,24 +41,17 @@ export const linedWaveEdgedRect = async (parent: SVGAElement, node: Node) => {
|
|||||||
),
|
),
|
||||||
{ x: w / 2 + (w / 2) * 0.1, y: -finalH / 2 },
|
{ x: w / 2 + (w / 2) * 0.1, y: -finalH / 2 },
|
||||||
{ x: -w / 2 - (w / 2) * 0.1, y: -finalH / 2 },
|
{ x: -w / 2 - (w / 2) * 0.1, y: -finalH / 2 },
|
||||||
|
{ x: -w / 2, y: -finalH / 2 },
|
||||||
|
{ x: -w / 2, y: (finalH / 2) * 1.1 },
|
||||||
|
{ x: -w / 2, y: -finalH / 2 },
|
||||||
];
|
];
|
||||||
|
|
||||||
const x = -w / 2;
|
const poly = rc.polygon(
|
||||||
const y = -finalH / 2;
|
points.map((p) => [p.x, p.y]),
|
||||||
|
options
|
||||||
|
);
|
||||||
|
|
||||||
const innerPathPoints = [
|
const waveEdgeRect = shapeSvg.insert(() => poly, ':first-child');
|
||||||
{ x: x, y: y },
|
|
||||||
{ x: x, y: -y * 1.1 },
|
|
||||||
];
|
|
||||||
|
|
||||||
const waveEdgeRectPath = createPathFromPoints(points);
|
|
||||||
const waveEdgeRectNode = rc.path(waveEdgeRectPath, options);
|
|
||||||
|
|
||||||
const innerSecondPath = createPathFromPoints(innerPathPoints);
|
|
||||||
const innerSecondNode = rc.path(innerSecondPath, options);
|
|
||||||
|
|
||||||
const waveEdgeRect = shapeSvg.insert(() => innerSecondNode, ':first-child');
|
|
||||||
waveEdgeRect.insert(() => waveEdgeRectNode, ':first-child');
|
|
||||||
|
|
||||||
waveEdgeRect.attr('class', 'basic label-container');
|
waveEdgeRect.attr('class', 'basic label-container');
|
||||||
|
|
||||||
|
@@ -55,9 +55,10 @@ export const rect_left_inv_arrow = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
polygon.attr('transform', `translate(${-notch / 2},0)`);
|
polygon.attr('transform', `translate(${-notch / 2},0)`);
|
||||||
|
|
||||||
label.attr(
|
label.attr(
|
||||||
'transform',
|
'transform',
|
||||||
`translate(${-w / 2 + -notch / 2 + (node.padding ?? 0) / 2 - (bbox.x - (bbox.left ?? 0))},${-h / 2 + (node.padding ?? 0) / 2 - (bbox.y - (bbox.top ?? 0))})`
|
`translate(${-notch / 2 - bbox.width / 2 - (bbox.x - (bbox.left ?? 0))}, ${-(bbox.height / 2) - (bbox.y - (bbox.top ?? 0))})`
|
||||||
);
|
);
|
||||||
updateNodeBounds(node, polygon);
|
updateNodeBounds(node, polygon);
|
||||||
|
|
||||||
|
@@ -163,3 +163,33 @@ export function generateFullSineWavePoints(x1, y1, x2, y2, amplitude, numCycles)
|
|||||||
|
|
||||||
return points;
|
return points;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function generateCirclePoints(
|
||||||
|
centerX, // x-coordinate of center of circle
|
||||||
|
centerY, // x-coordinate of center of circle
|
||||||
|
radius, // radius of circle
|
||||||
|
numPoints, // total points required
|
||||||
|
startAngle, // angle where arc will start
|
||||||
|
endAngle // angle where arc will end
|
||||||
|
) {
|
||||||
|
const points = [];
|
||||||
|
|
||||||
|
// Convert angles to radians
|
||||||
|
const startAngleRad = (startAngle * Math.PI) / 180;
|
||||||
|
const endAngleRad = (endAngle * Math.PI) / 180;
|
||||||
|
|
||||||
|
// Calculate the angle range in radians
|
||||||
|
const angleRange = endAngleRad - startAngleRad;
|
||||||
|
|
||||||
|
// Calculate the angle step
|
||||||
|
const angleStep = angleRange / (numPoints - 1);
|
||||||
|
|
||||||
|
for (let i = 0; i < numPoints; i++) {
|
||||||
|
const angle = startAngleRad + i * angleStep;
|
||||||
|
const x = centerX + radius * Math.cos(angle);
|
||||||
|
const y = centerY + radius * Math.sin(angle);
|
||||||
|
points.push({ x: -x, y: -y });
|
||||||
|
}
|
||||||
|
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { labelHelper, getNodeClasses, updateNodeBounds, createPathFromPoints } from './util.js';
|
import { labelHelper, getNodeClasses, updateNodeBounds } from './util.js';
|
||||||
import type { Node } from '../../types.d.ts';
|
import type { Node } from '../../types.d.ts';
|
||||||
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
||||||
import rough from 'roughjs';
|
import rough from 'roughjs';
|
||||||
@@ -26,31 +26,18 @@ export const windowPane = async (parent: SVGAElement, node: Node) => {
|
|||||||
{ x: x + w, y: y - rectOffset },
|
{ x: x + w, y: y - rectOffset },
|
||||||
];
|
];
|
||||||
|
|
||||||
const innerPathPoints = [
|
const path = `M${x - rectOffset},${y - rectOffset} L${x + w},${y - rectOffset} L${x + w},${y + h} L${x - rectOffset},${y + h} L${x - rectOffset},${y - rectOffset}
|
||||||
{ x: x - rectOffset, y },
|
M${x - rectOffset},${y} L${x + w},${y}
|
||||||
{ x: x + w, y },
|
M${x},${y - rectOffset} L${x},${y + h}`;
|
||||||
];
|
|
||||||
|
|
||||||
const innerSecondPathPoints = [
|
|
||||||
{ x, y: y - rectOffset },
|
|
||||||
{ x, y: y + h },
|
|
||||||
];
|
|
||||||
|
|
||||||
if (node.look !== 'handDrawn') {
|
if (node.look !== 'handDrawn') {
|
||||||
options.roughness = 0;
|
options.roughness = 0;
|
||||||
options.fillStyle = 'solid';
|
options.fillStyle = 'solid';
|
||||||
}
|
}
|
||||||
|
|
||||||
const outerPath = createPathFromPoints(outerPathPoints);
|
const no = rc.path(path, options);
|
||||||
const outerNode = rc.path(outerPath, options);
|
|
||||||
const innerPath = createPathFromPoints(innerPathPoints);
|
|
||||||
const innerNode = rc.path(innerPath, options);
|
|
||||||
const innerSecondPath = createPathFromPoints(innerSecondPathPoints);
|
|
||||||
const innerSecondNode = rc.path(innerSecondPath, options);
|
|
||||||
|
|
||||||
const windowPane = shapeSvg.insert(() => innerNode, ':first-child');
|
const windowPane = shapeSvg.insert(() => no, ':first-child');
|
||||||
windowPane.insert(() => innerSecondNode, ':first-child');
|
|
||||||
windowPane.insert(() => outerNode, ':first-child');
|
|
||||||
windowPane.attr('transform', `translate(${rectOffset / 2}, ${rectOffset / 2})`);
|
windowPane.attr('transform', `translate(${rectOffset / 2}, ${rectOffset / 2})`);
|
||||||
|
|
||||||
windowPane.attr('class', 'basic label-container');
|
windowPane.attr('class', 'basic label-container');
|
||||||
|
Reference in New Issue
Block a user