#3358 Adding arrows to rendering

This commit is contained in:
Knut Sveidqvist 2024-01-04 14:15:30 +01:00
parent df858dc7b6
commit 2ed4469029
7 changed files with 169 additions and 16 deletions

View File

@ -72,7 +72,10 @@ block-beta
</pre>
<pre id="diagram" class="mermaid">
block-beta
A("APA") --> B("GORILLA")
columns 3
A space space:3 B("GORILLA")
A("APA") --o B
</pre>
<pre id="diagram" class="mermaid2">
block-beta
@ -144,11 +147,8 @@ block-beta
%% end
</pre>
<pre id="diagram" class="mermaid2">
block-beta
id1
block
id2
end
flowchart LR
a1 -- apa --> b1
</pre>
<pre id="diagram" class="mermaid2">
block-beta

View File

@ -371,11 +371,12 @@ const cutPathAtIntersect = (_points, boundryNode) => {
//(edgePaths, e, edge, clusterDb, diagramtype, graph)
export const insertEdge = function (elem, e, edge, clusterDb, diagramType, graph) {
let points = edge.points;
log.info('abc88 InsertEdge: edge=', edge, 'e=', e);
let pointsHasChanged = false;
const tail = graph.node(e.v);
var head = graph.node(e.w);
log.info('abc88 InsertEdge (head & tail): ', head, tail);
log.info('abc88 InsertEdge: ', edge);
if (head.intersect && tail.intersect) {
points = points.slice(1, edge.points.length - 1);
points.unshift(tail.intersect(points[0]));

View File

@ -17,12 +17,21 @@ import clone from 'lodash-es/clone.js';
// Initialize the node database for simple lookups
let blockDatabase: Record<string, Block> = {};
let edgeList: Block[] = [];
let edgeCount: Record<string, number> = {};
const populateBlockDatabase = (blockList: Block[], parent: Block): void => {
const children = [];
for (const block of blockList) {
if (block.type === 'column-setting') {
parent.columns = block.columns || -1;
} else if (block.type === 'edge') {
if (edgeCount[block.id]) {
edgeCount[block.id]++;
} else {
edgeCount[block.id] = 1;
}
block.id = edgeCount[block.id] + '-' + block.id;
edgeList.push(block);
} else {
if (!block.label) {
if (block.type === 'composite') {
@ -31,7 +40,18 @@ const populateBlockDatabase = (blockList: Block[], parent: Block): void => {
block.label = block.id;
}
}
blockDatabase[block.id] = block;
const newBlock = !blockDatabase[block.id];
if (newBlock) {
blockDatabase[block.id] = block;
} else {
// Add newer relevant data to aggregated node
if (block.type !== 'na') {
blockDatabase[block.id].type = block.type;
}
if (block.label !== block.id) {
blockDatabase[block.id].label = block.label;
}
}
if (block.children) {
populateBlockDatabase(block.children, block);
@ -46,7 +66,9 @@ const populateBlockDatabase = (blockList: Block[], parent: Block): void => {
children.push(newBlock);
}
} else {
children.push(block);
if (newBlock) {
children.push(block);
}
}
}
}
@ -63,6 +85,9 @@ const clear = (): void => {
rootBlock = { id: 'root', type: 'composite', children: [], columns: -1 } as Block;
blockDatabase = { root: rootBlock };
blocks = [] as Block[];
edgeList = [];
edgeCount = {};
};
type ITypeStr2Type = (typeStr: string) => BlockType;
@ -101,7 +126,29 @@ export function typeStr2Type(typeStr: string): BlockType {
case '<[]>':
return 'block_arrow';
default:
return 'square';
return 'na';
}
}
type IEdgeTypeStr2Type = (typeStr: string) => string;
export function edgeTypeStr2Type(typeStr: string): string {
log.debug('typeStr2Type', typeStr);
switch (typeStr) {
case '==':
return 'thick';
default:
return 'normal';
}
}
type IEdgeStrToEdgeDataType = (typeStr: string) => string;
export function edgeStrToEdgeData(typeStr: string): string {
switch (typeStr.trim()) {
case '--x':
return 'arrow_cross';
case '--o':
return 'arrow_circle';
default:
return 'arrow_point';
}
}
@ -114,10 +161,10 @@ export const generateId = () => {
type ISetHierarchy = (block: Block[]) => void;
const setHierarchy = (block: Block[]): void => {
// log.debug('The hierarchy', JSON.stringify(block, null, 2));
log.debug('The hierarchy', JSON.stringify(block, null, 2));
rootBlock.children = block;
populateBlockDatabase(block, rootBlock);
// log.debug('The hierarchy', JSON.stringify(rootBlock, null, 2));
log.debug('The hierarchy', JSON.stringify(rootBlock, null, 2));
blocks = rootBlock.children;
};
@ -146,6 +193,10 @@ type IGetBlocks = () => Block[];
const getBlocks: IGetBlocks = () => {
return blocks || [];
};
type IGetEdges = () => Block[];
const getEdges: IGetEdges = () => {
return edgeList;
};
type IGetBlock = (id: string) => Block | undefined;
const getBlock: IGetBlock = (id: string) => {
return blockDatabase[id];
@ -166,12 +217,15 @@ export interface BlockDB extends DiagramDB {
getConfig: () => BlockConfig | undefined;
addLink: IAddLink;
getLogger: IGetLogger;
getEdges: IGetEdges;
getBlocks: IGetBlocks;
getBlock: IGetBlock;
setBlock: ISetBlock;
getLinks: IGetLinks;
getColumns: IGetColumns;
typeStr2Type: ITypeStr2Type;
edgeTypeStr2Type: IEdgeTypeStr2Type;
edgeStrToEdgeData: IEdgeStrToEdgeDataType;
setHierarchy: ISetHierarchy;
generateId: IGenerateId;
}
@ -180,8 +234,11 @@ const db: BlockDB = {
getConfig: () => configApi.getConfig().block,
addLink: addLink,
typeStr2Type: typeStr2Type,
edgeTypeStr2Type: edgeTypeStr2Type,
edgeStrToEdgeData,
getLogger, // TODO: remove
getBlocks,
getEdges,
getLinks,
setHierarchy,
getBlock,

View File

@ -1,8 +1,9 @@
import { Diagram } from '../../Diagram.js';
import * as configApi from '../../config.js';
import { calculateBlockSizes, insertBlocks } from './renderHelpers.js';
import { calculateBlockSizes, insertBlocks, insertEdges } from './renderHelpers.js';
import { layout } from './layout.js';
import { setupGraphViewbox } from '../../setupGraphViewbox.js';
import insertMarkers from '../../dagre-wrapper/markers.js';
import {
select as d3select,
scaleOrdinal as d3scaleOrdinal,
@ -36,13 +37,22 @@ export const draw = async function (
// @ts-ignore TODO root.select is not callable
const svg = securityLevel === 'sandbox' ? root.select(`[id="${id}"]`) : d3select(`[id="${id}"]`);
// Define the supported markers for the diagram
const markers = ['point', 'circle', 'cross'];
// Add the marker definitions to the svg as marker tags
// insertMarkers(svg, markers, diagObj.type, diagObj.arrowMarkerAbsolute);
insertMarkers(svg, markers, diagObj.type, true);
const bl = db.getBlocks();
const edges = db.getEdges();
const nodes = svg.insert('g').attr('class', 'block');
await calculateBlockSizes(nodes, bl, db);
const bounds = layout(db);
log.debug('Here blocks', bl);
// log.debug('Here be blocks', bl);
await insertBlocks(nodes, bl, db);
await insertEdges(nodes, edges, bl, db);
// log.debug('Here', bl);

View File

@ -5,7 +5,9 @@ export interface BlockConfig extends BaseDiagramConfig {
}
export type BlockType =
| 'na'
| 'column-setting'
| 'edge'
| 'round'
| 'block_arrow'
| 'space'
@ -29,9 +31,15 @@ export type BlockType =
| 'composite';
export interface Block {
// When the block have the type edge, the start and end are the id of the source and target blocks
start?: string;
end?: string;
arrowTypeEnd?: string;
arrowTypeStart?: string;
width?: number;
id: string;
label?: string;
intersect?: any;
parent?: Block;
type?: BlockType;
children: Block[];

View File

@ -191,10 +191,19 @@ statement
;
nodeStatement
: nodeStatement link node { yy.getLogger().info('Rule: (nodeStatement link node) ', $1, $2, $3); $$ = [{id: $1.id, label: $1.label, type:$1.type, directions: $1.directions}, {id: $3.id, label: $3.label, type: yy.typeStr2Type($3.typeStr), directions: $3.directions}]; }
: nodeStatement link node {
yy.getLogger().info('Rule: (nodeStatement link node) ', $1, $2, $3, 'abc88 typestr =>',$2);
const edgeData = yy.edgeStrToEdgeData($2)
$$ = [
{id: $1.id, label: $1.label, type:$1.type, directions: $1.directions},
{id: $1.id + '-' + $3.id, start: $1.id, end: $3.id, label: $3.label, type: 'edge', directions: $3.directions, arrowTypeEnd: edgeData, arrowTypeStart: 'arrow_open' },
{id: $3.id, label: $3.label, type: yy.typeStr2Type($3.typeStr), directions: $3.directions}
];
}
| node { yy.getLogger().info('Rule: nodeStatement (node) ', $1); $$ = {id: $1.id, label: $1.label, type: yy.typeStr2Type($1.typeStr), directions: $1.directions}; }
;
columnsStatement
: COLUMNS { yy.getLogger().info('APA123', this? this:'na'); yy.getLogger().info("COLUMNS: ", $1); $$ = {type: 'column-setting', columns: $1 === 'auto'?-1:parseInt($1) } }
;

View File

@ -1,5 +1,7 @@
import { getStylesFromArray } from '../../utils.js';
import { insertNode, positionNode } from '../../dagre-wrapper/nodes.js';
import { insertEdge } from '../../dagre-wrapper/edges.js';
import * as graphlib from 'dagre-d3-es/src/graphlib/index.js';
import { getConfig } from '../../config.js';
import { ContainerElement } from 'd3';
import type { Block } from './blockTypes.js';
@ -127,6 +129,7 @@ function getNodeFromBlock(block: Block, db: BlockDB, positioned = false) {
x: bounds.x,
y: bounds.y,
positioned,
intersect: undefined,
type: vertex.type,
// props: vertex.props,
padding: padding ?? (getConfig()?.flowchart?.padding || 0),
@ -158,6 +161,7 @@ export async function insertBlockPositioned(elem: any, block: Block, db: any) {
const obj = db.getBlock(node.id);
if (obj.type !== 'space') {
const nodeEl = await insertNode(elem, node);
block.intersect = node?.intersect;
positionNode(node);
}
}
@ -183,3 +187,67 @@ export async function calculateBlockSizes(elem: ContainerElement, blocks: Block[
export async function insertBlocks(elem: ContainerElement, blocks: Block[], db: BlockDB) {
await performOperations(elem, blocks, db, insertBlockPositioned);
}
export async function insertEdges(
elem: ContainerElement,
edges: Block[],
blocks: Block[],
db: BlockDB
) {
const g = new graphlib.Graph({
multigraph: true,
compound: true,
});
g.setGraph({
rankdir: 'TB',
nodesep: 10,
ranksep: 10,
marginx: 8,
marginy: 8,
});
for (const block of blocks) {
if (block.size) {
g.setNode(block.id, {
width: block.size.width,
height: block.size.height,
intersect: block.intersect,
});
}
}
// log.debug('abc88 edges', edges);
for (const edge of edges) {
// elem, e, edge, clusterDb, diagramType, graph;
if (edge.start && edge.end) {
const startBlock = db.getBlock(edge.start);
const endBlock = db.getBlock(edge.end);
if (startBlock?.size && endBlock?.size) {
const start = startBlock.size;
const end = endBlock.size;
const points = [
{ x: start.x, y: start.y },
{ x: start.x + (end.x - start.x) / 2, y: start.y + (end.y - start.y) / 2 },
{ x: end.x, y: end.y },
];
// edge.points = points;
await insertEdge(
elem,
{ v: edge.start, w: edge.end, name: edge.id },
{
...edge,
// arrowHead: 'normal',
arrowTypeEnd: edge.arrowTypeEnd,
arrowTypeStart: edge.arrowTypeStart,
points,
classes: 'edge-thickness-normal edge-pattern-solid flowchart-link LS-a1 LE-b1',
},
undefined,
'block',
g
);
}
}
}
}