this.root = new Array();
//
this.nodeList = new Array();
+ this.edgeList = new Array();
//
this.syncPHPURL = syncPHPURL;
this.isEnabledNetDB = true;
//
- this.callback_updatedNode = null; // function(nodeinstance){};
+ this.callback_updatedNode = null; // function(nodeInstance){};
+ this.callback_updatedEdge = null; // function(edgeInstance){};
}
MemoryDB.prototype = {
UUID_Null: "00000000-0000-0000-0000-000000000000",
// XMLHttpRequest オブジェクトを作成
rq = new XMLHttpRequest();
}catch(e){}
+ if(rq){
+ return rq;
+ }
// Internet Explorer
try{
rq = new ActiveXObject('MSXML2.XMLHTTP.6.0');
}catch(e){}
+ if(rq){
+ return rq;
+ }
try{
rq = new ActiveXObject('MSXML2.XMLHTTP.3.0');
}catch(e){}
+ if(rq){
+ return rq;
+ }
try{
rq = new ActiveXObject('MSXML2.XMLHTTP');
}catch(e){}
- if(rq == null){
- return null;
+ if(rq){
+ return rq;
}
- return rq;
+ return null;
},
requestObjectDisableCache: function(rq){
//call after open request.
syncDB: function(){
// MySQLと同期
// 定期的に呼び出されることを想定
+ this.syncDBNode();
+ this.syncDBEdge();
+ },
+ syncDBNode: function(){
var r, a, d;
- if(this.root.length == 0){
+ if(this.nodeList.length == 0){
// 初回同期時は全て取得
r = this.sendRequestSync("GET", this.syncPHPURL + "?action=getallnode", null);
} else{
// 差分のみを取得
d = this.getNodeFromUUID(this.UUID_Node_MemoryDBNetworkTimestamp).identifier;
- r = this.sendRequestSync("GET", this.syncPHPURL + "?action=getnodemod&t=" + d, null);
+ r = this.sendRequestSync("GET", this.syncPHPURL + "?action=getallnodemod&t=" + d, null);
}
a = r.split("\n");
for(var i = 0, iLen = a.length; i < iLen; i++){
}
this.updateNodeInternal(d[2], d[1], d[0]);
}
- //console.log(this.root);
+ },
+ syncDBEdge: function(){
+ var r, a, d;
+ if(this.edgeList.length == 0){
+ // 初回同期時は全て取得
+ r = this.sendRequestSync("GET", this.syncPHPURL + "?action=getalledge", null);
+ } else{
+ // 差分のみを取得
+ /*
+ d = this.getNodeFromUUID(this.UUID_Node_MemoryDBNetworkTimestamp).identifier;
+ r = this.sendRequestSync("GET", this.syncPHPURL + "?action=getallnodemod&t=" + d, null);
+ */
+ return;
+ }
+ a = r.split("\n");
+ for(var i = 0, iLen = a.length; i < iLen; i++){
+ try{
+ d = eval(a[i]);
+ } catch(e){
+ console.log(i + ": " + e + "\n");
+ continue;
+ }
+ if(d === undefined){
+ continue;
+ }
+ if( d[0] != this.UUID_Node_MemoryDBNetworkTimestamp &&
+ d[0] != this.UUID_Node_MemoryDBNetworkResponseCode){
+ // edge data
+ this.updateEdgeInternal(d[2], d[3], d[1], d[0]);
+ } else{
+ // node data
+ this.updateNodeInternal(d[2], d[1], d[0]);
+ }
+ }
},
updateNode: function(ident, tid, nid){
// 該当タグのデータを書き換え、もしくは新規作成する。
this.callback_updatedNode(t);
}
},
+ updateEdge: function(nid0, nid1, tid, eid){
+ // 該当タグのデータを書き換え、もしくは新規作成する。
+ // 可能であればネットワークに反映する
+ // eid(nodeid)は省略可能で、省略時は新たなUUIDが自動的に付与される
+ // tid(typeid)も省略可能で、省略時はNullUUIDが付与される
+ // 戻り値はMemoryDBEdgeTagインスタンス
+ // エラー発生時はundefinedを返す。
+ this.updateEdgeInternal(nid0, nid1, tid, eid, true);
+ },
+ updateEdgeInternal: function(nid0, nid1, tid, eid, enableSync){
+ // 基本的にローカルデータのみ変更
+ // enableSync == trueでネットワーク同期する
+ var t, s, r;
+ if(!tid){
+ tid = this.UUID_Null;
+ }
+ if(!eid){
+ eid = this.createUUID();
+ }
+ // 存在確認
+ t = this.getEdgeFromUUID(eid);
+ if(t){
+ // 変更
+ /*
+ t.typeid = tid;
+ t.identifier = ident;
+ if(enableSync && this.isEnabledNetDB){
+ s = this.syncPHPURL + "?action=updatenode";
+ s += "&nid=" + encodeURIComponent(nid);
+ s += "&tid=" + encodeURIComponent(tid);
+ s += "&ident=" + encodeURIComponent(ident);
+ r = this.sendRequestSync("GET", s, null);
+ //console.log(r);
+ }
+ */
+ return;
+ } else{
+ // 新規作成
+ t = new MemoryDBEdgeTag(eid, tid, nid0, nid1);
+ this.root.push(t);
+ this.edgeList.push(t);
+ if(enableSync && this.isEnabledNetDB){
+ s = this.syncPHPURL + "?action=addedge";
+ s += "&eid=" + encodeURIComponent(eid);
+ s += "&tid=" + encodeURIComponent(tid);
+ s += "&nid0=" + encodeURIComponent(nid0);
+ s += "&nid1=" + encodeURIComponent(nid1);
+ r = this.sendRequestSync("GET", s, null);
+ console.log(r);
+ }
+ }
+ if(this.callback_updatedEdge){
+ this.callback_updatedEdge(t);
+ }
+ },
createUUID: function(){
var f = this.createUUIDSub;
return (f() + f() + "-" + f() + "-" + f() + "-" + f() + "-" + f() + f() + f());
getNodeFromUUID: function(nodeid){
return this.nodeList.isIncluded(nodeid, function(a, b){ return a.nodeid == b; });
},
+ getEdgeFromUUID: function(edgeid){
+ return this.edgeList.isIncluded(edgeid, function(a, b){ return a.edgeid == b; });
+ },
}
function MemoryDBNodeTag(nodeid, typeid, identifier){
}
-function MemoryDBEdgeTag(typeUUIDStr){
- this.uuid = null;
+function MemoryDBEdgeTag(edgeid, typeid, nodeid0, nodeid1){
+ this.edgeid = edgeid;
+ this.typeid = typeid;
+ this.nodeid0 = nodeid0;
+ this.nodeid1 = nodeid1;
}
MemoryDBEdgeTag.prototype = {
define("QUERY_ADD_Node_TYPES", "sssi");
//
define("QUERY_ADD_Edge", "
-insert into Node (
+insert into Edge (
edgeid, typeid, nodeid0, nodeid1, modtimestamp
) values (
unhex(replace(?, '-', '')), unhex(replace(?, '-', '')), unhex(replace(?, '-', '')), unhex(replace(?, '-', '')), ?
");
define("QUERY_UPDATE_Node_TYPES", "ssis");
//
-
+define("QUERY_UPDATE_Edge", "
+UPDATE Edge SET
+ typeid=unhex(replace(?, '-', '')), nodeid0=unhex(replace(?, '-', '')), nodeid1=unhex(replace(?, '-', '')), modtimestamp=?
+WHERE
+ edgeid=unhex(replace(?, '-', ''))
+");
+define("QUERY_UPDATE_Edge_TYPES", "sssis");
+//
define("QUERY_SELECT_ALL_Node", "select hex(nodeid), hex(typeid), identifier from Node");
define("QUERY_SELECT_ALL_Node_With_modtimestamp", "select hex(nodeid), hex(typeid), identifier, modtimestamp from Node");
define("QUERY_SELECT_ALL_Edge", "select hex(edgeid), hex(typeid), hex(nodeid0), hex(nodeid1) from Edge");
+define("QUERY_SELECT_ALL_Edge_With_modtimestamp", "select hex(edgeid), hex(typeid), hex(nodeid0), hex(nodeid1), modtimestamp from Edge");
define("QUERY_SELECT_modified_Node", "select hex(nodeid), hex(typeid), identifier from Node WHERE modtimestamp>?");
define("QUERY_SELECT_modified_Node_TYPES", "i");
// put timestamp tag
echoMemoryDBNetworkTimestamp();
exit();
- } else if(strcmp($action, 'getnodemod') == 0){
+ } else if(strcmp($action, 'getalledge') == 0){
+ $stmt = $db->prepare(QUERY_SELECT_ALL_Edge);
+ $stmt->execute();
+ //
+ $stmt->store_result();
+ if($stmt->errno != 0){
+ exitWithResponseCode("A0518702-C90C-4785-B5EA-1A213DD0205B");
+ }
+
+ $stmt->bind_result($uuid, $typeid, $nid0, $nid1);
+ while($stmt->fetch()){
+ echoEdge(
+ getFormedUUIDString($uuid),
+ getFormedUUIDString($typeid),
+ getFormedUUIDString($nid0),
+ getFormedUUIDString($nid1)
+ );
+ echo(PHP_EOL);
+ }
+ $stmt->close();
+ // put timestamp tag
+ echoMemoryDBNetworkTimestamp();
+ exit();
+ } else if(strcmp($action, 'getallnodemod') == 0){
if(isset($_GET['t'])){
$ts = $_GET['t'];
} else{
echo($stmt->num_rows);
$stmt->close();
exit(" OK " . getTimeStampMs());
+ } else if(strcmp($action, 'viewalledge') == 0){
+ $stmt = $db->prepare(QUERY_SELECT_ALL_Edge_With_modtimestamp);
+ $stmt->execute();
+ //
+ $stmt->store_result();
+ if($stmt->errno != 0){
+ exitWithResponseCode("A0518702-C90C-4785-B5EA-1A213DD0205B");
+ }
+
+ $stmt->bind_result($uuid, $typeid, $nid0, $nid1, $mts);
+ while($stmt->fetch()){
+ echoEdge(
+ getFormedUUIDString($uuid),
+ getFormedUUIDString($typeid),
+ getFormedUUIDString($nid0),
+ getFormedUUIDString($nid1)
+ );
+ echo(' @' . $mts);
+ echo("<br />");
+ }
+ echo($stmt->num_rows);
+ $stmt->close();
+ exit(" OK " . getTimeStampMs());
} else if(strcmp($action, 'addnode') == 0){
if(isset($_GET['nid'])){
$nodeid = $_GET['nid'];
}
$stmt->close();
exitWithResponseCode("cea95615-649c-4837-9e24-0c968fa57647", "OK");
+ } else if(strcmp($action, 'addedge') == 0){
+ if(isset($_GET['eid'])){
+ $edgeid = $_GET['eid'];
+ } else{
+ exitWithResponseCode("A0518702-C90C-4785-B5EA-1A213DD0205B", "edgeid needed.");
+ }
+ if(isset($_GET['tid'])){
+ $typeid = $_GET['tid'];
+ } else{
+ exitWithResponseCode("A0518702-C90C-4785-B5EA-1A213DD0205B", "typeid needed.");
+ }
+ if(isset($_GET['nid0'])){
+ $nid0 = $_GET['nid0'];
+ } else{
+ exitWithResponseCode("A0518702-C90C-4785-B5EA-1A213DD0205B", "nodeid0 needed.");
+ }
+ if(isset($_GET['nid1'])){
+ $nid1 = $_GET['nid1'];
+ } else{
+ exitWithResponseCode("A0518702-C90C-4785-B5EA-1A213DD0205B", "nodeid1 needed.");
+ }
+
+ $stmt = $db->prepare(QUERY_ADD_Edge);
+ $mts = getTimeStampMs();
+ $stmt->bind_param(QUERY_ADD_Edge_TYPES, $edgeid, $typeid, $nid0, $nid1, $mts);
+ $stmt->execute();
+ //
+ $stmt->store_result();
+ if($stmt->errno != 0){
+ exitWithResponseCode("A0518702-C90C-4785-B5EA-1A213DD0205B", mysqli_error($db));
+ }
+ $stmt->close();
+ exitWithResponseCode("cea95615-649c-4837-9e24-0c968fa57647", "OK");
+ } else if(strcmp($action, 'updateedge') == 0){
+ if(isset($_GET['eid'])){
+ $edgeid = $_GET['eid'];
+ } else{
+ exitWithResponseCode("A0518702-C90C-4785-B5EA-1A213DD0205B", "edgeid needed.");
+ }
+ if(isset($_GET['tid'])){
+ $typeid = $_GET['tid'];
+ } else{
+ exitWithResponseCode("A0518702-C90C-4785-B5EA-1A213DD0205B", "typeid needed.");
+ }
+ if(isset($_GET['nid0'])){
+ $nid0 = $_GET['nid0'];
+ } else{
+ exitWithResponseCode("A0518702-C90C-4785-B5EA-1A213DD0205B", "nodeid0 needed.");
+ }
+ if(isset($_GET['nid1'])){
+ $nid1 = $_GET['nid1'];
+ } else{
+ exitWithResponseCode("A0518702-C90C-4785-B5EA-1A213DD0205B", "nodeid1 needed.");
+ }
+
+ $stmt = $db->prepare(QUERY_UPDATE_Edge);
+ $mts = getTimeStampMs();
+ $stmt->bind_param(QUERY_UPDATE_Edge_TYPES, $typeid, $nid0, $nid1, $mts, $edgeid);
+ $stmt->execute();
+ //
+ $stmt->store_result();
+ if($stmt->errno != 0){
+ exitWithResponseCode("A0518702-C90C-4785-B5EA-1A213DD0205B", mysqli_error($db));
+ }
+ $stmt->close();
+ exitWithResponseCode("cea95615-649c-4837-9e24-0c968fa57647", "OK");
}
}
echo('["' . $nid .'","' . $tid .'","' . $ident . '"]');
}
+function echoEdge($eid, $tid, $nid0, $nid1)
+{
+ echo('["' . $eid .'","' . $tid .'","' . $nid0 .'","' . $nid1 . '"]');
+}
+
function connectDB()
{
$db = new mysqli('localhost', DATABASE_USER, DATABASE_PWD, DATABASE_NAME);
var memdb;
onload = function() {
+ var that = this;
+ var DOM_Node0ID = document.getElementById("nidBox0");
+ var DOM_Node0Ident = document.getElementById("identBox0");
+ var DOM_Node1ID = document.getElementById("nidBox1");
+ var DOM_Node1Ident = document.getElementById("identBox1");
+ var DOM_EdgeID = document.getElementById("eidBox");
+ var DOM_EdgeTypeID = document.getElementById("etidBox");
+
memdb = new MemoryDB("./../memdb/memdb.php");
mgmain = new MGCanvas(document.getElementById("mainCanvas"));
+ document.getElementById("updateButton0").onclick = function(){
+ mgmain.setIdentifierForSelectedNode(DOM_Node0Ident.value);
+ };
+ document.getElementById("updateButton1").onclick = function(){
+ mgmain.setIdentifierForSelectedNode(DOM_Node1Ident.value, true);
+ };
+ document.getElementById("connectNodesButton").onclick = function(){
+ memdb.updateEdge(DOM_Node0ID.value, DOM_Node1ID.value);
+ };
+ mgmain.callback_selectedNodeChanged = function(newNode){
+ if(newNode){
+ DOM_Node0ID.value = newNode.nodeid;
+ DOM_Node0Ident.value = newNode.identifier;
+ } else{
+ DOM_Node0ID.value = "not selected";
+ DOM_Node0Ident.value = "";
+ }
+ };
+ mgmain.callback_selectedNodeDestinationChanged = function(newNode){
+ if(newNode){
+ DOM_Node1ID.value = newNode.nodeid;
+ DOM_Node1Ident.value = newNode.identifier;
+ } else{
+ DOM_Node1ID.value = "not selected";
+ DOM_Node1Ident.value = "";
+ }
+ };
+ mgmain.callback_selectedEdgeChanged = function(newEdge){
+ if(newEdge){
+ DOM_EdgeID.value = newEdge.edgeid;
+ DOM_EdgeTypeID.value = newEdge.typeid;
+ } else{
+ DOM_EdgeID.value = "not selected";
+ DOM_EdgeTypeID.value = "";
+ }
+ };
mgmain.setSourceMemoryDB(memdb);
}
</script>
<body>
<h1>Mind Graph Canvas</h1>
<canvas id="mainCanvas" width="1024" height="768" style="border:1px solid #000000;"></canvas>
-<br />
+<h3>Control</h3>
<button onclick="mgmain.bringToCenter();">Center</button>
<button onclick="mgmain.isPaused = !mgmain.isPaused;">Freeze</button>
<button onclick="mgmain.zoomIn();">+</button>
<button onclick="mgmain.moveViewRelative(0, 10);">↓</button>
<button onclick="mgmain.moveViewRelative(-10, 0);">←</button>
<button onclick="mgmain.moveViewRelative(10, 0);">→</button>
-<br />
-identifier:<input id="identBox" type="text"></input>
-<br />
-<button onclick="mgmain.setIdentifierForSelectedNode(getElementById('identBox').value);">setNodeIdent</button>
-<button onclick="memdb.updateNode(getElementById('identBox').value);">addNode</button>
+<h3>Node0</h3>
+id:<input disabled id="nidBox0" type="text" size="50"></input>
+identifier:<input id="identBox0" type="text" size="25"></input>
+<button id="updateButton0">updateNode</button>
+<h3>Node1</h3>
+id:<input disabled id="nidBox1" type="text" size="50"></input>
+identifier:<input id="identBox1" type="text" size="25"></input>
+<button id="updateButton1">updateNode</button>
+<h3>Edge</h3>
+id :<input disabled id="eidBox" type="text" size="50"></input>
+type :<input disabled id="etidBox" type="text" size="50"></input><button id="connectNodesButton">connectNodes</button>
</body>
</html>
\ No newline at end of file
function MGCanvas(canvasDOMObj){
var that = this;
-
+ //
this.initGraphicContext(canvasDOMObj);
+ //
this.tickPerSecond = 30;
this.tickCount = 0;
this.tickTimer = window.setInterval(function(){ that.tick(); }, 1000 / this.tickPerSecond);
this.isPaused = false;
this.isEnabledAutomaticTracking = true;
+ //
this.nodeList = new Array();
this.edgeList = new Array();
+ //
this.selectedNode = null;
+ this.callback_selectedNodeChanged = null; //function(newNodeInstance){};
+ //
+ this.selectedNodeDestination = null;
+ this.callback_selectedNodeDestinationChanged = null; //function(newNodeInstance){};
+ //
this.selectedEdge = null;
+ this.callback_selectedEdgeChanged = null; //function(newEdgeInstance){};
+ //
this.srcMemoryDB = null;
this.srcMemoryDBSyncPerTick = 60;
this.srcMemoryDBSyncCount = this.srcMemoryDBSyncPerTick;
-
- var that = this;
+
window.addEventListener('keydown', function(event){
switch(event.keyCode){
case 37: //左カーソル
var p = that.convertPointToGraphLayerFromCanvasLayerP(that.lastMousePosition);
//console.log(p.x + "," + p.y);
var node = that.getNodeAtPointP(p);
- that.selectNode(node);
+ that.selectNode(node, e.shiftKey);
var edge = that.getEdgeAtPointP(p);
that.selectEdge(edge);
that.isMouseDown = true;
// 新規追加
n = new MGNode(that, t.identifier);
n.nodeid = t.nodeid;
+ n.typeid = t.typeid;
that.nodeList.push(n);
} else{
// 更新
n.identifier = t.identifier;
+ n.typeid = t.typeid;
}
- }
+ };
+ mdb.callback_updatedEdge = function(t){
+ var e;
+ var n = function(nid){ return that.nodeList.isIncluded(nid, function(a, b){ return (a.nodeid == b); }); };
+ e = that.edgeList.isIncluded(t.edgeid, function(a, b){ return a.edgeid == b; });
+ if(!e){
+ // 新規追加
+ e = new MGEdge(that, "", n(t.nodeid0), n(t.nodeid1));
+ e.edgeid = t.edgeid;
+ e.typeid = t.typeid;
+ that.edgeList.push(e);
+ } else{
+ // 更新
+ // e.identifier
+ e.node0 = n(t.nodeid0);
+ e.node1 = n(t.nodeid1);
+ e.typeid = t.typeid;
+ }
+ };
},
bringToCenter: function(){
// 重心を求めて、それを表示オフセットに設定する
this.context.translate(w, h);
this.displayRect = new Rectangle(-w, -h, this.canvas.width, this.canvas.height);
this.currentScale = 1;
- this.zoomOut();
+ //this.zoomOut();
this.positionOffset = new Point2D(0, 0);
},
convertPointToGraphLayerFromCanvasLayerP: function(pCanvas){
}
return null;
},
- selectNode: function(node){
- if(this.selectedNode){
- this.selectedNode.isSelected = false;
- }
- if(node){
- node.isSelected = true;
+ selectNode: function(node, destNode){
+ if(!destNode){
+ // 通常のノード選択
+ if(this.selectedNode == node){
+ return;
+ }
+ if(this.selectedNode){
+ this.selectedNode.isSelected = false;
+ }
+ if(node){
+ node.isSelected = true;
+ }
+ this.selectedNode = node;
+ if(this.callback_selectedNodeChanged){
+ this.callback_selectedNodeChanged(this.selectedNode);
+ }
+ } else{
+ // 接続先のノード選択(Shiftキーを押しながら)
+ if(this.selectedNodeDestination == node){
+ return;
+ }
+ if(this.selectedNodeDestination){
+ this.selectedNodeDestination.isSelected = false;
+ }
+ if(node){
+ node.isSelected = true;
+ }
+ this.selectedNodeDestination = node;
+ if(this.callback_selectedNodeDestinationChanged){
+ this.callback_selectedNodeDestinationChanged(this.selectedNodeDestination);
+ }
}
- this.selectedNode = node;
},
selectEdge: function(edge){
+ if(this.selectedEdge == edge){
+ return;
+ }
if(this.selectedEdge){
this.selectedEdge.isSelected = false;
}
edge.isSelected = true;
}
this.selectedEdge = edge;
+ if(this.callback_selectedEdgeChanged){
+ this.callback_selectedEdgeChanged(this.selectedEdge);
+ }
},
- setIdentifierForSelectedNode: function(str){
- if(this.selectedNode){
- if(this.srcMemoryDB){
- this.srcMemoryDB.updateNode(str, this.selectedNode.typeid, this.selectedNode.nodeid);
- } else{
- this.selectedNode.identifier = str;
+ setIdentifierForSelectedNode: function(str, destNode){
+ if(!destNode){
+ if(this.selectedNode){
+ if(this.srcMemoryDB){
+ this.srcMemoryDB.updateNode(str, this.selectedNode.typeid, this.selectedNode.nodeid);
+ } else{
+ this.selectedNode.identifier = str;
+ }
+ }
+ } else{
+ if(this.selectedNodeDestination){
+ if(this.srcMemoryDB){
+ this.srcMemoryDB.updateNode(str, this.selectedNodeDestination.typeid, this.selectedNodeDestination.nodeid);
+ } else{
+ this.selectedNodeDestination.identifier = str;
+ }
}
}
},
function MGEdge(env, identifier, node0, node1){
this.env = env;
+ //
this.identifier = identifier;
this.node0 = node0;
this.node1 = node1;
+ //
+ this.edgeid = null;
+ this.typeid = null;
+ //
this.freeLength = 250;
//
this.strokeStyle = "rgba(0, 0, 0, 0.5)";
this.env.context.strokeStyle = this.strokeStyle;
}
if(this.node0 && this.node1){
- this.drawCurvedLineP(this.node0.position, this.node1.position);
- this.env.strokeRect(this.centerPoint.x - 8, this.centerPoint.y - 8, 16, 16);
+ //this.drawCurvedLineP(this.node0.position, this.node1.position);
+ this.env.drawLineP(this.node0.position, this.node1.position)
+ //this.env.strokeRect(this.centerPoint.x - 8, this.centerPoint.y - 8, 16, 16);
if(this.identifier){
this.env.drawText(this.identifier.toString(), this.centerPoint.x, this.centerPoint.y);
}