UNPKG

@leafer-in/arrow

Version:
520 lines (432 loc) 15.4 kB
"use strict"; var draw = require("@leafer-ui/draw"); function __decorate(decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; } typeof SuppressedError === "function" ? SuppressedError : function(error, suppressed, message) { var e = new Error(message); return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; }; class ArrowData extends draw.LineData {} function arrowType(defaultValue) { return draw.decorateLeafAttr(defaultValue, key => draw.attr({ set(value) { if (this.__setAttr(key, value)) { const data = this.__; const useArrow = data.startArrow !== "none" || data.endArrow !== "none"; if (data.__useArrow !== useArrow) draw.doBoundsType(this); data.__useArrow = useArrow; draw.doStrokeType(this); } } })); } exports.Arrow = class Arrow extends draw.Line { get __tag() { return "Arrow"; } constructor(data) { super(data); this.__.__useArrow = true; } static registerArrow(name, data) { draw.PathArrow.register(name, data); } }; __decorate([ draw.dataProcessor(ArrowData) ], exports.Arrow.prototype, "__", void 0); __decorate([ arrowType("angle") ], exports.Arrow.prototype, "endArrow", void 0); exports.Arrow = __decorate([ draw.registerUI() ], exports.Arrow); const {M: M$1, L: L$1, C: C$1, Q: Q$1, O: O$1} = draw.PathCommandMap; const {rotate: rotate$1, copyFrom: copyFrom$1, scale: scale} = draw.PointHelper; const point = {}; const PathMatrixHelper = { layout(data, x, y, scaleX, scaleY, rotation, origin) { let command, i = 0, j, len = data.length; while (i < len) { command = data[i]; switch (command) { case M$1: case L$1: setPoint$1(data, i + 1, x, y, scaleX, scaleY, rotation, origin); i += 3; break; case C$1: for (j = 1; j < 6; j += 2) setPoint$1(data, i + j, x, y, scaleX, scaleY, rotation, origin); i += 7; break; case Q$1: for (j = 1; j < 4; j += 2) setPoint$1(data, i + j, x, y, scaleX, scaleY, rotation, origin); i += 5; break; case O$1: data[i + 1] += x; data[i + 2] += y; if (scaleX) data[i + 3] *= scaleX; if (rotation) { data[i + 4] += rotation; data[i + 5] += rotation; } i += 7; break; } } }, rotate(data, rotation, center) { PathMatrixHelper.layout(data, 0, 0, 1, 1, rotation, center); } }; function setPoint$1(data, startIndex, x, y, scaleX, scaleY, rotation, origin) { copyFrom$1(point, data[startIndex], data[startIndex + 1]); if (rotation) rotate$1(point, rotation, origin); if (scaleX) scale(point, scaleX, scaleY); data[startIndex] = x + point.x; data[startIndex + 1] = y + point.y; } const {layout: layout, rotate: rotate} = PathMatrixHelper; const {getAngle: getAngle} = draw.PointHelper; const zero = { x: 0 }, half = { x: -.5 }; const angle = { connect: zero, offset: { x: -.71, bevelJoin: .36, roundJoin: .22 }, path: [ 1, -3, -3, 2, 0, 0, 2, -3, 3 ] }; const angleSide = { connect: half, offset: { x: -1.207, bevelJoin: .854, roundJoin: .707 }, path: [ 1, -3, -3, 2, 0, 0, 2, -1, 0 ] }; const triangleLinePath = [ 1, -3, 0, 2, -3, -2, 2, 0, 0, 2, -3, 2, 2, -3, 0 ]; const triangleFill = { connect: zero, offset: { x: -.9, bevelJoin: .624, roundJoin: .4 }, path: [ ...triangleLinePath ] }; const triangle = draw.DataHelper.clone(triangleFill); triangle.path = [ ...triangleLinePath, 1, -2.05, 1.1, 2, -2.05, -1.1 ]; triangle.dashPath = [ 1, -2, 0, 2, -.5, 0 ]; const triangleFlipFill = { connect: zero, offset: half, path: [ ...triangleFill.path ] }; const triangleFlip = { connect: zero, offset: half, path: [ ...triangle.path ], dashPath: [ 1, -2.5, 0, 2, -1, 0 ] }; rotate(triangleFlipFill.path, 180, { x: -1.5, y: 0 }); rotate(triangleFlip.path, 180, { x: -1.5, y: 0 }); const arrowLinePath = [ 1, -3, 0, 2, -3.5, -1.8, 2, 0, 0, 2, -3.5, 1.8, 2, -3, 0 ]; const arrowFill = { connect: zero, offset: { x: -1.1, bevelJoin: .872, roundJoin: .6 }, path: [ ...arrowLinePath ] }; const arrow = draw.DataHelper.clone(arrowFill); arrow.path = [ ...arrowLinePath, 1, -2.25, .95, 2, -2.25, -.95 ]; arrow.dashPath = [ 1, -3, 0, 2, -.5, 0 ]; const circleLine = { connect: { x: -1.8 }, path: [ 1, 1.8, -.1, 2, 1.8, 0, 26, 0, 0, 1.8, 0, 359, 0 ] }; const circleFill = { connect: { x: .5 }, path: [ ...circleLine.path ] }; const circle = draw.DataHelper.clone(circleFill); circle.path = [ ...circleLine.path, 1, 0, 0, 26, 0, 0, 1, 0, 360, 0 ]; circle.dashPath = [ 1, -.5, 0, 2, .5, 0 ]; const squareLine = { connect: { x: -1.4 }, path: [ 1, -1.4, 0, 2, -1.4, -1.4, 2, 1.4, -1.4, 2, 1.4, 1.4, 2, -1.4, 1.4, 2, -1.4, 0 ] }; const squareFill = { path: [ ...squareLine.path ] }; const square = { path: [ ...squareLine.path, 2, -1.4, -.49, 2, 1, -.49, 1, -1.4, .49, 2, 1, .49 ] }; const diamondLine = draw.DataHelper.clone(squareLine); diamondLine.connect.x = -1.9; const diamondFill = draw.DataHelper.clone(squareFill); const diamond = draw.DataHelper.clone(square); rotate(diamondLine.path, 45); rotate(diamondFill.path, 45); rotate(diamond.path, 45); const mark = { offset: half, path: [ 1, 0, -2, 2, 0, 2 ] }; const arrows = { angle: angle, "angle-side": angleSide, arrow: arrow, triangle: triangle, "triangle-flip": triangleFlip, circle: circle, "circle-line": circleLine, square: square, "square-line": squareLine, diamond: diamond, "diamond-line": diamondLine, mark: mark }; const fillArrows = { triangle: triangleFill, "triangle-flip": triangleFlipFill, arrow: arrowFill, circle: circleFill, square: squareFill, diamond: diamondFill }; function getArrowPath(ui, arrow, from, to, size, connectOffset, hasDashPattern) { let pathData, scale = 1, rotation = 0, fill; const {strokeCap: strokeCap, strokeJoin: strokeJoin} = ui.__; if (draw.isObject(arrow)) { if (arrow.type) { scale = arrow.scale || 1; rotation = arrow.rotation || 0; pathData = arrows[arrow.type]; if (scale > 1) { const fillData = fillArrows[arrow.type]; if (fillData) pathData = fillData, fill = true; } } else pathData = arrow; } else { pathData = arrows[arrow]; } if (!fill) fill = pathData.fill; const {offset: offset, connect: connect, path: path, dashPath: dashPath} = pathData; let connectX = connect ? connect.x * scale : 0; let offsetX = offset ? offset.x : 0; const data = [ ...path ]; if (hasDashPattern && dashPath) data.push(...dashPath); if (strokeCap !== "none") connectX -= .5; if (offset) { if (strokeJoin === "round" && offset.roundJoin) offsetX += offset.roundJoin; else if (strokeJoin === "bevel" && offset.bevelJoin) offsetX += offset.bevelJoin; } if (scale !== 1) layout(data, 0, 0, scale, scale); if (offsetX) layout(data, offsetX, 0); layout(data, to.x, to.y, size, size, getAngle(from, to) + rotation); connectOffset.x = (connectX + offsetX) * size; const arrowData = { data: data }; if (fill) arrowData.fill = fill; return arrowData; } const {M: M, L: L, C: C, Q: Q, Z: Z, N: N, D: D, X: X, G: G, F: F, O: O, P: P, U: U} = draw.PathCommandMap; const {copy: copy, copyFrom: copyFrom, getDistancePoint: getDistancePoint, isSame: isSame} = draw.PointHelper; const {stintSet: stintSet} = draw.DataHelper; const connectPoint = {}; const first = {}, second = {}; const old = {}, last = {}, now = {}; const PathArrowModule = { list: arrows, fillList: fillArrows, addArrows(ui, updateCache) { const uData = ui.__; const {startArrow: startArrow, endArrow: endArrow, __strokeWidth: strokeWidth, dashPattern: dashPattern, __pathForRender: data, cornerRadius: cornerRadius} = uData; const clonePathForArrow = !cornerRadius; if (!updateCache) uData.__strokeWidthCache = strokeWidth; let startArrowPath, singleStartArrow, endArrowPath, singleEndArrow; let command, i = 0, len = data.length, count = 0, useStartArrow = startArrow && startArrow !== "none"; while (i < len) { command = data[i]; switch (command) { case M: case L: if (count < 2 || i + 6 >= len) { copy(old, now); copyFrom(now, data[i + 1], data[i + 2]); if (!count && useStartArrow) copy(first, now); } i += 3; break; case C: if (count === 1 || i + 7 >= len - 3) { copyPoints(data, last, now, i + 3); old.x = data[i + 1], old.y = data[i + 2]; } i += 7; break; case Q: if (count === 1 || i + 5 >= len - 3) { copyPoints(data, last, now, i + 1); copy(old, last); } i += 5; break; case Z: return; case N: i += 5; break; case D: i += 9; break; case X: i += 6; break; case G: i += 9; break; case F: i += 5; break; case O: i += 7; break; case P: i += 4; break; case U: if (count === 1 || i + 6 >= len - 3) { copyPoints(data, last, now, i + 1); copy(old, last); if (i + 6 !== len) { now.x -= (now.x - last.x) / 10; now.y -= (now.y - last.y) / 10; } } i += 6; break; } count++; if (count === 1 && command !== M) return; if (count === 2 && useStartArrow) copy(second, command === L ? now : isSame(old, first) ? last : old); if (i === len) { const path = uData.__pathForRender = clonePathForArrow ? [ ...data ] : data; if (useStartArrow) { startArrowPath = getArrowPath(ui, startArrow, second, first, strokeWidth, connectPoint, !!dashPattern); singleStartArrow = startArrowPath.fill || dashPattern; if (!singleStartArrow) path.push(...startArrowPath.data); if (connectPoint.x) { getDistancePoint(first, second, -connectPoint.x, true); path[1] = second.x; path[2] = second.y; } } if (endArrow && endArrow !== "none") { if (isSame(last, now)) copy(last, old); endArrowPath = getArrowPath(ui, endArrow, last, now, strokeWidth, connectPoint, !!dashPattern); singleEndArrow = endArrowPath.fill || dashPattern; if (!singleEndArrow) path.push(...endArrowPath.data); if (connectPoint.x) { getDistancePoint(now, last, -connectPoint.x, true); let index; switch (command) { case L: index = i - 3 + 1; break; case C: index = i - 7 + 5; break; case Q: index = i - 5 + 3; break; case U: index = i - 6 + 3; break; } if (index) setPoint(path, last, index); } } } else { copy(last, now); } stintSet(uData, "__startArrowPath", singleStartArrow && startArrowPath); stintSet(uData, "__endArrowPath", singleEndArrow && endArrowPath); } }, updateArrow(ui) { const data = ui.__; if (data.strokeScaleFixed) { if (data.__strokeWidthCache !== data.__strokeWidth) { ui.__updateRenderPath(true); } } }, register(name, data, fillData) { this.list[name] = data; if (fillData) this.fillList[name] = data; }, get(name) { return this.list[name]; } }; function copyPoints(data, from, to, startIndex) { copyFrom(from, data[startIndex], data[startIndex + 1]); copyFrom(to, data[startIndex + 2], data[startIndex + 3]); } function setPoint(data, point, startIndex) { data[startIndex] = point.x; data[startIndex + 1] = point.y; } draw.Plugin.add("arrow"); draw.UI.addAttr("startArrow", "none", arrowType); draw.UI.addAttr("endArrow", "none", arrowType); Object.assign(draw.PathArrow, PathArrowModule); Object.assign(draw.Paint, { strokeArrow(stroke, ui, canvas, _renderOptions) { const {__startArrowPath: __startArrowPath, __endArrowPath: __endArrowPath, dashPattern: dashPattern} = ui.__; if (dashPattern) canvas.dashPattern = null; if (__startArrowPath) { canvas.beginPath(); ui.__drawPathByData(canvas, __startArrowPath.data); canvas.stroke(); if (__startArrowPath.fill) { canvas.fillStyle = stroke; canvas.fill(); } } if (__endArrowPath) { canvas.beginPath(); ui.__drawPathByData(canvas, __endArrowPath.data); canvas.stroke(); if (__endArrowPath.fill) { canvas.fillStyle = stroke; canvas.fill(); } } } }); exports.ArrowData = ArrowData; exports.PathArrowModule = PathArrowModule; exports.PathMatrixHelper = PathMatrixHelper; exports.arrowType = arrowType;