懒羊羊
2023-08-30 1ac2bc1590406d9babec036e154d8d08f34a6aa1
提交 | 用户 | 时间
1ac2bc 1 /**
2  * Class to generate polyline
3  *
4  * @author Dmitry Farafonov
5  */
6  
7 var ANCHOR_TYPE= {
8     main: "main",
9     middle: "middle",
10     first: "first",
11     last: "last"
12 };
13
14 function Anchor(uuid, type, x, y) {
15     this.uuid = uuid; 
16     this.x = x
17     this.y = y
18     this.type = (type == ANCHOR_TYPE.middle) ? ANCHOR_TYPE.middle : ANCHOR_TYPE.main;
19 };
20 Anchor.prototype = {
21     uuid: null,
22     x: 0,
23     y: 0,
24     type: ANCHOR_TYPE.main,
25     isFirst: false,
26     isLast: false,
27     ndex: 0,
28     typeIndex: 0
29 };
30
31 function Polyline(uuid, points, strokeWidth) {
32     /* Array on coordinates:
33      * points: [{x: 410, y: 110}, 1
34      *            {x: 570, y: 110}, 1 2
35      *            {x: 620, y: 240},   2 3
36      *            {x: 750, y: 270},     3 4
37      *            {x: 650, y: 370}];      4
38      */
39     this.points = points;
40     
41     /*
42      * path for graph
43      * [["M", x1, y1], ["L", x2, y2], ["C", ax, ay, bx, by, x3, y3], ["L", x3, y3]]
44      */
45     this.path = [];
46     
47     this.anchors = [];
48     
49     if (strokeWidth) this.strokeWidth = strokeWidth;
50     
51     this.closePath = false;
52     
53     this.init();
54 };
55
56 Polyline.prototype = {
57     id: null,
58     points: [],
59     path: [],
60     anchors: [],
61     strokeWidth: 1,
62     radius: 15,
63     showDetails: false,
64     element: null,
65     isDefaultConditionAvailable: false,
66     closePath: false,
67     
68     init: function(points){
69         var linesCount = this.getLinesCount();
70         if (linesCount < 1)
71             return;
72             
73         this.normalizeCoordinates();
74         
75         // create anchors
76         
77         this.pushAnchor(ANCHOR_TYPE.first, this.getLine(0).x1, this.getLine(0).y1);
78         
79         for(var i = 1; i < linesCount; i++){
80             var line1 = this.getLine(i-1),
81                 line2 = this.getLine(i);
82             
83             //this.pushAnchor(ANCHOR_TYPE.middle, line1.x1 + line1.x2-line1.x1, line1.y1 + line1.y2-line1.y1);
84             this.pushAnchor(ANCHOR_TYPE.main,  line1.x2, line1.y2);
85             //this.pushAnchor(ANCHOR_TYPE.middle,  line2.x1 + line2.x2-line2.x1, line2.y1 + line2.y2-line2.y1);
86         }
87         
88         this.pushAnchor(ANCHOR_TYPE.last, this.getLine(linesCount-1).x2, this.getLine(linesCount-1).y2);
89         
90         this.rebuildPath();
91     },
92     
93     normalizeCoordinates: function(){
94         for(var i=0; i < this.points.length; i++){
95             this.points[i].x = parseFloat(this.points[i].x);
96             this.points[i].y = parseFloat(this.points[i].y);
97         }
98     },
99     
100     getLinesCount: function(){
101         return this.points.length-1;
102     },
103     _getLine: function(i){
104         return {x1: this.points[i].x, y1: this.points[i].y, x2: this.points[i+1].x, y2: this.points[i+1].y};
105     },
106     getLine: function(i){
107         var line = this._getLine(i);
108         line.angle = this.getLineAngle(i) ;
109         return line;
110     },
111     getLineAngle: function(i){
112         var line = this._getLine(i);
113         return Math.atan2(line.y2 - line.y1, line.x2 - line.x1);
114     },
115     getLineLengthX: function(i){
116         var line = this.getLine(i);
117         return (line.x2 - line.x1);
118     },
119     getLineLengthY: function(i){
120         var line = this.getLine(i);
121         return (line.y2 - line.y1);
122     },
123     getLineLength: function(i){
124         var line = this.getLine(i);
125         return Math.sqrt(Math.pow(this.getLineLengthX(i), 2) + Math.pow(this.getLineLengthY(i), 2));
126     },
127     
128     getAnchors: function(){
129         // âåðíóòü îòñîðòèðîâàííûé ìàññèâ
130         // ????
131         return this.anchors;
132     },
133     getAnchorsCount: function(type){
134         if (!type)
135             return this.anchors.length;
136         else {
137             var count = 0;
138             for(var i=0; i < this.getAnchorsCount(); i++){
139                 var anchor = this.anchors[i];
140                 if (anchor.getType() == type) {
141                     count++;
142                 }
143             }
144             return count;
145         }
146     },
147     
148     pushAnchor: function(type, x, y, index){
149         if (type == ANCHOR_TYPE.first) {
150             index = 0;
151             typeIndex = 0;
152         } else if (type == ANCHOR_TYPE.last) {
153             index = this.getAnchorsCount();
154             typeIndex = 0;
155         } else if (!index) {
156             index = this.anchors.length;
157         } else {
158             // ïåðåáðàòü anchors, ñäâèíóòü ïîçèöèþ äëÿ êàæäîãî, íà÷èíàÿ ñ index
159             //var anchor = this.getAnchor()
160             for(var i=0; i < this.getAnchorsCount(); i++){
161                 var anchor = this.anchors[i];
162                 if (anchor.index > index) {
163                     anchor.index++;
164                     anchor.typeIndex++;
165                 }
166             }
167         }
168         
169         var anchor = new Anchor(this.id, ANCHOR_TYPE.main, x, y, index, typeIndex);
170         
171         this.anchors.push(anchor);
172     },
173     
174     getAnchor: function(position){
175         return this.anchors[position];
176     },
177     
178     getAnchorByType: function(type, position){
179         if (type == ANCHOR_TYPE.first)
180             return this.anchors[0];
181         if (type == ANCHOR_TYPE.last)
182             return this.anchors[this.getAnchorsCount()-1];
183         
184         for(var i=0; i < this.getAnchorsCount(); i++){
185             var anchor = this.anchors[i];
186             if (anchor.type == type) {
187                 if( position == anchor.position)
188                     return anchor;
189             }
190         }
191         return null;
192     },
193     
194     addNewPoint: function(position, x, y){
195         // 
196         for(var i = 0; i < this.getLinesCount(); i++){
197             var line = this.getLine(i);
198             if (x > line.x1 && x < line.x2 && y > line.y1 && y < line.y2) {
199                 this.points.splice(i+1,0,{x: x, y: y});
200                 break;
201             }
202         }
203         
204         this.rebuildPath();
205     },
206     
207     rebuildPath: function(){
208         var path = [];
209         
210         for(var i = 0; i < this.getAnchorsCount(); i++){
211             var anchor = this.getAnchor(i);
212             
213             var pathType = ""
214             if (i==0)
215                 pathType = "M";
216             else 
217                 pathType = "L";
218             
219 // TODO: save previous points and calculate new path just if points are updated, and then save currents values as previous
220             
221             var targetX = anchor.x, targetY = anchor.y;
222             if (i>0 && i < this.getAnchorsCount()-1) {
223                 // get new x,y
224                 var cx = anchor.x, cy = anchor.y;
225                 
226                 // pivot point of prev line
227                 var AO = this.getLineLength(i-1);
228                 if (AO < this.radius) {
229                     AO = this.radius;
230                 }
231                 
232                 this.isDefaultConditionAvailable = (this.isDefaultConditionAvailable || (i == 1 && AO > 10));
233                 //console.log("isDefaultConditionAvailable", this.isDefaultConditionAvailable);
234                 
235                 var ED = this.getLineLengthY(i-1) * this.radius / AO;
236                 var OD = this.getLineLengthX(i-1) * this.radius / AO;
237                     targetX = anchor.x - OD;
238                     targetY = anchor.y - ED;
239                 
240                 if (AO < 2*this.radius && i>1) {
241                     targetX = anchor.x - this.getLineLengthX(i-1)/2;
242                     targetY = anchor.y - this.getLineLengthY(i-1)/2;;
243                 }
244                     
245                 // pivot point of next line
246                 var AO = this.getLineLength(i);
247                 if (AO < this.radius) {
248                     AO = this.radius;
249                 }
250                 var ED = this.getLineLengthY(i) * this.radius / AO;
251                 var OD = this.getLineLengthX(i) * this.radius / AO;
252                     var nextSrcX = anchor.x + OD;
253                     var nextSrcY = anchor.y + ED;
254                     
255                 if (AO < 2*this.radius && i<this.getAnchorsCount()-2) {
256                     nextSrcX = anchor.x + this.getLineLengthX(i)/2;
257                     nextSrcY = anchor.y + this.getLineLengthY(i)/2;;
258                 }
259                     
260                 
261                 var dx0 = (cx - targetX) / 3,
262                     dy0 = (cy - targetY) / 3,
263                     ax = cx - dx0,
264                     ay = cy - dy0,
265                     
266                     dx1 = (cx - nextSrcX) / 3,
267                     dy1 = (cy - nextSrcY) / 3,
268                     bx = cx - dx1,
269                     by = cy - dy1,
270                     
271                     zx=nextSrcX, zy=nextSrcY;
272                     
273                 if (this.showDetails) {
274                     var c = ProcessDiagramCanvas.g.path("M"+targetX+","+targetY+"L"+ax+","+ay).attr({stroke: Color.get(255, 153, 51), "stroke-dasharray": "- "});
275                     var c = ProcessDiagramCanvas.g.path("M"+nextSrcX+","+nextSrcY+"L"+bx+","+by).attr({stroke: Color.get(255, 153, 51), "stroke-dasharray": "- "});
276                     var c = ProcessDiagramCanvas.g.ellipse(ax, ay, 2, 2).attr({stroke: Color.SlateGrey});
277                     var c = ProcessDiagramCanvas.g.ellipse(bx, by, 2, 2).attr({stroke: Color.SlateGrey});
278                     var c = ProcessDiagramCanvas.g.ellipse(cx, cy, this.radius, this.radius).attr({stroke: Color.Gainsboro});
279                     var c = ProcessDiagramCanvas.g.ellipse(targetX, targetY, 2, 2).attr({fill: Color.red});
280                     var c = ProcessDiagramCanvas.g.ellipse(nextSrcX, nextSrcY, 2, 2).attr({fill: Color.red});
281                 }
282             } else if (i==1 && this.getAnchorsCount() == 2){
283                 var AO = this.getLineLength(i-1);
284                 if (AO < this.radius) {
285                     AO = this.radius;
286                 }
287                 this.isDefaultConditionAvailable = (this.isDefaultConditionAvailable || (i == 1 && AO > 10));
288                 //console.log("-- isDefaultConditionAvailable", this.isDefaultConditionAvailable);
289             }
290
291             // anti smoothing
292             if (this.strokeWidth%2 == 1) {
293                 targetX += 0.5;
294                 targetY += 0.5;
295             }
296             
297             path.push([pathType, targetX, targetY]);
298             
299             if (i>0 && i < this.getAnchorsCount()-1) {
300                 path.push(["C", ax, ay, bx, by, zx, zy]);
301             }
302         }
303         
304         if (this.closePath) {
305             console.log("closePath:", this.closePath);
306             path.push(["Z"]);
307         }
308         
309         this.path = path;
310     },
311     
312     transform: function(transformation){
313         this.element.transform(transformation);
314     },
315     attr: function(attrs){
316         //console.log("attrs: " +attrs, "", this.element);
317         // TODO: foreach and set each
318         this.element.attr(attrs);
319     }
320 };
321
322 function Polygone(points, strokeWidth) {
323     /* Array on coordinates:
324      * points: [{x: 410, y: 110}, 1
325      *            {x: 570, y: 110}, 1 2
326      *            {x: 620, y: 240},   2 3
327      *            {x: 750, y: 270},     3 4
328      *            {x: 650, y: 370}];      4
329      */
330     this.points = points;
331     
332     /*
333      * path for graph
334      * [["M", x1, y1], ["L", x2, y2], ["C", ax, ay, bx, by, x3, y3], ["L", x3, y3]]
335      */
336     this.path = [];
337     
338     this.anchors = [];
339     
340     if (strokeWidth) this.strokeWidth = strokeWidth;
341     
342     this.closePath = true;
343     this.init();
344 };
345
346
347 /*
348  * Poligone is inherited from Poliline: draws closedPath of polyline
349  */
350
351 var Foo = function () { };
352 Foo.prototype = Polyline.prototype;
353
354 Polygone.prototype = new Foo();
355
356 Polygone.prototype.rebuildPath = function(){
357     var path = [];
358     //console.log("Polygone rebuildPath");
359     for(var i = 0; i < this.getAnchorsCount(); i++){
360         var anchor = this.getAnchor(i);
361         
362         var pathType = ""
363         if (i==0)
364             pathType = "M";
365         else 
366             pathType = "L";
367         
368         var targetX = anchor.x, targetY = anchor.y;
369         
370         // anti smoothing
371         if (this.strokeWidth%2 == 1) {
372             targetX += 0.5;
373             targetY += 0.5;
374         }
375         
376         path.push([pathType, targetX, targetY]);    
377     }
378     if (this.closePath)
379         path.push(["Z"]);
380     
381     this.path = path;
382 };
383 /*
384 Polygone.prototype.transform = function(transformation){
385     this.element.transform(transformation);
386 };
387 */