SV.registerShape('three-cell-node', { draw(cfg, group) { cfg.size = cfg.size || [30, 10]; const width = cfg.size[0], height = cfg.size[1]; const wrapperRect = group.addShape('rect', { attrs: { x: width / 2, y: height / 2, width: width, height: height, stroke: cfg.style.stroke, fill: '#eee' }, name: 'wrapper', draggable: true }); group.addShape('rect', { attrs: { x: width / 2, y: height / 2, width: width / 3, height: height, fill: cfg.style.fill, stroke: cfg.style.stroke }, name: 'left-rect', draggable: true }); group.addShape('rect', { attrs: { x: width * (5 / 6), y: height / 2, width: width / 3, height: height, fill: '#eee', stroke: cfg.style.stroke }, name: 'mid-rect', draggable: true }); if (cfg.label) { const style = (cfg.labelCfg && cfg.labelCfg.style) || {}; group.addShape('text', { attrs: { x: width * (2 / 3), y: height, textAlign: 'center', textBaseline: 'middle', text: cfg.label, fill: style.fill || '#000', fontSize: style.fontSize || 16 }, name: 'text', draggable: true }); } return wrapperRect; }, getAnchorPoints() { return [ [1 / 6, 0], [0.5, 0], [0.5, 0.5], [5 / 6, 0.5], [0.5, 1], [0, 0.5] ]; } }); SV.registerLayout('GeneralizedList', { defineOptions() { return { element: { table: { type: 'three-cell-node', label: '[id]', size: [90, 30], style: { stroke: '#333', fill: '#b83b5e' } }, atom: { type: 'two-cell-node', label: ['[id]', 'dcd'], size: [60, 30], style: { stroke: '#333', fill: '#b83b5e' } } }, link: { loopSub: {}, loopNext: { type: 'quadratic', curveOffset: -50, sourceAnchor: 2, targetAnchor: 4, style: { stroke: '#333', endArrow: 'default', startArrow: { path: G6.Arrow.circle(2, -1), fill: '#333' } } }, sub: { type: 'line', sourceAnchor: 2, targetAnchor: 0, style: { stroke: '#333', endArrow: { path: G6.Arrow.triangle(8, 6, 0), fill: '#333' }, startArrow: { path: G6.Arrow.circle(2, -1), fill: '#333' } } }, next: { type: 'line', sourceAnchor: 3, targetAnchor: 5, style: { stroke: '#333', endArrow: { path: G6.Arrow.triangle(8, 6, 0), fill: '#333' }, startArrow: { path: G6.Arrow.circle(2, -1), fill: '#333' } } } }, layout: { xInterval: 40, yInterval: 20, } }; }, /** * 对子树进行递归布局 * @param node * @param parent */ layoutItem(node, prev, layoutOptions) { if(!node) { return; } let [width, height] = node.get('size'); if(prev) { node.set('y', prev.get('y')); node.set('x', prev.get('x') + layoutOptions.xInterval + width) } if(node.next) { this.layoutItem(node.next, node, layoutOptions); } // 存在子节点 if(node.sub) { node.sub.set('y', node.get('y') + layoutOptions.yInterval + height); // 子结点还是广义表 if(node.sub.tag === 1) { node.sub.set('x', node.get('x') + width / 3); this.layoutItem(node.sub, null, layoutOptions); } else { let subWidth = node.sub.get('size')[0]; node.sub.set('x', node.get('x') + width - subWidth); } } }, layout(elements, layoutOptions) { let tableNodes = elements.filter(item => item.type === 'table'), tableRootNode = tableNodes.filter(item => item.root)[0]; this.layoutItem(tableRootNode, null, layoutOptions); } })