/** * @fileOverview 边的 shape * @author dxq613@gmail.com */ const Util = require('../../util'); const Shape = require('./shape'); const ShapeUtil = require('../util/shape'); const Global = require('../../global'); const PathUtil = require('../util/path'); const CORNER_PERCENT = 1 / 3; function getAttrs(cfg) { const defaultCfg = Global.shape.edge; const lineAttrs = Util.mix({}, defaultCfg, cfg.style); ShapeUtil.addStrokeAttrs(lineAttrs, cfg); if (cfg.size) { lineAttrs.lineWidth = cfg.size; } return lineAttrs; } const Edge = Shape.registerFactory('edge', { defaultShapeType: 'line', getDefaultPoints(pointInfo) { return ShapeUtil.splitPoints(pointInfo); }, getActiveCfg(type, cfg) { const lineWidth = cfg.lineWidth || 0; return { lineWidth: lineWidth + 1 }; } }); function getCPath(from, to) { const points = []; points.push({ x: from.x, y: from.y * (1 - 1 / 2) + to.y * 1 / 2 }); points.push({ y: from.y * (1 - 1 / 2) + to.y * 1 / 2, x: to.x }); points.push(to); const sub = [ 'C' ]; Util.each(points, point => { sub.push(point.x, point.y); }); return sub; } function getQPath(to, center) { const points = []; points.push({ x: center.x, y: center.y }); points.push(to); const sub = [ 'Q' ]; Util.each(points, point => { sub.push(point.x, point.y); }); return sub; } function createSmoothPath(from, to) { const sub = getCPath(from, to); const path = [ [ 'M', from.x, from.y ] ]; path.push(sub); return path; } function createArcPath(from, to, center) { const sub = getQPath(to, center); const path = [ [ 'M', from.x, from.y ] ]; path.push(sub); return path; } function createArcWeightPath(points, center) { const arc1 = getQPath(points[1], center); const arc2 = getQPath(points[3], center); const path = [ [ 'M', points[0].x, points[0].y ] ]; path.push(arc2); path.push([ 'L', points[3].x, points[3].y ]); path.push([ 'L', points[2].x, points[2].y ]); path.push(arc1); path.push([ 'L', points[1].x, points[1].y ]); path.push([ 'L', points[0].x, points[0].y ]); path.push([ 'Z' ]); return path; } function createRectPath(from, to) { const points = []; points.push({ y: from.y * (1 - CORNER_PERCENT) + to.y * CORNER_PERCENT, x: from.x }); points.push({ y: from.y * (1 - CORNER_PERCENT) + to.y * CORNER_PERCENT, x: to.x }); points.push(to); const path = [ [ 'M', from.x, from.y ] ]; Util.each(points, point => { path.push([ 'L', point.x, point.y ]); }); return path; } Shape.registerShape('edge', 'line', { draw(cfg, container) { const points = this.parsePoints(cfg.points); const attrCfg = getAttrs(cfg); const path = PathUtil.getLinePath(points); const line = container.addShape('path', { attrs: Util.mix(attrCfg, { path }) }); return line; }, getMarkerCfg(cfg) { return Util.mix({ symbol: 'circle', radius: 4.5 }, getAttrs(cfg)); } }); Shape.registerShape('edge', 'vhv', { draw(cfg, container) { const points = cfg.points; const attrCfg = getAttrs(cfg); let path = createRectPath(points[0], points[1]); path = this.parsePath(path); const line = container.addShape('path', { attrs: Util.mix(attrCfg, { path }) }); return line; }, getMarkerCfg(cfg) { return Util.mix({ symbol: 'circle', radius: 4.5 }, getAttrs(cfg)); } }); Shape.registerShape('edge', 'smooth', { draw(cfg, container) { const points = cfg.points; const attrCfg = getAttrs(cfg); let path = createSmoothPath(points[0], points[1]); path = this.parsePath(path); const line = container.addShape('path', { attrs: Util.mix(attrCfg, { path }) }); return line; }, getMarkerCfg(cfg) { return Util.mix({ symbol: 'circle', radius: 4.5 }, getAttrs(cfg)); } }); // 弧线包括笛卡尔坐标系下的半圆弧线、极坐标系下以圆心为控制点的二阶曲线、笛卡尔坐标系下带权重的三阶曲线、极坐标系下带权重的以圆心为控制点的二阶曲线 Shape.registerShape('edge', 'arc', { draw(cfg, container) { let points = cfg.points; const type = points.length > 2 ? 'weight' : 'normal'; const attrCfg = getAttrs(cfg); let line; let path; if (cfg.isInCircle) { const center = { x: 0, y: 1 }; if (type === 'normal') { path = createArcPath(points[0], points[1], center); } else { attrCfg.fill = attrCfg.stroke; path = createArcWeightPath(points, center); } path = this.parsePath(path); line = container.addShape('path', { attrs: Util.mix(attrCfg, { path }) }); } else { if (type === 'normal') { points = this.parsePoints(points); line = container.addShape('arc', { attrs: Util.mix(attrCfg, { x: (points[1].x + points[0].x) / 2, y: points[0].y, r: Math.abs((points[1].x - points[0].x)) / 2, startAngle: Math.PI, endAngle: Math.PI * 2 }) }); } else { path = [ [ 'M', points[0].x, points[0].y ], [ 'L', points[1].x, points[1].y ] ]; const c1 = getCPath(points[1], points[3]); const c2 = getCPath(points[2], points[0]); path.push(c1); path.push([ 'L', points[3].x, points[3].y ]); path.push([ 'L', points[2].x, points[2].y ]); path.push(c2); path.push([ 'Z' ]); path = this.parsePath(path); attrCfg.fill = attrCfg.stroke; line = container.addShape('path', { attrs: Util.mix(attrCfg, { path }) }); } } return line; }, getMarkerCfg(cfg) { return Util.mix({ symbol: 'circle', radius: 4.5 }, getAttrs(cfg)); } }); module.exports = Edge;