Dropped the _new in vector lib, Node and edge creation/modification hookup with openerp

bzr revid: fva@openerp.com-20120222140047-oqpj2axm35wswyei
This commit is contained in:
Frédéric van der Essen 2012-02-22 15:00:47 +01:00
parent a444a588cd
commit 9af0dcc56d
3 changed files with 122 additions and 259 deletions

View File

@ -111,34 +111,16 @@ openerp.web.DiagramView = openerp.web.View.extend({
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",
@ -148,89 +130,48 @@ openerp.web.DiagramView = openerp.web.View.extend({
"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';
CuteNode.double_click_callback = function(cutenode){
self.add_edit_node( cutenode.id, self.node );
}
var node = r[shape](n.node.x, n.node.y).attr({
"fill": n.node.color
CuteEdge.double_click_callback = function(cuteedge){
self.add_edit_node(cuteedge.id,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
});
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();
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());
_.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);
});
}
});*/
return {label:""}; // TODO destroy edge on cancel
}
},
add_edit_node: function(id, model, defaults) {

View File

@ -1,5 +1,6 @@
(function(window){
// this serves as the end of an edge when creating a link
@ -61,7 +62,10 @@
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);
@ -289,7 +293,7 @@
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
@ -314,12 +318,22 @@
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
@ -329,6 +343,7 @@
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
@ -342,7 +357,7 @@
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);
@ -358,12 +373,12 @@
}
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){
@ -377,6 +392,14 @@
}
}
}
/*
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 ;
@ -431,12 +454,31 @@
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) {
@ -460,6 +502,7 @@
/*
window.onload = function(){
//Example
var style = { "background" :'url("grid.png")',
"edge" :"#A0A0A0",
"edge_label" :"#555",
@ -470,15 +513,17 @@ window.onload = function(){
"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);
}*/

View File

@ -6,24 +6,17 @@
// 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
@ -102,149 +95,55 @@
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(){
@ -256,45 +155,25 @@
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;
};
//rotate this vector counterclockwise by deg degrees
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 new Vec2(px,py);
};
//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);
Vec2.prototype.rotate_deg = function(deg){
return this.rotate(deg * deg2rad);
};
//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(){
@ -359,8 +238,8 @@
// 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
@ -368,7 +247,7 @@
}
// 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;
@ -383,18 +262,16 @@
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;
};
@ -470,7 +347,7 @@
};
// 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)