mirror of
				https://github.com/mermaid-js/mermaid.git
				synced 2025-11-04 04:44:08 +01:00 
			
		
		
		
	Updated waveEdge and wave rectangle
This commit is contained in:
		@@ -140,3 +140,25 @@ export function createPathFromPoints(points) {
 | 
			
		||||
  pointStrings.push('Z');
 | 
			
		||||
  return pointStrings.join(' ');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function generateFullSineWavePoints(x1, y1, x2, y2, amplitude, numCycles) {
 | 
			
		||||
  const points = [];
 | 
			
		||||
  const steps = 50; // Number of segments to create a smooth curve
 | 
			
		||||
  const deltaX = x2 - x1;
 | 
			
		||||
  const deltaY = y2 - y1;
 | 
			
		||||
  const cycleLength = deltaX / numCycles;
 | 
			
		||||
 | 
			
		||||
  // Calculate frequency and phase shift
 | 
			
		||||
  const frequency = (2 * Math.PI) / cycleLength;
 | 
			
		||||
  const midY = y1 + deltaY / 2;
 | 
			
		||||
 | 
			
		||||
  for (let i = 0; i <= steps; i++) {
 | 
			
		||||
    const t = i / steps;
 | 
			
		||||
    const x = x1 + t * deltaX;
 | 
			
		||||
    const y = midY + amplitude * Math.sin(frequency * (x - x1));
 | 
			
		||||
 | 
			
		||||
    points.push({ x, y });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return points;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,52 +1,25 @@
 | 
			
		||||
import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js';
 | 
			
		||||
import {
 | 
			
		||||
  labelHelper,
 | 
			
		||||
  updateNodeBounds,
 | 
			
		||||
  getNodeClasses,
 | 
			
		||||
  generateFullSineWavePoints,
 | 
			
		||||
  createPathFromPoints,
 | 
			
		||||
} from './util.js';
 | 
			
		||||
import intersect from '../intersect/index.js';
 | 
			
		||||
import type { Node } from '$root/rendering-util/types.d.ts';
 | 
			
		||||
import rough from 'roughjs';
 | 
			
		||||
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
 | 
			
		||||
 | 
			
		||||
export function createWaveEdgedRectanglePathD(width: number, height: number) {
 | 
			
		||||
  // Calculate control points
 | 
			
		||||
  const rightX = width;
 | 
			
		||||
  const midX = width / 2;
 | 
			
		||||
  const controlY1 = height * 0.8;
 | 
			
		||||
  const controlY2 = height * 1.15;
 | 
			
		||||
  const endY = height * 0.94;
 | 
			
		||||
 | 
			
		||||
  // Construct the path
 | 
			
		||||
  const path = `M0 0 
 | 
			
		||||
                H${rightX} 
 | 
			
		||||
                V${controlY1}
 | 
			
		||||
                C${midX} ${controlY1}, ${midX} ${controlY2}, 0 ${endY}
 | 
			
		||||
                Z`;
 | 
			
		||||
 | 
			
		||||
  return path;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const waveEdgedRectangle = async (parent: SVGAElement, node: Node) => {
 | 
			
		||||
  const { labelStyles, nodeStyles } = styles2String(node);
 | 
			
		||||
  node.labelStyle = labelStyles;
 | 
			
		||||
  const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node));
 | 
			
		||||
  const w = bbox.width + node.padding;
 | 
			
		||||
  const h = bbox.height + node.padding + 20;
 | 
			
		||||
 | 
			
		||||
  const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node));
 | 
			
		||||
  const w = Math.max(bbox.width + (node.padding ?? 0) * 2, node?.width ?? 0);
 | 
			
		||||
  const h = Math.max(bbox.height + (node.padding ?? 0) * 2, node?.height ?? 0);
 | 
			
		||||
  const waveAmplitude = h / 4;
 | 
			
		||||
  const finalH = h + waveAmplitude;
 | 
			
		||||
  const { cssStyles } = node;
 | 
			
		||||
 | 
			
		||||
  const rightX = w;
 | 
			
		||||
  const midX = w / 2;
 | 
			
		||||
  const controlY1 = h * 0.8;
 | 
			
		||||
  const controlY2 = h * 1.15;
 | 
			
		||||
  const endY = h * 0.94;
 | 
			
		||||
 | 
			
		||||
  const points = [
 | 
			
		||||
    { x: 0, y: 0 },
 | 
			
		||||
    { x: rightX, y: 0 },
 | 
			
		||||
    { x: rightX, y: controlY1 },
 | 
			
		||||
    { x: midX, y: controlY1 },
 | 
			
		||||
    { x: midX, y: controlY2 * 0.8 },
 | 
			
		||||
    { x: 0, y: endY },
 | 
			
		||||
  ];
 | 
			
		||||
  const pathData = createWaveEdgedRectanglePathD(w, h);
 | 
			
		||||
 | 
			
		||||
  // @ts-ignore - rough is not typed
 | 
			
		||||
  const rc = rough.svg(shapeSvg);
 | 
			
		||||
  const options = userNodeOverrides(node, {});
 | 
			
		||||
@@ -55,22 +28,36 @@ export const waveEdgedRectangle = async (parent: SVGAElement, node: Node) => {
 | 
			
		||||
    options.roughness = 0;
 | 
			
		||||
    options.fillStyle = 'solid';
 | 
			
		||||
  }
 | 
			
		||||
  const shapeNode = rc.path(pathData, options);
 | 
			
		||||
  const shape = shapeSvg.insert(() => shapeNode, ':first-child');
 | 
			
		||||
  shape.attr('class', 'basic label-container');
 | 
			
		||||
 | 
			
		||||
  if (cssStyles) {
 | 
			
		||||
    shape.attr('style', cssStyles);
 | 
			
		||||
  const points = [
 | 
			
		||||
    { x: -w / 2, y: finalH / 2 },
 | 
			
		||||
    ...generateFullSineWavePoints(-w / 2, finalH / 2, w / 2, finalH / 2, waveAmplitude, 0.8),
 | 
			
		||||
    { x: w / 2, y: -finalH / 2 },
 | 
			
		||||
    { x: -w / 2, y: -finalH / 2 },
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
  const waveEdgeRectPath = createPathFromPoints(points);
 | 
			
		||||
  const waveEdgeRectNode = rc.path(waveEdgeRectPath, options);
 | 
			
		||||
 | 
			
		||||
  const waveEdgeRect = shapeSvg.insert(() => waveEdgeRectNode, ':first-child');
 | 
			
		||||
 | 
			
		||||
  waveEdgeRect.attr('class', 'basic label-container');
 | 
			
		||||
 | 
			
		||||
  if (cssStyles && node.look !== 'handDrawn') {
 | 
			
		||||
    waveEdgeRect.selectAll('path').attr('style', cssStyles);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (nodeStyles) {
 | 
			
		||||
    shape.attr('style', nodeStyles);
 | 
			
		||||
  if (nodeStyles && node.look !== 'handDrawn') {
 | 
			
		||||
    waveEdgeRect.selectAll('path').attr('style', nodeStyles);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  shape.attr('transform', `translate(${-w / 2}, ${-h / 2})`);
 | 
			
		||||
 | 
			
		||||
  updateNodeBounds(node, shape);
 | 
			
		||||
  waveEdgeRect.attr('transform', `translate(0,${-waveAmplitude / 2})`);
 | 
			
		||||
  label.attr(
 | 
			
		||||
    'transform',
 | 
			
		||||
    `translate(${-w / 2 + (node.padding ?? 0)},${-h / 2 + (node.padding ?? 0) - waveAmplitude / 2})`
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  updateNodeBounds(node, waveEdgeRect);
 | 
			
		||||
  node.intersect = function (point) {
 | 
			
		||||
    const pos = intersect.polygon(node, points, point);
 | 
			
		||||
    return pos;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,10 @@
 | 
			
		||||
import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js';
 | 
			
		||||
import {
 | 
			
		||||
  labelHelper,
 | 
			
		||||
  updateNodeBounds,
 | 
			
		||||
  getNodeClasses,
 | 
			
		||||
  createPathFromPoints,
 | 
			
		||||
  generateFullSineWavePoints,
 | 
			
		||||
} from './util.js';
 | 
			
		||||
import intersect from '../intersect/index.js';
 | 
			
		||||
import type { Node } from '$root/rendering-util/types.d.ts';
 | 
			
		||||
import {
 | 
			
		||||
@@ -7,58 +13,50 @@ import {
 | 
			
		||||
} from '$root/rendering-util/rendering-elements/shapes/handDrawnShapeStyles.js';
 | 
			
		||||
import rough from 'roughjs';
 | 
			
		||||
 | 
			
		||||
function createWaveRectanglePathD(x: number, y: number, width: number, height: number) {
 | 
			
		||||
  const halfWidth = width / 2;
 | 
			
		||||
  const halfHeight = height / 2;
 | 
			
		||||
 | 
			
		||||
  const pathData = `M ${x} ${y}
 | 
			
		||||
    Q ${x + halfWidth / 2} ${y + halfHeight}, ${x + halfWidth} ${y}
 | 
			
		||||
    Q ${x + (3 * halfWidth) / 2} ${y - halfHeight}, ${x + width} ${y}
 | 
			
		||||
    L ${x + width} ${y + height}
 | 
			
		||||
    Q ${x + (3 * halfWidth) / 2} ${y + halfHeight}, ${x + halfWidth} ${y + height}
 | 
			
		||||
    Q ${x + halfWidth / 2} ${y + 3 * halfHeight}, ${x} ${y + height}
 | 
			
		||||
    L ${x} ${y}
 | 
			
		||||
    Z`;
 | 
			
		||||
  return pathData;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const waveRectangle = async (parent: SVGAElement, node: Node) => {
 | 
			
		||||
  const { labelStyles, nodeStyles } = styles2String(node);
 | 
			
		||||
  node.labelStyle = labelStyles;
 | 
			
		||||
  const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node));
 | 
			
		||||
  const w = bbox.width + node.padding;
 | 
			
		||||
  const h = bbox.height + node.padding + 20;
 | 
			
		||||
 | 
			
		||||
  let shape: d3.Selection<SVGPathElement | SVGGElement, unknown, null, undefined>;
 | 
			
		||||
  const w = Math.max(bbox.width + (node.padding ?? 0) * 2, node?.width ?? 0);
 | 
			
		||||
  const h = Math.max(bbox.height + (node.padding ?? 0) * 2, node?.height ?? 0);
 | 
			
		||||
  const waveAmplitude = h / 4;
 | 
			
		||||
  const finalH = h + waveAmplitude;
 | 
			
		||||
  const { cssStyles } = node;
 | 
			
		||||
 | 
			
		||||
  if (node.look === 'handDrawn') {
 | 
			
		||||
    // @ts-ignore - rough is not typed
 | 
			
		||||
    const rc = rough.svg(shapeSvg);
 | 
			
		||||
    const pathData = createWaveRectanglePathD(0, 0, w, h);
 | 
			
		||||
    const shapeNode = rc.path(pathData, userNodeOverrides(node, {}));
 | 
			
		||||
  // @ts-ignore - rough is not typed
 | 
			
		||||
  const rc = rough.svg(shapeSvg);
 | 
			
		||||
  const options = userNodeOverrides(node, {});
 | 
			
		||||
 | 
			
		||||
    shape = shapeSvg.insert(() => shapeNode, ':first-child');
 | 
			
		||||
    shape.attr('class', 'basic label-container');
 | 
			
		||||
    if (cssStyles) {
 | 
			
		||||
      shape.attr('style', cssStyles);
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    const pathData = createWaveRectanglePathD(0, 0, w, h);
 | 
			
		||||
    shape = shapeSvg
 | 
			
		||||
      .insert('path', ':first-child')
 | 
			
		||||
      .attr('d', pathData)
 | 
			
		||||
      .attr('class', 'basic label-container')
 | 
			
		||||
      .attr('style', cssStyles)
 | 
			
		||||
      .attr('style', nodeStyles);
 | 
			
		||||
  if (node.look !== 'handDrawn') {
 | 
			
		||||
    options.roughness = 0;
 | 
			
		||||
    options.fillStyle = 'solid';
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  shape.attr('transform', `translate(${-w / 2}, ${-h / 2})`);
 | 
			
		||||
  const points = [
 | 
			
		||||
    { x: -w / 2, y: finalH / 2 },
 | 
			
		||||
    ...generateFullSineWavePoints(-w / 2, finalH / 2, w / 2, finalH / 2, waveAmplitude, 1),
 | 
			
		||||
    { x: w / 2, y: -finalH / 2 },
 | 
			
		||||
    ...generateFullSineWavePoints(w / 2, -finalH / 2, -w / 2, -finalH / 2, waveAmplitude, -1),
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
  updateNodeBounds(node, shape);
 | 
			
		||||
  const waveRectPath = createPathFromPoints(points);
 | 
			
		||||
  const waveRectNode = rc.path(waveRectPath, options);
 | 
			
		||||
 | 
			
		||||
  const waveRect = shapeSvg.insert(() => waveRectNode, ':first-child');
 | 
			
		||||
 | 
			
		||||
  waveRect.attr('class', 'basic label-container');
 | 
			
		||||
 | 
			
		||||
  if (cssStyles && node.look !== 'handDrawn') {
 | 
			
		||||
    waveRect.selectAll('path').attr('style', cssStyles);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (nodeStyles && node.look !== 'handDrawn') {
 | 
			
		||||
    waveRect.selectAll('path').attr('style', nodeStyles);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  updateNodeBounds(node, waveRect);
 | 
			
		||||
  node.intersect = function (point) {
 | 
			
		||||
    const pos = intersect.polygon(node, point);
 | 
			
		||||
    const pos = intersect.polygon(node, points, point);
 | 
			
		||||
    return pos;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user