Dropped the _new in vector lib, Node and edge creation/modification hookup with openerp
authorFrédéric van der Essen <fva@openerp.com>
Wed, 22 Feb 2012 14:00:47 +0000 (15:00 +0100)
committerFrédéric van der Essen <fva@openerp.com>
Wed, 22 Feb 2012 14:00:47 +0000 (15:00 +0100)
bzr revid: fva@openerp.com-20120222140047-oqpj2axm35wswyei

addons/web_diagram/static/src/js/diagram.js
addons/web_diagram/static/src/js/graph.js
addons/web_diagram/static/src/js/vec2js/vec2.js

index cd360dc..4e43960 100644 (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';
-
-            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) {
index 11c2909..77a4d6c 100644 (file)
@@ -1,5 +1,6 @@
 
 (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",
@@ -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);
 }*/
 
index 9572060..e7c0371 100644 (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
     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)