this.get_diagram_info();
}
},
- select_node: function (node, element) {
- if (!this.selected_node) {
- this.selected_node = node;
- element.attr('stroke', 'red');
- return;
- }
- // Re-click selected node, deselect it
- if (node.id === this.selected_node.id) {
- this.selected_node = null;
- element.attr('stroke', 'black');
- return;
- }
- this.add_edit_node(null, this.connector, {
- act_from: this.selected_node.id,
- act_to: node.id
- });
- },
draw_diagram: function(result) {
+ var self = this;
console.log(result);
var res_nodes = result['nodes'];
var res_edges = result['conn'];
var id_to_node = {}
- var edge_list = [];
- var style = { "background" : 'url("grid.png")',
- "edge" : "#A0A0A0",
+ var style = { "edge" : "#A0A0A0",
"edge_label" : "#555",
"text" : "#333",
"outline" : "#000",
"node_size_x" : 90,
"node_size_y" : 60,
"edge_spacing" : 100,
- "edge_label_font_size" : 9 };
+ "edge_label_font_size" : 10 };
+
+ $('#dia-canvas *').remove(); // remove previous diagram
var r = new Raphael(document.getElementById("dia-canvas"), '100%','500px');
var graph = new CuteGraph(r,style);
_.each(res_nodes, function(node) {
- id_to_node[node.id] = new CuteNode( graph,
- node.x,
- node.y,
- CuteGraph.wordwrap(node.name, 17),
- node.shape === 'rectangle' ? 'rect' : 'circle',
- node.color === 'white' ? style.white : style.gray );
-
+ var n = new CuteNode( graph,
+ node.x,
+ node.y,
+ CuteGraph.wordwrap(node.name, 17),
+ node.shape === 'rectangle' ? 'rect' : 'circle',
+ node.color === 'white' ? style.white : style.gray );
+ n.id = node.id;
+ id_to_node[node.id] = n;
});
_.each(res_edges, function(edge) {
- edge_list.push( new CuteEdge( graph,
+ var e = new CuteEdge( graph,
CuteGraph.wordwrap(edge.signal, 32),
id_to_node[edge.s_id],
- id_to_node[edge.d_id] ));
+ id_to_node[edge.d_id] );
+ e.id = edge.id;
});
- /*
- //Custom logic
- this.selected_node = null;
- this.active_model = result['id_model'];
- this.parent_field = result.parent_field;
- var diagram = new Graph();
- var self = this;
- var renderer = function(r, n) {
- var shape = (n.node.shape === 'rectangle') ? 'rect' : 'ellipse';
-
- var node = r[shape](n.node.x, n.node.y).attr({
- "fill": n.node.color
- });
-
- var nodes = r.set(node, r.text(n.node.x, n.node.y, (n.label || n.id)))
- .attr("cursor", "pointer")
- .dblclick(function() {
- self.add_edit_node(n.node.id, self.node);
- })
- .mousedown(function () { node.moved = false; })
- .mousemove(function () { node.moved = true; })
- .click(function () {
- // Ignore click from move event
- if (node.moved) { return; }
- self.select_node(n.node, node);
- });
-
- if (shape === 'rect') {
- node.attr({width: "60", height: "44"});
- node.next.attr({"text-anchor": "middle", x: n.node.x + 20, y: n.node.y + 20});
- } else {
- node.attr({rx: "40", ry: "20"});
- }
-
- return nodes;
- };
-
- _.each(res_nodes, function(res_node) {
- diagram.addNode(res_node['name'],{node: res_node,render: renderer});
- });
-
- // Id for Path(Edges)
- var edge_ids = [];
-
- _.each(res_connectors, function(connector, index) {
- edge_ids.push(index);
- diagram.addEdge(connector['source'], connector['destination'], {directed : true, label: connector['signal']});
- });
-
- self.$element.find('.diagram').empty();
+ CuteNode.double_click_callback = function(cutenode){
+ self.add_edit_node( cutenode.id, self.node );
+ }
- var layouter = new Graph.Layout.Ordered(diagram);
- var render_diagram = new Graph.Renderer.Raphael('dia-canvas', diagram, $('div#dia-canvas').width(), $('div#dia-canvas').height());
+ CuteEdge.double_click_callback = function(cuteedge){
+ self.add_edit_node(cuteedge.id,self.connector);
+ }
- _.each(diagram.edges, function(edge, index) {
- if(edge.connection) {
- edge.connection.fg.attr({cursor: "pointer"}).dblclick(function() {
- self.add_edit_node(edge_ids[index], self.connector);
- });
- }
- });*/
+ CuteEdge.creation_callback = function(node_start, node_end){
+ console.log("creating edge from:",node_start," to:",node_end);
+ self.add_edit_node(null, self.connector, {
+ act_from: node_start.id,
+ act_to: node_end.id
+ });
+ return {label:""}; // TODO destroy edge on cancel
+ }
},
add_edit_node: function(id, model, defaults) {
(function(window){
+
// this serves as the end of an edge when creating a link
graph.creating_edge = false;
self.edge_tmp.remove();
if(graph.target_node && graph.target_node != node){
- new GraphEdge(graph,'new edge!', node,graph.target_node);
+ edge_prop = GraphEdge.creation_callback(node,graph.target_node);
+ if(edge_prop){
+ new GraphEdge(graph,edge_prop.label, node,graph.target_node);
+ }
}
}
conn_circle.drag(drag_move,drag_down,drag_up);
for(var i = 0; i < edges.length; i++){
edges[i].label_disable();
}
- set_pos(this.opos.add_new_xy(dx,dy));
+ set_pos(this.opos.add_xy(dx,dy));
}
var drag_up = function(){
//we re-enable the
node_fig.hover(hover_in,hover_out);
node_label.hover(hover_in,hover_out);
+ function double_click(){
+ GraphNode.double_click_callback(self);
+ }
+ node_fig.dblclick(double_click);
+ node_label.dblclick(double_click);
+
this.connectors.push(new Connector(graph,this,-sx/2,0));
this.connectors.push(new Connector(graph,this,sx/2,0));
this.connectors.push(new Connector(graph,this,0,-sy/2));
this.connectors.push(new Connector(graph,this,0,sy/2));
}
+ GraphNode.double_click_callback = function(node){
+ console.log("double click from node:",node);
+ }
+
// creates a new edge with label 'label' from start to end. start and end must implement get_pos_*,
// if tmp is true, the edge is not added to the graph, used for drag edges. TODO pass graph in constructor,
// replace tmp == false by graph == null
var curvature = 0; // 0 = straight, != 0 curved
var s,e; // positions of the start and end point of the line between start and end
var mc; // position of the middle of the curve (bezier control point)
+ var mc1,mc2; // control points of the cubic bezier for the loop edges
var elfs = graph.style.edge_label_font_size || 10 ;
var label_enabled = true;
this.uid = 0; // unique id used to order the curved edges
var cpos = path.getTotalLength() * 0.5;
var cindex = Math.abs(Math.floor(curvature));
var mod = ((cindex % 3)) * (elfs * 3.1) - (elfs * 0.5);
- var verticality = Math.abs(end.get_pos().sub_new(start.get_pos()).normalize().dot_xy(0,1));
+ var verticality = Math.abs(end.get_pos().sub(start.get_pos()).normalize().dot_xy(0,1));
verticality = Math.max(verticality-0.5,0)*2;
var lpos = path.getPointAtLength(cpos + mod * verticality);
}
s = start.get_pos();
e = end.get_pos();
- mc = s.lerp_new(e,0.5); //middle of the line s->e
- var se = e.sub_new(s);
- se.normalize();
- se.rotate_deg(-90);
- se.scale(curvature * graph.style.edge_spacing);
- mc.add(se);
+ mc = s.lerp(e,0.5); //middle of the line s->e
+ var se = e.sub(s);
+ se = se.normalize();
+ se = se.rotate_deg(-90);
+ se = se.scale(curvature * graph.style.edge_spacing);
+ mc = mc.add(se);
if(start.get_bound){
var col = start.get_bound().collide_segment(s,mc);
if(col.length > 0){
}
}
}
+ /*
+ function update_loop_pos(){
+ s = start.get_pos();
+ mc = s.add_new(Vec2.new_polar_deg(graph.style.edge_loop_radius,45*self.uid));
+ var p = mc.normalize_new().rotate_deg(90);
+ mc1 = mc.add_new
+ */
+
function make_line(){
return "M" + s.x + "," + s.y + "L" + e.x + "," + e.y ;
edge_label.remove();
}
+ function double_click(){
+ GraphEdge.double_click_callback(self);
+ }
+ edge.dblclick(double_click);
+ edge_label.dblclick(double_click);
+
this.label_enable = label_enable;
this.label_disable = label_disable;
this.update = update;
this.remove = remove;
-
}
+
+ GraphEdge.double_click_callback = function(edge){
+ console.log("double click from edge:",edge);
+ }
+
+ // this is the default edge creation callback. It is called before an edge is created
+ // It returns an object containing the properties of the edge.
+ // If it returns null, the edge is not created.
+ GraphEdge.creation_callback = function(start,end){
+ var edge_prop = {};
+ edge_prop.label = 'new edge!';
+ return edge_prop;
+ }
+
// returns a new string with the same content as str, but with lines of maximum 'width' characters.
// lines are broken on words, or into words if a word is longer than 'width'
function wordwrap( str, width) {
/*
window.onload = function(){
+ //Example
var style = { "background" :'url("grid.png")',
"edge" :"#A0A0A0",
"edge_label" :"#555",
"white" :"#FFF",
"node_size_x" : 110,
"node_size_y" : 80,
- "edge_spacing" : 100 };
+ "edge_spacing" : 100,
+ "edge_label_font_size" : 10
+ "edge_loop_radius": 50 };
var r = new Raphael(document.getElementById("canvas_container"),'100%','100%');
var g = new CuteGraph(r,style);
- var n1 = new GraphNode(g,100,250,'Hello World','circle',colors.white);
- var n2 = new GraphNode(g,400,250,'Hello Planet','rect',colors.white);
- var n3 = new GraphNode(g,250,400,'Lonely Node','rect',colors.gray);
- var e1 = new GraphEdge(g,'test',n1,n2);
+ var n1 = new CuteNode(g,100,250,'Hello World','circle',colors.white);
+ var n2 = new CuteNode(g,400,250,'Hello Planet','rect',colors.white);
+ var n3 = new CuteNode(g,250,400,'Lonely Node','rect',colors.gray);
+ var e1 = new CuteEdge(g,'test',n1,n2);
}*/
// A Javascript 2D vector library
// conventions :
// method that returns a float value do not modify the vector
- // method that implement operators are applied onto the calling vector eg:
+ // method that implement operators return a new vector with the modifications without
+ // modifying the calling vector or the parameters.
//
- // v1.add(v2); // v2 is added onto v1, v2 is modified, v1 is not
- //
- // but the parameters are never modified
- // those methods also return the result so that the calls can be chained.
- // method that implement operators are usually also available with a '_new'
- // suffix. those method do not modify the calling vector and return the result
- // as a new vector instead.
- //
- // v3 = v1.add_new(v2) // v3 is set to v1 + v2. v1 and v2 are not modified
+ // v3 = v1.add(v2); // v3 is set to v1 + v2, v1, v2 are not modified
//
// methods that take a single vector as a parameter are usually also available with
// q '_xy' suffix. Those method takes two floats representing the x,y coordinates of
// the vector parameter and allow you to avoid to needlessly create a vector object :
//
- // v1.add(new Vec2(3,4));
- // v1.add_xy(3,4); //equivalent to previous line
+ // v2 = v1.add(new Vec2(3,4));
+ // v2 = v1.add_xy(3,4); //equivalent to previous line
//
// angles are in radians by default but method that takes angle as parameters
// or return angle values usually have a variant with a '_deg' suffix that works in degrees
Vec2.prototype.clone = function(){
return new Vec2(this.x,this.y);
};
- // sets the coordinate of this vector to (0,0)
- Vec2.prototype.zero = function(){
- this.x = 0;
- this.y = 0;
- return this;
- };
- // sets the coordinates of this to be equal to the coordinates of v
- Vec2.prototype.set = function(v){
- this.x = v.x;
- this.y = v.y;
- return this;
- };
- // sets the coordinate of this to be equal to the vector (x,y)
- Vec2.prototype.set_xy = function(x,y){
- this.x = x;
- this.y = y;
- return this;
- };
- // sets this to be the sum of this and vector v
- Vec2.prototype.add = function(v){
- this.x += v.x;
- this.y += v.y;
- return this;
- };
- // sets this to be the sum of this and the vector (x,y)
- Vec2.prototype.add_xy = function(x,y){
- this.x += x;
- this.y += y;
- return this;
- };
// return the sum of this and vector v as a new vector
- Vec2.prototype.add_new = function(v){
+ Vec2.prototype.add = function(v){
return new Vec2(this.x+v.x,this.y+v.y);
};
// return the sum of this and vector (x,y) as a new vector
- Vec2.prototype.add_new_xy = function(x,y){
+ Vec2.prototype.add_xy = function(x,y){
return new Vec2(this.x+x,this.y+y);
};
- // sets this to be (this - v) where v is a vector and - is the vector substraction
- Vec2.prototype.sub = function(v){
- this.x -= v.x;
- this.y -= v.y;
- return this;
- };
- // sets this to be (this - (x,y)) where - is the vector substraction
- Vec2.prototype.sub_xy = function(x,y){
- this.x -= x;
- this.y -= y;
- return this;
- };
// returns (this - v) as a new vector where v is a vector and - is the vector substraction
- Vec2.prototype.sub_new = function(v){
+ Vec2.prototype.sub = function(v){
return new Vec2(this.x-v.x,this.y-v.y);
};
// returns (this - (x,y)) as a new vector where - is vector substraction
- Vec2.prototype.sub_new_xy = function(x,y){
+ Vec2.prototype.sub_xy = function(x,y){
return new Vec2(this.x-x,this.y-y);
};
- // sets this to be (this * v) where v is a vector and * is the by component product and
- Vec2.prototype.mult = function(v){
- this.x *= v.x;
- this.y *= v.y;
- return this;
- };
- // sets this to be (this * (x,y) ) where v is a vector and * is the by component product
- Vec2.prototype.mult_xy = function(x,y){
- this.x *= x;
- this.y *= y;
- return this;
- };
// return (this * v) as a new vector where v is a vector and * is the by component product
- Vec2.prototype.mult_new = function(v){
+ Vec2.prototype.mult = function(v){
return new Vec2(this.x*v.x,this.y*v.y);
};
// return (this * (x,y)) as a new vector where * is the by component product
- Vec2.prototype.mult_new_xy = function(x,y){
+ Vec2.prototype.mult_xy = function(x,y){
return new Vec2(this.x*x,this.y*y);
};
- // multiply all components of this vector by float f
- Vec2.prototype.scale = function(f){
- this.x *= f;
- this.y *= f;
- return this;
- };
// return this scaled by float f as a new fector
- Vec2.prototype.scale_new = function(f){
+ Vec2.prototype.scale = function(f){
return new Vec2(this.x*f, this.y*f);
};
- //sets this vector to be the negative of itself
- Vec2.prototype.neg = function(f){
- this.x = -this.x;
- this.y = -this.y;
- return this;
- };
// return the negation of this vector
- Vec2.prototype.neg_new = function(f){
+ Vec2.prototype.neg = function(f){
return new Vec2(-this.x,-this.y);
};
- // normalizes this vector
- Vec2.prototype.normalize = function(){
- var len = this.len();
- if(len == 0){
- this.x = 1;
- }else if(len != 1){
- this.scale(1.0/len);
- }
- return this;
- };
// return this vector normalized as a new vector
- Vec2.prototype.normalize_new = function(){
+ Vec2.prototype.normalize = function(){
var len = this.len();
if(len == 0){
return new Vec2(0,1);
}else if(len != 1){
- return this.scale_new(1.0/len);
+ return this.scale(1.0/len);
}
return new Vec2(this.x,this.y);
};
- // sets the length of this vector to float l without changing its angle. (negative values of l will invert direction)
- Vec2.prototype.set_len = function(l){
- this.normalize();
- this.scale(l);
- return this;
- };
// return a new vector with the same direction as this vector of length float l. (negative values of l will invert direction)
- Vec2.prototype.set_len_new = function(l){
- var v = this.normalize_new();
- v.scale(l);
- return v;
- };
- // projects this vector onto the vector v
- Vec2.prototype.project = function(v){
- var d = this.dot(v);
- this.set(v);
- this.normalize();
- this.scale(d);
- return this;
+ Vec2.prototype.set_len = function(l){
+ return this.normalize().scale(l);
};
// return the projection of this onto the vector v as a new vector
- Vec2.prototype.project_new = function(v){
- var vc = this.clone();
- vc.project(v);
- return vc;
+ Vec2.prototype.project = function(v){
+ return v.set_len(this.dot(v));
};
// return a string representation of this vector
Vec2.prototype.toString = function(){
str += "]";
return str;
};
- // rotate this vector counterclockwise by rad radians.
+ //return this vector counterclockwise rotated by rad radians as a new vector
Vec2.prototype.rotate = function(rad){
var c = Math.cos(rad);
var s = Math.sin(rad);
var px = this.x * c - this.y *s;
var py = this.x * s + this.y *c;
- this.x = px;
- this.y = py;
- return this;
+ return new Vec2(px,py);
};
- //rotate this vector counterclockwise by deg degrees
+ //return this vector counterclockwise rotated by deg degrees as a new vector
Vec2.prototype.rotate_deg = function(deg){
return this.rotate(deg * deg2rad);
};
- //return this vector counterclockwise rotated by rad radians as a new vector
- Vec2.prototype.rotate_new = function(rad){
- var v = this.clone();
- return v.rotate(rad);
- };
- //return this vector counterclockwise rotated by deg degrees as a new vector
- Vec2.prototype.rotate_deg_new = function(deg){
- var v = this.clone();
- return v.rotate_deg(deg);
- };
//linearly interpolate this vector towards the vector v by float factor alpha.
// alpha == 0 : does nothing
// alpha == 1 : sets this to v
Vec2.prototype.lerp = function(v,alpha){
var inv_alpha = 1 - alpha;
- this.x = this.x * inv_alpha + v.x * alpha;
- this.y = this.y * inv_alpha + v.y * alpha;
- };
- // returns this vector lerped to v by alpha as a new vector
- Vec2.prototype.lerp_new = function(v,alpha){
- var inv_alpha = 1 - alpha;
- var v2 = new Vec2( this.x * inv_alpha + v.x * alpha,
+ return new Vec2( this.x * inv_alpha + v.x * alpha,
this.y * inv_alpha + v.y * alpha );
- return v2;
-
};
// returns the angle between this vector and the vector (1,0) in radians
Vec2.prototype.angle = function(){
// make all computations in a space where the ellipse is a circle
// centered on zero
var c = new Vec2(this.cx,this.cy);
- a = a.sub_new(c).mult_xy(1/this.hx,1/this.hy);
- b = b.sub_new(c).mult_xy(1/this.hx,1/this.hy);
+ a = a.sub(c).mult_xy(1/this.hx,1/this.hy);
+ b = b.sub(c).mult_xy(1/this.hx,1/this.hy);
if(a.len_sq() < 1 && b.len_sq() < 1){ //both points inside the ellipse
}
// compute the roots of the intersection
- var ab = b.sub_new(a);
+ var ab = b.sub(a);
var A = (ab.x*ab.x + ab.y*ab.y);
var B = 2*( ab.x*a.x + ab.y*a.y);
var C = a.x*a.x + a.y*a.y - 1;
var u2 = (-B - u) / (2*A);
if(u1 >= 0 && u1 <= 1){
- var pos = a.clone();
- pos.add(ab.scale_new(u1));
+ var pos = a.add(ab.scale(u1));
collisions.push(pos);
}
if(u1 != u2 && u2 >= 0 && u2 <= 1){
- var pos = a.clone();
- pos.add(ab.scale_new(u2));
+ var pos = a.add(ab.scale(u2));
collisions.push(pos);
}
for(var i = 0; i < collisions.length; i++){
- collisions[i].mult_xy(this.hx,this.hy);
- collisions[i].add_xy(this.cx,this.cy);
+ collisions[i] = collisions[i].mult_xy(this.hx,this.hy);
+ collisions[i] = collisions[i].add_xy(this.cx,this.cy);
}
return collisions;
};
};
// returns true if the ellipse contains the position defined by the vector 'vec'
BEllipse.prototype.contains_vec = function(v){
- v = v.mult_new_xy(this.hx,this.hy);
+ v = v.mult_xy(this.hx,this.hy);
return v.len_sq() <= 1;
};
// returns true if the ellipse contains the position (x,y)