3 //まず、最も多数のエッジを持つノードを探す。
6 function MGCanvas(canvasDOMObj){
9 this.initGraphicContext(canvasDOMObj);
10 this.tickPerSecond = 30;
12 this.tickTimer = window.setInterval(function(){ that.tick(); }, 1000/this.tickPerSecond);
13 this.nodeList = new Array();
14 this.edgeList = new Array();
17 this.canvas.onmousemove = function (e){
22 var loc = that.getMousePositionOnElement(e);
24 //console.log("x:" + loc.x);
25 //console.log("y:" + loc.y);
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);
31 MGCanvas.prototype = {
32 setGraph: function(gArray){
33 //gArray = [[Node1, Node2, Node3, ...], [[Node1, Node3], [Node3, Node2], ...]]
36 var n = function(identifier){ return that.nodeList.isIncluded(identifier, function(a, b){ return (a.identifier == b); }); };
38 for(var i = 0, iLen = p.length; i < iLen; i++){
39 this.nodeList.push(new MGNode(this, p[i]));
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])));
47 bringToCenter: function(){
48 var g = new Point2D(0, 0);
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;
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;
71 //console.log(this.tickCount);
77 for(var i = 0, iLen = p.length; i < iLen; i++){
78 nTemp = this.getVectorLength(p[i].vector);
85 n.ignoreEdgeRepulsion = 10;
92 for(var i = 0, iLen = p.length; i < iLen; i++){
93 this.nodeList[i].tick();
96 for(var i = 0, iLen = p.length; i < iLen; i++){
97 this.edgeList[i].tick();
103 dr = this.displayRect;
104 this.context.clearRect(dr.origin.x, dr.origin.y, dr.size.x, dr.size.y);
106 for(var i = 0, iLen = p.length; i < iLen; i++){
107 this.nodeList[i].draw();
110 for(var i = 0, iLen = p.length; i < iLen; i++){
111 this.edgeList[i].draw();
114 getMousePositionOnElement: function(e){
115 // http://tmlife.net/programming/javascript/javascript-mouse-pos.html
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;
123 fillRect: function(x, y, w, h){
124 var d = this.drawCoordinatesInInteger;
125 this.context.fillRect(d(x), d(y), d(w), d(h));
127 strokeRect: function(x, y, w, h){
128 var d = this.drawCoordinatesInInteger;
129 this.context.strokeRect(d(x), d(y), d(w), d(h));
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);
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();
142 this.context.stroke();
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();
152 drawText: function(text, x, y){
154 //前景をstrokeStyleで塗りつぶした文字列を描画する
157 var textsize = this.context.measureText(text);
158 this.context.fillRect(x, y, textsize.width, 20);
160 this.context.fillStyle = this.context.strokeStyle;
161 //fillText引数の座標は文字列の左下!
162 this.context.fillText(text, x, y + 20 - 1);
163 this.context.restore();
165 getVectorLengthP: function(p, q){
166 return this.getVectorLength(this.getVectorP(p, q));
168 getVectorLength: function(a){
169 return Math.sqrt(a.x * a.x + a.y * a.y);
171 getVectorP: function(p, q){
172 return new Point2D(q.x - p.x, q.y - p.y);
174 getUnitVectorP: function(p, q){
175 var e = this.getVectorP(p, q);
176 return this.getUnitVector(e);
178 getUnitVector: function(a){
179 var l = Math.sqrt(a.x * a.x + a.y * a.y);
184 getNormalUnitVectorSideOfP: function(a, b, p){
185 //直線ab上にない点pが存在する側へ向かう単位法線ベクトルを返す。
186 return this.getUnitVector(this.getNormalVectorSideOfP(a, b, p));
188 getNormalVectorSideOfP: function(a, b, p){
189 //直線ab上にない点pが存在する側へ向かう法線ベクトルを返す。
190 //pがab上にある場合は零ベクトルとなる。
191 var n = this.getVectorP(a, b);
197 i = this.getInnerVector2D(n, this.getVectorP(a, p));
199 //この法線ベクトルとapの向きが逆なので反転する。
208 getExteriorVector2D: function(a, b){
209 return a.x * b.y - a.y * b.x;
211 getInnerVector2D: function(a, b){
212 return a.x * b.x + a.y * b.y;
214 getDistanceDotAndLineP: function(p, a, b){
215 // http://www.sousakuba.com/Programming/gs_dot_line_distance.html
222 ab = this.getVectorP(a, b);
223 ap = this.getVectorP(a, p);
225 s = Math.abs(this.getExteriorVector2D(ab, ap));
226 l = this.getVectorLengthP(a, b);
229 s = this.getInnerVector2D(ap, ab);
231 //線分の範囲外なので端点aからの距離に変換
234 d = Math.sqrt(d * d + l * l);
235 } else if(s > l * l){
238 d = Math.sqrt(d * d + l * l);
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);
253 loadAIMemory: function(str){
256 // http://www.html5rocks.com/ja/tutorials/file/dndfiles/
257 handleFileSelect: function(evt){
258 evt.stopPropagation();
259 evt.preventDefault();
261 var files = evt.dataTransfer.files; // FileList object.
264 // files is a FileList of File objects. List some properties.
266 for(var i = 0, f; f = files[i]; i++){
267 var r = new FileReader();
268 r.onload = (function(file){
270 //mainAI.sendTextFromFileToAI(r.result, file.name, file.lastModifiedDate, "File");
271 that.loadAIMemory(r.result);
277 handleDragOver: function(evt){
278 evt.stopPropagation();
279 evt.preventDefault();
280 evt.dataTransfer.dropEffect = 'copy'; // Explicitly show this is a copy.
284 function MGNode(env, identifier){
286 this.identifier = identifier;
287 this.position = new Point2D(0, 0);
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;
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);
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;
311 if(!this.ignoreEdgeRepulsion){
313 //距離の近い点同士には斥力が働くとする。
314 p = this.env.nodeList;
315 for(var i = 0, iLen = p.length; i < iLen; i++){
316 var q = this.env.nodeList[i];
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;
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){
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;
352 this.ignoreEdgeRepulsion--;
357 function MGEdge(env, identifier, node0, node1){
359 this.identifier = identifier;
362 this.freeLength = 250;
366 if(this.node0 && this.node1){
367 this.env.drawLineP(this.node0.position, this.node1.position);
371 var l = this.env.getVectorLengthP(this.node0.position, this.node1.position);
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;
385 function Point2D(x, y){
389 Point2D.prototype = {
393 function Rectangle(x, y, width, height){
394 this.origin = new Point2D(x,y);
395 this.size = new Point2D(width,height);
397 Rectangle.prototype = {