/** * Class to generate polyline * * @author Dmitry Farafonov */ var ANCHOR_TYPE = { main: "main", middle: "middle", first: "first", last: "last" }; function Anchor(uuid, type, x, y) { this.uuid = uuid; this.x = x this.y = y this.type = (type == ANCHOR_TYPE.middle) ? ANCHOR_TYPE.middle : ANCHOR_TYPE.main; }; Anchor.prototype = { uuid: null, x: 0, y: 0, type: ANCHOR_TYPE.main, isFirst: false, isLast: false, ndex: 0, typeIndex: 0 }; function Polyline(uuid, points, strokeWidth) { /* Array on coordinates: * points: [{x: 410, y: 110}, 1 * {x: 570, y: 110}, 1 2 * {x: 620, y: 240}, 2 3 * {x: 750, y: 270}, 3 4 * {x: 650, y: 370}]; 4 */ this.points = points; /* * path for graph * [["M", x1, y1], ["L", x2, y2], ["C", ax, ay, bx, by, x3, y3], ["L", x3, y3]] */ this.path = []; this.anchors = []; if (strokeWidth) this.strokeWidth = strokeWidth; this.closePath = false; this.init(); }; Polyline.prototype = { id: null, points: [], path: [], anchors: [], strokeWidth: 1, radius: 15, showDetails: false, element: null, isDefaultConditionAvailable: false, closePath: false, init: function (points) { var linesCount = this.getLinesCount(); if (linesCount < 1) return; this.normalizeCoordinates(); // create anchors this.pushAnchor(ANCHOR_TYPE.first, this.getLine(0).x1, this.getLine(0).y1); for (var i = 1; i < linesCount; i++) { var line1 = this.getLine(i - 1), line2 = this.getLine(i); //this.pushAnchor(ANCHOR_TYPE.middle, line1.x1 + line1.x2-line1.x1, line1.y1 + line1.y2-line1.y1); this.pushAnchor(ANCHOR_TYPE.main, line1.x2, line1.y2); //this.pushAnchor(ANCHOR_TYPE.middle, line2.x1 + line2.x2-line2.x1, line2.y1 + line2.y2-line2.y1); } this.pushAnchor(ANCHOR_TYPE.last, this.getLine(linesCount - 1).x2, this.getLine(linesCount - 1).y2); this.rebuildPath(); }, normalizeCoordinates: function () { for (var i = 0; i < this.points.length; i++) { this.points[i].x = parseFloat(this.points[i].x); this.points[i].y = parseFloat(this.points[i].y); } }, getLinesCount: function () { return this.points.length - 1; }, _getLine: function (i) { return {x1: this.points[i].x, y1: this.points[i].y, x2: this.points[i + 1].x, y2: this.points[i + 1].y}; }, getLine: function (i) { var line = this._getLine(i); line.angle = this.getLineAngle(i); return line; }, getLineAngle: function (i) { var line = this._getLine(i); return Math.atan2(line.y2 - line.y1, line.x2 - line.x1); }, getLineLengthX: function (i) { var line = this.getLine(i); return (line.x2 - line.x1); }, getLineLengthY: function (i) { var line = this.getLine(i); return (line.y2 - line.y1); }, getLineLength: function (i) { var line = this.getLine(i); return Math.sqrt(Math.pow(this.getLineLengthX(i), 2) + Math.pow(this.getLineLengthY(i), 2)); }, getAnchors: function () { // ������� ��������������� ������ // ???? return this.anchors; }, getAnchorsCount: function (type) { if (!type) return this.anchors.length; else { var count = 0; for (var i = 0; i < this.getAnchorsCount(); i++) { var anchor = this.anchors[i]; if (anchor.getType() == type) { count++; } } return count; } }, pushAnchor: function (type, x, y, index) { if (type == ANCHOR_TYPE.first) { index = 0; typeIndex = 0; } else if (type == ANCHOR_TYPE.last) { index = this.getAnchorsCount(); typeIndex = 0; } else if (!index) { index = this.anchors.length; } else { // ��������� anchors, �������� ������� ��� �������, ������� � index //var anchor = this.getAnchor() for (var i = 0; i < this.getAnchorsCount(); i++) { var anchor = this.anchors[i]; if (anchor.index > index) { anchor.index++; anchor.typeIndex++; } } } var anchor = new Anchor(this.id, ANCHOR_TYPE.main, x, y, index, typeIndex); this.anchors.push(anchor); }, getAnchor: function (position) { return this.anchors[position]; }, getAnchorByType: function (type, position) { if (type == ANCHOR_TYPE.first) return this.anchors[0]; if (type == ANCHOR_TYPE.last) return this.anchors[this.getAnchorsCount() - 1]; for (var i = 0; i < this.getAnchorsCount(); i++) { var anchor = this.anchors[i]; if (anchor.type == type) { if (position == anchor.position) return anchor; } } return null; }, addNewPoint: function (position, x, y) { // for (var i = 0; i < this.getLinesCount(); i++) { var line = this.getLine(i); if (x > line.x1 && x < line.x2 && y > line.y1 && y < line.y2) { this.points.splice(i + 1, 0, {x: x, y: y}); break; } } this.rebuildPath(); }, rebuildPath: function () { var path = []; for (var i = 0; i < this.getAnchorsCount(); i++) { var anchor = this.getAnchor(i); var pathType = "" if (i == 0) pathType = "M"; else pathType = "L"; // TODO: save previous points and calculate new path just if points are updated, and then save currents values as previous var targetX = anchor.x, targetY = anchor.y; if (i > 0 && i < this.getAnchorsCount() - 1) { // get new x,y var cx = anchor.x, cy = anchor.y; // pivot point of prev line var AO = this.getLineLength(i - 1); if (AO < this.radius) { AO = this.radius; } this.isDefaultConditionAvailable = (this.isDefaultConditionAvailable || (i == 1 && AO > 10)); //console.log("isDefaultConditionAvailable", this.isDefaultConditionAvailable); var ED = this.getLineLengthY(i - 1) * this.radius / AO; var OD = this.getLineLengthX(i - 1) * this.radius / AO; targetX = anchor.x - OD; targetY = anchor.y - ED; if (AO < 2 * this.radius && i > 1) { targetX = anchor.x - this.getLineLengthX(i - 1) / 2; targetY = anchor.y - this.getLineLengthY(i - 1) / 2; ; } // pivot point of next line var AO = this.getLineLength(i); if (AO < this.radius) { AO = this.radius; } var ED = this.getLineLengthY(i) * this.radius / AO; var OD = this.getLineLengthX(i) * this.radius / AO; var nextSrcX = anchor.x + OD; var nextSrcY = anchor.y + ED; if (AO < 2 * this.radius && i < this.getAnchorsCount() - 2) { nextSrcX = anchor.x + this.getLineLengthX(i) / 2; nextSrcY = anchor.y + this.getLineLengthY(i) / 2; ; } var dx0 = (cx - targetX) / 3, dy0 = (cy - targetY) / 3, ax = cx - dx0, ay = cy - dy0, dx1 = (cx - nextSrcX) / 3, dy1 = (cy - nextSrcY) / 3, bx = cx - dx1, by = cy - dy1, zx = nextSrcX, zy = nextSrcY; if (this.showDetails) { var c = ProcessDiagramCanvas.g.path("M" + targetX + "," + targetY + "L" + ax + "," + ay).attr({ stroke: Color.get(255, 153, 51), "stroke-dasharray": "- " }); var c = ProcessDiagramCanvas.g.path("M" + nextSrcX + "," + nextSrcY + "L" + bx + "," + by).attr({ stroke: Color.get(255, 153, 51), "stroke-dasharray": "- " }); var c = ProcessDiagramCanvas.g.ellipse(ax, ay, 2, 2).attr({stroke: Color.SlateGrey}); var c = ProcessDiagramCanvas.g.ellipse(bx, by, 2, 2).attr({stroke: Color.SlateGrey}); var c = ProcessDiagramCanvas.g.ellipse(cx, cy, this.radius, this.radius).attr({stroke: Color.Gainsboro}); var c = ProcessDiagramCanvas.g.ellipse(targetX, targetY, 2, 2).attr({fill: Color.red}); var c = ProcessDiagramCanvas.g.ellipse(nextSrcX, nextSrcY, 2, 2).attr({fill: Color.red}); } } else if (i == 1 && this.getAnchorsCount() == 2) { var AO = this.getLineLength(i - 1); if (AO < this.radius) { AO = this.radius; } this.isDefaultConditionAvailable = (this.isDefaultConditionAvailable || (i == 1 && AO > 10)); //console.log("-- isDefaultConditionAvailable", this.isDefaultConditionAvailable); } // anti smoothing if (this.strokeWidth % 2 == 1) { targetX += 0.5; targetY += 0.5; } path.push([pathType, targetX, targetY]); if (i > 0 && i < this.getAnchorsCount() - 1) { path.push(["C", ax, ay, bx, by, zx, zy]); } } if (this.closePath) { console.log("closePath:", this.closePath); path.push(["Z"]); } this.path = path; }, transform: function (transformation) { this.element.transform(transformation); }, attr: function (attrs) { //console.log("attrs: " +attrs, "", this.element); // TODO: foreach and set each this.element.attr(attrs); } }; function Polygone(points, strokeWidth) { /* Array on coordinates: * points: [{x: 410, y: 110}, 1 * {x: 570, y: 110}, 1 2 * {x: 620, y: 240}, 2 3 * {x: 750, y: 270}, 3 4 * {x: 650, y: 370}]; 4 */ this.points = points; /* * path for graph * [["M", x1, y1], ["L", x2, y2], ["C", ax, ay, bx, by, x3, y3], ["L", x3, y3]] */ this.path = []; this.anchors = []; if (strokeWidth) this.strokeWidth = strokeWidth; this.closePath = true; this.init(); }; /* * Poligone is inherited from Poliline: draws closedPath of polyline */ var Foo = function () { }; Foo.prototype = Polyline.prototype; Polygone.prototype = new Foo(); Polygone.prototype.rebuildPath = function () { var path = []; //console.log("Polygone rebuildPath"); for (var i = 0; i < this.getAnchorsCount(); i++) { var anchor = this.getAnchor(i); var pathType = "" if (i == 0) pathType = "M"; else pathType = "L"; var targetX = anchor.x, targetY = anchor.y; // anti smoothing if (this.strokeWidth % 2 == 1) { targetX += 0.5; targetY += 0.5; } path.push([pathType, targetX, targetY]); } if (this.closePath) path.push(["Z"]); this.path = path; }; /* Polygone.prototype.transform = function(transformation){ this.element.transform(transformation); }; */