diff --git a/cypress/platform/current.html b/cypress/platform/current.html index b987d1b08..e48303f0e 100644 --- a/cypress/platform/current.html +++ b/cypress/platform/current.html @@ -107,14 +107,15 @@ a <--> b b o--o c c x--x d - a2 --> b2 - b2 --o c2 - c2 --x d2 + a21([In the box]) --> b2 + b2((b2)) --o c2 + c2(c2) --x d2 --> id1{{This is the text in the box}} --> A[(cylindrical
shape
test)] old:
graph LR - a --> b + a((a)) --> b --> id1{{This is the text in the box}} + A[(cylindrical
shape
test)]
diff --git a/src/dagre-wrapper/intersect/index.js b/src/dagre-wrapper/intersect/index.js index 5cf3a259c..20c3a8ccb 100644 --- a/src/dagre-wrapper/intersect/index.js +++ b/src/dagre-wrapper/intersect/index.js @@ -2,11 +2,11 @@ * Borrowed with love from from dagrge-d3. Many thanks to cpettitt! */ -import node from './intersect-node'; -import circle from './intersect-circle'; -import ellipse from './intersect-ellipse'; -import polygon from './intersect-polygon'; -import rect from './intersect-rect'; +import node from './intersect-node.js'; +import circle from './intersect-circle.js'; +import ellipse from './intersect-ellipse.js'; +import polygon from './intersect-polygon.js'; +import rect from './intersect-rect.js'; export default { node, diff --git a/src/dagre-wrapper/nodes.js b/src/dagre-wrapper/nodes.js index 06e947dad..78c8d98c8 100644 --- a/src/dagre-wrapper/nodes.js +++ b/src/dagre-wrapper/nodes.js @@ -1,17 +1,14 @@ -import intersectRect from './intersect/intersect-rect'; +import intersect from './intersect/index.js'; import { logger } from '../logger'; // eslint-disable-line import createLabel from './createLabel'; -const rect = (parent, node) => { +const labelHelper = (parent, node) => { // Add outer g element const shapeSvg = parent .insert('g') .attr('class', 'node default') .attr('id', node.id); - // add the rect - const rect = shapeSvg.insert('rect', ':first-child'); - // Create the label and insert it after the rect const label = shapeSvg.insert('g').attr('class', 'label'); @@ -22,7 +19,279 @@ const rect = (parent, node) => { const halfPadding = node.padding / 2; - // center the rect around its coordinate + // Center the label + label.attr('transform', 'translate(' + -bbox.width / 2 + ', ' + -bbox.height / 2 + ')'); + + return { shapeSvg, bbox, halfPadding, label }; +}; + +const updateNodeBounds = (node, element) => { + const bbox = element.node().getBBox(); + node.width = bbox.width; + node.height = bbox.height; +}; + +function insertPolygonShape(parent, w, h, points) { + return parent + .insert('polygon', ':first-child') + .attr( + 'points', + points + .map(function(d) { + return d.x + ',' + d.y; + }) + .join(' ') + ) + .attr('transform', 'translate(' + -w / 2 + ',' + h / 2 + ')'); +} +const question = (parent, node) => { + const { shapeSvg, bbox } = labelHelper(parent, node); + + const w = bbox.width + node.padding; + const h = bbox.height + node.padding; + const s = (w + h) * 0.9; + const points = [ + { x: s / 2, y: 0 }, + { x: s, y: -s / 2 }, + { x: s / 2, y: -s }, + { x: 0, y: -s / 2 } + ]; + + const questionElem = insertPolygonShape(shapeSvg, s, s, points); + updateNodeBounds(node, questionElem); + node.intersect = function(point) { + return intersect.polugon(node, points, point); + }; + + return shapeSvg; +}; + +const hexagon = (parent, node) => { + const { shapeSvg, bbox } = labelHelper(parent, node); + + const f = 4; + const h = bbox.height + node.padding; + const m = h / f; + const w = bbox.width + 2 * m + node.padding; + const points = [ + { x: m, y: 0 }, + { x: w - m, y: 0 }, + { x: w, y: -h / 2 }, + { x: w - m, y: -h }, + { x: m, y: -h }, + { x: 0, y: -h / 2 } + ]; + const hex = insertPolygonShape(shapeSvg, w, h, points); + updateNodeBounds(node, hex); + + node.intersect = function(point) { + return intersect.polygon(node, point); + }; + + return shapeSvg; +}; + +const rect_left_inv_arrow = (parent, node) => { + const { shapeSvg, bbox } = labelHelper(parent, node); + + const w = bbox.width + node.padding; + const h = bbox.height + node.padding; + const points = [ + { x: -h / 2, y: 0 }, + { x: w, y: 0 }, + { x: w, y: -h }, + { x: -h / 2, y: -h }, + { x: 0, y: -h / 2 } + ]; + + const el = insertPolygonShape(shapeSvg, w, h, points); + updateNodeBounds(node, el); + + node.intersect = function(point) { + return intersect.polygon(node, point); + }; + + return shapeSvg; +}; +const lean_right = (parent, node) => { + const { shapeSvg, bbox } = labelHelper(parent, node); + + const w = bbox.width + node.padding; + const h = bbox.height + node.padding; + const points = [ + { x: (-2 * h) / 6, y: 0 }, + { x: w - h / 6, y: 0 }, + { x: w + (2 * h) / 6, y: -h }, + { x: h / 6, y: -h } + ]; + + const el = insertPolygonShape(shapeSvg, w, h, points); + updateNodeBounds(node, el); + + node.intersect = function(point) { + return intersect.polygon(node, point); + }; + + return shapeSvg; +}; + +const lean_left = (parent, node) => { + const { shapeSvg, bbox } = labelHelper(parent, node); + + const w = bbox.width + node.padding; + const h = bbox.height + node.padding; + const points = [ + { x: (2 * h) / 6, y: 0 }, + { x: w + h / 6, y: 0 }, + { x: w - (2 * h) / 6, y: -h }, + { x: -h / 6, y: -h } + ]; + + const el = insertPolygonShape(shapeSvg, w, h, points); + updateNodeBounds(node, el); + + node.intersect = function(point) { + return intersect.polygon(node, point); + }; + + return shapeSvg; +}; + +const trapezoid = (parent, node) => { + const { shapeSvg, bbox } = labelHelper(parent, node); + + const w = bbox.width + node.padding; + const h = bbox.height + node.padding; + const points = [ + { x: (-2 * h) / 6, y: 0 }, + { x: w + (2 * h) / 6, y: 0 }, + { x: w - h / 6, y: -h }, + { x: h / 6, y: -h } + ]; + const el = insertPolygonShape(shapeSvg, w, h, points); + updateNodeBounds(node, el); + + node.intersect = function(point) { + return intersect.polygon(node, point); + }; + + return shapeSvg; +}; + +const inv_trapezoid = (parent, node) => { + const { shapeSvg, bbox } = labelHelper(parent, node); + + const w = bbox.width + node.padding; + const h = bbox.height + node.padding; + const points = [ + { x: h / 6, y: 0 }, + { x: w - h / 6, y: 0 }, + { x: w + (2 * h) / 6, y: -h }, + { x: (-2 * h) / 6, y: -h } + ]; + const el = insertPolygonShape(shapeSvg, w, h, points); + updateNodeBounds(node, el); + + node.intersect = function(point) { + return intersect.polygon(node, point); + }; + + return shapeSvg; +}; +const rect_right_inv_arrow = (parent, node) => { + const { shapeSvg, bbox } = labelHelper(parent, node); + + const w = bbox.width + node.padding; + const h = bbox.height + node.padding; + const points = [ + { x: 0, y: 0 }, + { x: w + h / 2, y: 0 }, + { x: w, y: -h / 2 }, + { x: w + h / 2, y: -h }, + { x: 0, y: -h } + ]; + const el = insertPolygonShape(shapeSvg, w, h, points); + updateNodeBounds(node, el); + + node.intersect = function(point) { + return intersect.polygon(node, point); + }; + + return shapeSvg; +}; +const cylinder = (parent, node) => { + const { shapeSvg, bbox } = labelHelper(parent, node); + + const w = bbox.width + node.padding; + const rx = w / 2; + const ry = rx / (2.5 + w / 50); + const h = bbox.height + ry + node.padding; + + const shape = + 'M 0,' + + ry + + ' a ' + + rx + + ',' + + ry + + ' 0,0,0 ' + + w + + ' 0 a ' + + rx + + ',' + + ry + + ' 0,0,0 ' + + -w + + ' 0 l 0,' + + h + + ' a ' + + rx + + ',' + + ry + + ' 0,0,0 ' + + w + + ' 0 l 0,' + + -h; + + const el = shapeSvg + .attr('label-offset-y', ry) + .insert('path', ':first-child') + .attr('d', shape) + .attr('transform', 'translate(' + -w / 2 + ',' + -(h / 2 + ry) + ')'); + + updateNodeBounds(node, el); + + node.intersect = function(point) { + const pos = intersect.rect(node, point); + const x = pos.x - node.x; + + if ( + rx != 0 && + (Math.abs(x) < node.width / 2 || + (Math.abs(x) == node.width / 2 && Math.abs(pos.y - node.y) > node.height / 2 - ry)) + ) { + // ellipsis equation: x*x / a*a + y*y / b*b = 1 + // solve for y to get adjustion value for pos.y + let y = ry * ry * (1 - (x * x) / (rx * rx)); + if (y != 0) y = Math.sqrt(y); + y = ry - y; + if (point.y - node.y > 0) y = -y; + + pos.y += y; + } + + return pos; + }; + + return shapeSvg; +}; + +const rect = (parent, node) => { + const { shapeSvg, bbox, halfPadding } = labelHelper(parent, node); + + // add the rect + const rect = shapeSvg.insert('rect', ':first-child'); + rect .attr('rx', node.rx) .attr('ry', node.ry) @@ -31,21 +300,74 @@ const rect = (parent, node) => { .attr('width', bbox.width + node.padding) .attr('height', bbox.height + node.padding); - // Center the label - label.attr('transform', 'translate(' + -bbox.width / 2 + ', ' + -bbox.height / 2 + ')'); - - const rectBox = rect.node().getBBox(); - node.width = rectBox.width; - node.height = rectBox.height; + updateNodeBounds(node, rect); node.intersect = function(point) { - return intersectRect(node, point); + return intersect.rect(node, point); }; return shapeSvg; }; -const shapes = { rect }; +const stadium = (parent, node) => { + const { shapeSvg, bbox } = labelHelper(parent, node); + + const h = bbox.height + node.padding; + const w = bbox.width + h / 4 + node.padding; + + // add the rect + const rect = shapeSvg + .insert('rect', ':first-child') + .attr('rx', h / 2) + .attr('ry', h / 2) + .attr('x', -w / 2) + .attr('y', -h / 2) + .attr('width', w) + .attr('height', h); + + updateNodeBounds(node, rect); + + node.intersect = function(point) { + return intersect.rect(node, point); + }; + + return shapeSvg; +}; +const circle = (parent, node) => { + const { shapeSvg, bbox, halfPadding } = labelHelper(parent, node); + const circle = shapeSvg.insert('circle', ':first-child'); + + // center the circle around its coordinate + circle + .attr('rx', node.rx) + .attr('ry', node.ry) + .attr('r', bbox.width / 2 + halfPadding) + .attr('width', bbox.width + node.padding) + .attr('height', bbox.height + node.padding); + + updateNodeBounds(node, circle); + + node.intersect = function(point) { + return intersect.circle(node, point); + }; + + return shapeSvg; +}; + +const shapes = { + question, + rect, + circle, + stadium, + hexagon, + rect_left_inv_arrow, + lean_right, + lean_left, + trapezoid, + inv_trapezoid, + rect_right_inv_arrow, + cylinder +}; let nodeElems = {}; diff --git a/src/diagrams/flowchart/flowRenderer-v2.js b/src/diagrams/flowchart/flowRenderer-v2.js index e1866d396..17c532a6f 100644 --- a/src/diagrams/flowchart/flowRenderer-v2.js +++ b/src/diagrams/flowchart/flowRenderer-v2.js @@ -329,44 +329,6 @@ export const draw = function(text, id) { // Add custom shapes // flowChartShapes.addToRenderV2(addShape); - // Add our custom arrow - an empty arrowhead - // render.arrows().none = function normal(parent, id, edge, type) { - // const marker = parent - // .append('marker') - // .attr('id', id) - // .attr('viewBox', '0 0 10 10') - // .attr('refX', 9) - // .attr('refY', 5) - // .attr('markerUnits', 'strokeWidth') - // .attr('markerWidth', 8) - // .attr('markerHeight', 6) - // .attr('orient', 'auto'); - - // // const path = marker.append('path').attr('d', 'M 0 0 L 0 0 L 0 0 z'); - // dagreD3.util.applyStyle(path, edge[type + 'Style']); - // }; - - // Override normal arrowhead defined in d3. Remove style & add class to allow css styling. - // render.arrows().normal = function normal(parent, id) { - // const marker = parent - // .append('marker') - // .attr('id', id) - // .attr('viewBox', '0 0 10 10') - // .attr('refX', 9) - // .attr('refY', 5) - // .attr('markerUnits', 'strokeWidth') - // .attr('markerWidth', 8) - // .attr('markerHeight', 6) - // .attr('orient', 'auto'); - - // marker - // .append('path') - // .attr('d', 'M 0 0 L 10 5 L 0 10 z') - // .attr('class', 'arrowheadPath') - // .style('stroke-width', 1) - // .style('stroke-dasharray', '1,0'); - // }; - // Set up an SVG group so that we can translate the final graph. const svg = d3.select(`[id="${id}"]`);