OSDN Git Service

36e6541cb4a9ab06bdaf94d137fb3307450a007d
[chnosproject/AI004.git] / mgcanvas / mgcanvas.js
1 //文字列をキーとする。
2
3 //まず、最も多数のエッジを持つノードを探す。
4 //それを起点にして、エッジをたどる。
5
6 function MGCanvas(canvasDOMObj){
7         var that = this;
8
9         this.initGraphicContext(canvasDOMObj);
10         this.tickPerSecond = 30;
11         this.tickCount = 0;
12         this.tickTimer = window.setInterval(function(){ that.tick(); }, 1000/this.tickPerSecond);
13         this.nodeList = new Array();
14         this.edgeList = new Array();
15         
16         var that = this;
17         this.canvas.onmousemove = function (e){
18                 if(!e){
19                         //for IE
20                         e = window.event;
21                 }
22                 var loc = that.getMousePositionOnElement(e);
23                 // 出力テスト
24                 //console.log("x:" + loc.x);
25                 //console.log("y:" + loc.y);
26         };
27         //ファイルの入力を受け付ける場合はコメントを外す
28         //this.canvas.addEventListener('dragover', function(evt){ return that.handleDragOver(evt); }, false);
29         //this.canvas.addEventListener('drop', function(evt){ return that.handleFileSelect(evt); }, false);
30 }
31 MGCanvas.prototype = {
32         setGraph: function(gArray){
33                 //gArray = [[Node1, Node2, Node3, ...], [[Node1, Node3], [Node3, Node2], ...]]
34                 var that = this;
35                 var p = gArray[0];
36                 var n = function(identifier){ return that.nodeList.isIncluded(identifier, function(a, b){ return (a.identifier == b); }); };
37                 
38                 for(var i = 0, iLen = p.length; i < iLen; i++){
39                         this.nodeList.push(new MGNode(this, p[i]));
40                 }
41                 
42                 p = gArray[1];
43                 for(var i = 0, iLen = p.length; i < iLen; i++){
44                         this.edgeList.push(new MGEdge(this, p[i][2], n(p[i][0]), n(p[i][1])));
45                 }
46         },
47         bringToCenter: function(){
48                 var g = new Point2D(0, 0);
49                 var p;
50                 p = this.nodeList;
51                 for(var i = 0, iLen = p.length; i < iLen; i++){
52                         g.x += p[i].position.x;
53                         g.y += p[i].position.y;
54                 }
55                 g.x /= p.length;
56                 g.y /= p.length;
57                 for(var i = 0, iLen = p.length; i < iLen; i++){
58                         p[i].position.x -= g.x;
59                         p[i].position.y -= g.y;
60                 }
61         },
62         tick: function(){
63                 var p;
64                 var t;
65                 var dr;
66                 var n = null;
67                 var nMax = 0;
68                 var nTemp;
69                 
70                 this.tickCount++;
71                 //console.log(this.tickCount);
72                 
73                 //
74                 // Check
75                 //
76                 p = this.nodeList;
77                 for(var i = 0, iLen = p.length; i < iLen; i++){
78                         nTemp = this.getVectorLength(p[i].vector);
79                         if(nMax < nTemp){
80                                 n = p[i];
81                                 nMax = nTemp;
82                         }
83                 }
84                 if(n){
85                         n.ignoreEdgeRepulsion = 10;
86                 }
87                 
88                 //
89                 // Move
90                 //
91                 p = this.nodeList;
92                 for(var i = 0, iLen = p.length; i < iLen; i++){
93                         this.nodeList[i].tick();
94                 }
95                 p = this.edgeList;
96                 for(var i = 0, iLen = p.length; i < iLen; i++){
97                         this.edgeList[i].tick();
98                 }
99                 
100                 //
101                 // Refresh
102                 //
103                 dr = this.displayRect;
104                 this.context.clearRect(dr.origin.x, dr.origin.y, dr.size.x, dr.size.y);
105                 p = this.nodeList;
106                 for(var i = 0, iLen = p.length; i < iLen; i++){
107                         this.nodeList[i].draw();
108                 }
109                 p = this.edgeList;
110                 for(var i = 0, iLen = p.length; i < iLen; i++){
111                         this.edgeList[i].draw();
112                 }
113         },
114         getMousePositionOnElement: function(e){
115                 // http://tmlife.net/programming/javascript/javascript-mouse-pos.html
116                 // retv:
117                 var retv = new Object();
118                 var rect = e.target.getBoundingClientRect();
119                 retv.x = e.clientX - rect.left;
120                 retv.y = e.clientY - rect.top;
121                 return retv;
122         },
123         fillRect: function(x, y, w, h){
124                 var d = this.drawCoordinatesInInteger;
125                 this.context.fillRect(d(x), d(y), d(w), d(h));
126         },
127         strokeRect: function(x, y, w, h){
128                 var d = this.drawCoordinatesInInteger;
129                 this.context.strokeRect(d(x), d(y), d(w), d(h));
130         },
131         drawCoordinatesInInteger: function(coordinateElement){
132                 //from http://www.html5rocks.com/ja/tutorials/canvas/performance/
133                 // With a bitwise or.
134                 return ((0.5 + coordinateElement) | 0);
135         },
136         drawCircle: function(x, y, r){
137                 var d = this.drawCoordinatesInInteger;
138                 this.context.beginPath();
139                 this.context.arc(d(x), d(y), d(r), 0, 2 * Math.PI);
140                 this.context.closePath();
141                 this.context.fill();
142                 this.context.stroke();
143         },
144         drawLineP: function(p, q){
145                 var d = this.drawCoordinatesInInteger;
146                 this.context.beginPath();
147                 this.context.moveTo(d(p.x), d(p.y));
148                 this.context.lineTo(d(q.x), d(q.y));
149                 this.context.closePath();
150                 this.context.stroke();
151         },
152         drawText: function(text, x, y){
153                 //背景をfillStyle
154                 //前景をstrokeStyleで塗りつぶした文字列を描画する
155                 //塗りつぶし高さは20px固定
156                 //座標は文字列の左上の座標となる
157                 var textsize = this.context.measureText(text);
158                 this.context.fillRect(x, y, textsize.width, 20);
159                 this.context.save();
160                 this.context.fillStyle = this.context.strokeStyle;
161                 //fillText引数の座標は文字列の左下!
162                 this.context.fillText(text, x, y + 20 - 1);
163                 this.context.restore();
164         },
165         getVectorLengthP: function(p, q){
166                 return this.getVectorLength(this.getVectorP(p, q));
167         },
168         getVectorLength: function(a){
169                 return Math.sqrt(a.x * a.x + a.y * a.y);
170         },
171         getVectorP: function(p, q){
172                 return new Point2D(q.x - p.x, q.y - p.y);
173         },
174         getUnitVectorP: function(p, q){
175                 var e = this.getVectorP(p, q);
176                 return this.getUnitVector(e);
177         },
178         getUnitVector: function(a){
179                 var l = Math.sqrt(a.x * a.x + a.y * a.y);
180                 a.x /= l;
181                 a.y /= l;
182                 return a;
183         },
184         getNormalUnitVectorSideOfP: function(a, b, p){
185                 //直線ab上にない点pが存在する側へ向かう単位法線ベクトルを返す。
186                 return this.getUnitVector(this.getNormalVectorSideOfP(a, b, p));
187         },
188         getNormalVectorSideOfP: function(a, b, p){
189                 //直線ab上にない点pが存在する側へ向かう法線ベクトルを返す。
190                 //pがab上にある場合は零ベクトルとなる。
191                 var n = this.getVectorP(a, b);
192                 var t = n.x;
193                 var i;
194                 n.x = -n.y;
195                 n.y = t;
196                 
197                 i = this.getInnerVector2D(n, this.getVectorP(a, p));
198                 if(i < 0){
199                         //この法線ベクトルとapの向きが逆なので反転する。
200                         n.x = -n.x;
201                         n.y = -n.y;
202                 } else if(i == 0){
203                         n.x = 0;
204                         n.y = 0;
205                 }
206                 return n;
207         },
208         getExteriorVector2D: function(a, b){
209                 return a.x * b.y - a.y * b.x;
210         },
211         getInnerVector2D: function(a, b){
212                 return a.x * b.x + a.y * b.y;
213         },
214         getDistanceDotAndLineP: function(p, a, b){
215                 // http://www.sousakuba.com/Programming/gs_dot_line_distance.html
216                 var ab;
217                 var ap;
218                 var s;
219                 var l;
220                 var d;
221                 
222                 ab = this.getVectorP(a, b);
223                 ap = this.getVectorP(a, p);
224                 
225                 s = Math.abs(this.getExteriorVector2D(ab, ap));
226                 l = this.getVectorLengthP(a, b);
227                 d = (s / l);
228                 
229                 s = this.getInnerVector2D(ap, ab);
230                 if(s < 0){
231                         //線分の範囲外なので端点aからの距離に変換
232                         //端点から垂線の足までの距離
233                         l = - (s / l);
234                         d = Math.sqrt(d * d + l * l);
235                 } else if(s > l * l){
236                         //同様に端点bからの距離に変換
237                         l = s / l;
238                         d = Math.sqrt(d * d + l * l);
239                 }
240                 return d;
241         },
242         initGraphicContext: function(newCanvas){
243                 this.canvas = newCanvas;
244                 this.context = this.canvas.getContext('2d');
245                 this.context.fillStyle = "rgba(255,255,255,0.5)";
246                 this.context.strokeStyle = "rgba(0, 0, 0, 1)";
247                 this.context.font = "normal 20px sans-serif";
248                 var w = this.canvas.width / 2;
249                 var h = this.canvas.height / 2;
250                 this.context.translate(w, h);
251                 this.displayRect = new Rectangle(-w, -h, this.canvas.width, this.canvas.height);
252         },
253         loadAIMemory: function(str){
254                 console.log(str);
255         },
256         // http://www.html5rocks.com/ja/tutorials/file/dndfiles/
257         handleFileSelect: function(evt){
258                 evt.stopPropagation();
259                 evt.preventDefault();
260         
261                 var files = evt.dataTransfer.files; // FileList object.
262                 var that = this;
263                 
264                 // files is a FileList of File objects. List some properties.
265                 var output = [];
266                 for(var i = 0, f; f = files[i]; i++){
267                         var r = new FileReader();
268                         r.onload = (function(file){
269                                 return function(e){
270                                         //mainAI.sendTextFromFileToAI(r.result, file.name, file.lastModifiedDate, "File");
271                                         that.loadAIMemory(r.result);
272                                 }
273                         })(f);
274                         r.readAsText(f);
275                 }
276         },
277         handleDragOver: function(evt){
278                 evt.stopPropagation();
279                 evt.preventDefault();
280                 evt.dataTransfer.dropEffect = 'copy'; // Explicitly show this is a copy.
281         },
282 }
283
284 function MGNode(env, identifier){
285         this.env = env;
286         this.identifier = identifier;
287         this.position = new Point2D(0, 0);
288         this.size = 10;
289         //ランダムな初期ベクトルをもつ。
290         this.vector = new Point2D(Math.random() * 2 - 1, Math.random() * 2 - 1);
291         this.friction = (100 - 7) / 100;
292         this.repulsionLengthNode = 100;
293         this.repulsionLengthEdge = 75;
294         this.ignoreEdgeRepulsion = 0;
295 }
296 MGNode.prototype = {
297         draw: function(){
298                 this.env.drawCircle(this.position.x, this.position.y, this.size);
299                 this.env.drawText(this.identifier.toString(), this.position.x + 10, this.position.y + 10);
300         },
301         tick: function(){
302                 var e;
303                 var p;
304                 var l;
305                 var q;
306                 this.position.x += this.vector.x;
307                 this.position.y += this.vector.y;
308                 this.vector.x *= this.friction;
309                 this.vector.y *= this.friction;
310                 
311                 if(!this.ignoreEdgeRepulsion){
312                         //node
313                         //距離の近い点同士には斥力が働くとする。
314                         p = this.env.nodeList;
315                         for(var i = 0, iLen = p.length; i < iLen; i++){
316                                 var q = this.env.nodeList[i];
317                                 if(q != this){
318                                         l = this.env.getVectorLengthP(this.position, q.position);
319                                         if(l < this.repulsionLengthNode && l != 0){
320                                                 e = this.env.getUnitVectorP(q.position, this.position);
321                                                 e.x *= this.repulsionLengthNode / l;
322                                                 e.y *= this.repulsionLengthNode / l;
323                                                 this.vector.x += e.x;
324                                                 this.vector.y += e.y;
325                                         }
326                                 }
327                         }
328                         //edge
329                         //自分を端点に含まないエッジとの距離が近い場合、そのエッジから遠ざかろうとする。
330                         p = this.env.edgeList;
331                         for(var i = 0, iLen = p.length; i < iLen; i++){
332                                 var q = this.env.edgeList[i];
333                                 if(q.node0 != this && q.node1 != this){
334                                         l = this.env.getDistanceDotAndLineP(this.position, q.node0.position, q.node1.position);
335                                         if(l < this.repulsionLengthEdge && l != 0){
336                                                 if(l < 1){
337                                                         l = 1;
338                                                 }
339                                                 e = this.env.getNormalUnitVectorSideOfP(q.node0.position, q.node1.position, this.position);
340                                                 e.x *= this.repulsionLengthEdge / l;
341                                                 e.y *= this.repulsionLengthEdge / l;
342                                                 this.vector.x += e.x;
343                                                 this.vector.y += e.y;
344                                                 q.node0.vector.x -= e.x / 2;
345                                                 q.node0.vector.y -= e.y / 2;
346                                                 q.node1.vector.x -= e.x / 2;
347                                                 q.node1.vector.y -= e.y / 2;
348                                         }
349                                 }
350                         }
351                 } else{
352                         this.ignoreEdgeRepulsion--;
353                 }
354         },
355 }
356
357 function MGEdge(env, identifier, node0, node1){
358         this.env = env;
359         this.identifier = identifier;
360         this.node0 = node0;
361         this.node1 = node1;
362         this.freeLength = 250;
363 }
364 MGEdge.prototype = {
365         draw: function(){
366                 if(this.node0 && this.node1){
367                         this.env.drawLineP(this.node0.position, this.node1.position);
368                 }
369         },
370         tick: function(){
371                 var l = this.env.getVectorLengthP(this.node0.position, this.node1.position);
372                 var e;
373                 if(l > this.freeLength){
374                         e = this.env.getUnitVectorP(this.node0.position, this.node1.position);
375                         e.x *= l / this.freeLength;
376                         e.y *= l / this.freeLength;
377                         this.node0.vector.x += e.x;
378                         this.node0.vector.y += e.y;
379                         this.node1.vector.x -= e.x;
380                         this.node1.vector.y -= e.y;
381                 }
382         },
383 }
384
385 function Point2D(x, y){
386         this.x = x;
387         this.y = y;
388 }
389 Point2D.prototype = {
390         
391 }
392
393 function Rectangle(x, y, width, height){
394         this.origin = new Point2D(x,y);
395         this.size = new Point2D(width,height);
396 }
397 Rectangle.prototype = {
398         
399 }
400