/** * @fileOverview point shapes * @author dxq613@gmail.com * @author sima.zhang1990@gmail.com * @author huangtonger@aliyun.com */ const Util = require('../../util'); const ShapeUtil = require('../util/shape'); const Global = require('../../global'); const Shape = require('./shape'); // const svgpath = require('svgpath'); const { Marker } = require('../../renderer'); const PathUtil = Util.PathUtil; const SHAPES = [ 'circle', 'square', 'bowtie', 'diamond', 'hexagon', 'triangle', 'triangle-down' ]; const HOLLOW_SHAPES = [ 'cross', 'tick', 'plus', 'hyphen', 'line', 'pointerLine', 'pointerArrow' ]; const SQRT_3 = Math.sqrt(3); // 增加marker Util.mix(Marker.Symbols, { hexagon(x, y, r) { const diffX = (r / 2) * SQRT_3; return [ [ 'M', x, y - r ], [ 'L', x + diffX, y - r / 2 ], [ 'L', x + diffX, y + r / 2 ], [ 'L', x, y + r ], [ 'L', x - diffX, y + r / 2 ], [ 'L', x - diffX, y - r / 2 ], [ 'Z' ] ]; }, bowtie(x, y, r) { const diffY = r - 1.5; return [ [ 'M', x - r, y - diffY ], [ 'L', x + r, y + diffY ], [ 'L', x + r, y - diffY ], [ 'L', x - r, y + diffY ], [ 'Z' ] ]; }, cross(x, y, r) { return [ [ 'M', x - r, y - r ], [ 'L', x + r, y + r ], [ 'M', x + r, y - r ], [ 'L', x - r, y + r ] ]; }, tick(x, y, r) { return [ [ 'M', x - r / 2, y - r ], [ 'L', x + r / 2, y - r ], [ 'M', x, y - r ], [ 'L', x, y + r ], [ 'M', x - r / 2, y + r ], [ 'L', x + r / 2, y + r ] ]; }, plus(x, y, r) { return [ [ 'M', x - r, y ], [ 'L', x + r, y ], [ 'M', x, y - r ], [ 'L', x, y + r ] ]; }, hyphen(x, y, r) { return [ [ 'M', x - r, y ], [ 'L', x + r, y ] ]; }, line(x, y, r) { return [ [ 'M', x, y - r ], [ 'L', x, y + r ] ]; } }); function getFillAttrs(cfg) { const defaultAttrs = Global.shape.point; const pointAttrs = Util.mix({}, defaultAttrs, cfg.style); ShapeUtil.addFillAttrs(pointAttrs, cfg); if (Util.isNumber(cfg.size)) { pointAttrs.radius = cfg.size; } return pointAttrs; } function getLineAttrs(cfg) { const defaultAttrs = Global.shape.hollowPoint; const pointAttrs = Util.mix({}, defaultAttrs, cfg.style); ShapeUtil.addStrokeAttrs(pointAttrs, cfg); if (Util.isNumber(cfg.size)) { pointAttrs.radius = cfg.size; } return pointAttrs; } const Point = Shape.registerFactory('point', { defaultShapeType: 'hollowCircle', getActiveCfg(type, cfg) { // 点放大 + 颜色加亮 const radius = cfg.radius; let color; if (type && (type.indexOf('hollow') === 0 || Util.indexOf(HOLLOW_SHAPES, type) !== -1) || !type) { color = cfg.stroke || cfg.strokeStyle; } else { color = cfg.fill || cfg.fillStyle; } return { radius: radius + 1, shadowBlur: radius, shadowColor: color, stroke: color, strokeOpacity: 1, lineWidth: 1 }; }, getDefaultPoints(pointInfo) { return ShapeUtil.splitPoints(pointInfo); } }); function getRectPath(cfg) { const x = cfg.points[0].x; const y = cfg.points[0].y; const w = cfg.size[0]; const h = cfg.size[1]; const path = [[ 'M', x - 0.5 * w, y - 0.5 * h ], [ 'L', x + 0.5 * w, y - 0.5 * h ], [ 'L', x + 0.5 * w, y + 0.5 * h ], [ 'L', x - 0.5 * w, y + 0.5 * h ], [ 'z' ]]; return path; } // 用于桑基图的节点 Shape.registerShape('point', 'rect', { draw(cfg, container) { const rectAttrs = getFillAttrs(cfg); let path = getRectPath(cfg); path = this.parsePath(path); const gShape = container.addShape('path', { attrs: Util.mix(rectAttrs, { path }) }); return gShape; }, getMarkerCfg(cfg) { const attrs = getFillAttrs(cfg); attrs.symbol = 'rect'; attrs.radius = 4.5; return attrs; } }); // 添加shapes Util.each(SHAPES, shape => { Shape.registerShape('point', shape, { draw(cfg, container) { // cfg.points = this.parsePoints(cfg.points); const attrs = getFillAttrs(cfg); return container.addShape('Marker', { attrs: Util.mix(attrs, { symbol: shape, x: cfg.x, y: cfg.y }) }); }, getMarkerCfg(cfg) { const attrs = getFillAttrs(cfg); attrs.symbol = shape; attrs.radius = 4.5; return attrs; } }); // 添加该 shape 对应的 hollowShape Shape.registerShape('point', 'hollow' + Util.upperFirst(shape), { draw(cfg, container) { // cfg.points = this.parsePoints(cfg.points); const attrs = getLineAttrs(cfg); return container.addShape('Marker', { attrs: Util.mix(attrs, { symbol: shape, x: cfg.x, y: cfg.y }) }); }, getMarkerCfg(cfg) { const attrs = getLineAttrs(cfg); attrs.symbol = shape; attrs.radius = 4.5; return attrs; } }); }); // 添加 hollowShapes Util.each(HOLLOW_SHAPES, shape => { Shape.registerShape('point', shape, { draw(cfg, container) { const attrs = getLineAttrs(cfg); return container.addShape('Marker', { attrs: Util.mix(attrs, { symbol: shape, x: cfg.x, y: cfg.y }) }); }, getMarkerCfg(cfg) { const attrs = getLineAttrs(cfg); attrs.symbol = shape; attrs.radius = 4.5; return attrs; } }); }); // image Shape.registerShape('point', 'image', { draw(cfg, container) { cfg.points = this.parsePoints(cfg.points); return container.addShape('image', { attrs: { x: cfg.points[0].x - (cfg.size / 2), y: cfg.points[0].y - cfg.size, width: cfg.size, height: cfg.size, img: cfg.shape[1] } }); } }); // path const pathMetaCache = {}; Shape.registerShape('point', 'path', { draw(cfg, container) { const attrs = Util.mix({}, getLineAttrs(cfg), getFillAttrs(cfg)); const path = cfg.shape[1]; const size = cfg.size || 10; let pathMeta; if (pathMetaCache[path]) { pathMeta = pathMetaCache[path]; } else { const segments = PathUtil.parsePathString(path); const nums = Util.flatten(segments).filter(num => Util.isNumber(num)); pathMetaCache[path] = pathMeta = { range: Math.max.apply(null, nums) - Math.min.apply(null, nums), segments }; } const scale = size / pathMeta.range; const transform = []; if (attrs.rotate) { transform.push([ 'r', attrs.rotate / 180 * Math.PI ]); delete attrs.rotate; } const shape = container.addShape('path', { attrs: Util.mix(attrs, { path: pathMeta.segments }) }); transform.push([ 's', scale, scale ], [ 't', cfg.x, cfg.y ]); shape.transform(transform); return shape; } }); module.exports = Point;