简体   繁体   中英

Arbor.js edges length

I'm working with arbor.js to build networks of controversies. The issue is that when I have just a few nodes with long labels, the borders of the canvas are not respected. How can I change the length of my edges ? Or how can I make arbor respect the limits of the canvas ?

My code :

<html lang="fr">
    <head>
        <script language="javascript" type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script>
        <script language="javascript" type="text/javascript" src="path/to/arbor.js" ></script>
        <script language="javascript" type="text/javascript" src="path/to/arbor-tween.js" ></script>
        <script language="javascript" type="text/javascript" src="path/to/graphics.js" ></script>
        <script language="javascript" type="text/javascript" src="renderer.js" ></script>
        <style>
            canvas.linkable{cursor: pointer;}      
        </style>        
    </head>

<body>
    <canvas id="viewport" width="940" height="600"></canvas>
     <script language="javascript" type="text/javascript">

    var colour = {
      orange:"#EEB211",
      darkblue:"#21526a",
      purple:"#941e5e",
      limegreen:"#c1d72e",
      darkgreen:"#619b45",
      lightblue:"#009fc3",
      pink:"#d11b67",    
    }
    var data = {
      nodes:{
        principal:{ 'label':'Fins de la sélection',
                     'color':colour.darkblue, 'alpha': 0, link:'http://controverses.sciencespo-toulouse.fr/angr/' },
        controverse2:{ 'label':'La sélection, symbole du modèle productiviste de l\'agriculture ?', 
                     'color':colour.darkblue, 'alpha': 0, link:'http://controverses.sciencespo-toulouse.fr/angr/' },
        controverse3:{ 'label':'Les objectifs de la sélection entrent-ils en conflit avec ceux de la conservation ?', 
                     'color':colour.darkblue, 'alpha': 0, link:'http://controverses.sciencespo-toulouse.fr/angr/' },
       controverse4:{ 'label':'Race pure ou race croisée ?', 
                     'color':colour.darkblue, 'alpha': 0, link:'http://controverses.sciencespo-toulouse.fr/angr/' },      

      },
      edges:{
        principal:{ controverse2:{}, 
              controverse3:{}, 
              controverse4:{} }, 
      }
    }

     // Initialise arbor
    var sys = arbor.ParticleSystem()
    sys.parameters({stiffness:900, repulsion:2000, gravity:false, dt:0.015})
    sys.renderer = Renderer("#viewport");
    sys.graft(data);
    /*
    var nav = Nav("#nav")
    $(sys.renderer).bind('navigate', nav.navigate)
    $(nav).bind('mode', sys.renderer.switchMode)
    nav.init()*/
  </script>

</body>
</html>

My renderer.js :

(function(){

  Renderer = function(canvas){
    var canvas = $(canvas).get(0)
    var ctx = canvas.getContext("2d");
    var gfx = arbor.Graphics(canvas)
    var particleSystem = null
    var dom = $(canvas)

    var that = {
      init:function(system){
        particleSystem = system
        particleSystem.screenSize(canvas.width, canvas.height) 
        particleSystem.screenPadding(40)

        that.initMouseHandling()
      },

      redraw:function(){
        if (!particleSystem) return

        gfx.clear() // convenience ƒ: clears the whole canvas rect

        // draw the nodes & save their bounds for edge drawing
        var nodeBoxes = {}
        particleSystem.eachNode(function(node, pt){
          // node: {mass:#, p:{x,y}, name:"", data:{}}
          // pt:   {x:#, y:#}  node position in screen coords

          // determine the box size and round off the coords if we'll be 
          // drawing a text label (awful alignment jitter otherwise...)
          var label = node.data.label||""
          var w = ctx.measureText(""+label).width + 10
          if (!(""+label).match(/^[ \t]*$/)){
            pt.x = Math.floor(pt.x)
            pt.y = Math.floor(pt.y)
          }else{
            label = null
          }

          // draw a rectangle centered at pt
          if (node.data.color) ctx.fillStyle = node.data.color
          else ctx.fillStyle = "rgba(0,0,0,.2)"
          if (node.data.color=='none') ctx.fillStyle = "white"

          if (node.data.shape=='dot'){
            gfx.oval(pt.x-w/2, pt.y-w/2, w,w, {fill:ctx.fillStyle})
            nodeBoxes[node.name] = [pt.x-w/2, pt.y-w/2, w,w]
          }else{
            gfx.rect(pt.x-w/2, pt.y-10, w,20, 4, {fill:ctx.fillStyle})
            nodeBoxes[node.name] = [pt.x-w/2, pt.y-11, w, 22]
          }

          // draw the text
          if (label){
            ctx.font = "12px Helvetica"
            ctx.textAlign = "center"
            ctx.fillStyle = "white"
            if (node.data.color=='none') ctx.fillStyle = '#333333'
            ctx.fillText(label||"", pt.x, pt.y+4)
            ctx.fillText(label||"", pt.x, pt.y+4)
          }
        })              


        // draw the edges
        particleSystem.eachEdge(function(edge, pt1, pt2){
          // edge: {source:Node, target:Node, length:#, data:{}}
          // pt1:  {x:#, y:#}  source position in screen coords
          // pt2:  {x:#, y:#}  target position in screen coords

          var weight = edge.data.weight
          var color = edge.data.color

          if (!color || (""+color).match(/^[ \t]*$/)) color = null

          // find the start point
          var tail = intersect_line_box(pt1, pt2, nodeBoxes[edge.source.name])
          var head = intersect_line_box(tail, pt2, nodeBoxes[edge.target.name])

          ctx.save() 
            ctx.beginPath()
            ctx.lineWidth = (!isNaN(weight)) ? parseFloat(weight) : 1
            ctx.strokeStyle = (color) ? color : "#cccccc"
            ctx.fillStyle = null

            ctx.moveTo(tail.x, tail.y)
            ctx.lineTo(head.x, head.y)
            ctx.stroke()
          ctx.restore()

          // draw an arrowhead if this is a -> style edge
          if (edge.data.directed){
            ctx.save()
              // move to the head position of the edge we just drew
              var wt = !isNaN(weight) ? parseFloat(weight) : 1
              var arrowLength = 6 + wt
              var arrowWidth = 2 + wt
              ctx.fillStyle = (color) ? color : "#cccccc"
              ctx.translate(head.x, head.y);
              ctx.rotate(Math.atan2(head.y - tail.y, head.x - tail.x));

              // delete some of the edge that's already there (so the point isn't hidden)
              ctx.clearRect(-arrowLength/2,-wt/2, arrowLength/2,wt)

              // draw the chevron
              ctx.beginPath();
              ctx.moveTo(-arrowLength, arrowWidth);
              ctx.lineTo(0, 0);
              ctx.lineTo(-arrowLength, -arrowWidth);
              ctx.lineTo(-arrowLength * 0.8, -0);
              ctx.closePath();
              ctx.fill();
            ctx.restore()
          }
        })



      },
      initMouseHandling:function(){
    // no-nonsense drag and drop (thanks springy.js)
    selected = null;
    nearest = null;
    var dragged = null;
    var oldmass = 1
    var mouse_is_down = false;
    var mouse_is_moving = false


    // set up a handler object that will initially listen for mousedowns then
    // for moves and mouseups while dragging
    var handler = {
      mousemove:function(e){
        if(!mouse_is_down){
          var pos = $(canvas).offset();
          _mouseP = arbor.Point(e.pageX-pos.left, e.pageY-pos.top)
          nearest = particleSystem.nearest(_mouseP);

          if (!nearest.node) return false
          selected = (nearest.distance < 50) ? nearest : null
          if(selected && selected.node.data.link){
            dom.addClass('linkable')
          } else {
            dom.removeClass('linkable')
          }
        }
        return false
      },
      clicked:function(e){
        var pos = $(canvas).offset();
        _mouseP = arbor.Point(e.pageX-pos.left, e.pageY-pos.top)
        nearest = particleSystem.nearest(_mouseP);

        if (!nearest.node) return false
        selected = (nearest.distance < 50) ? nearest : null

        if (nearest && selected && nearest.node===selected.node){
          var link = selected.node.data.link
          if (link.match(/^#/)){
             $(that).trigger({type:"navigate", path:link.substr(1)})
          }else{
             window.open(link, "frame3")
          }
          return false
        }
      },
      mousedown:function(e){
        var pos = $(canvas).offset();
        _mouseP = arbor.Point(e.pageX-pos.left, e.pageY-pos.top)
        selected = nearest = dragged = particleSystem.nearest(_mouseP);

        if (dragged.node !== null) dragged.node.fixed = true

        mouse_is_down = true
        mouse_is_moving = false

        $(canvas).bind('mousemove', handler.dragged)
        $(window).bind('mouseup', handler.dropped)

        return false
      },
      dragged:function(e){
        var old_nearest = nearest && nearest.node._id
        var pos = $(canvas).offset();
        var s = arbor.Point(e.pageX-pos.left, e.pageY-pos.top)

        mouse_is_moving = true

        if (!nearest) return
        if (dragged !== null && dragged.node !== null){
          var p = particleSystem.fromScreen(s)
          dragged.node.p = p
        }

        return false
      },

      dropped:function(e){
        if (dragged===null || dragged.node===undefined) return
        if (dragged.node !== null) dragged.node.fixed = false
        dragged.node.tempMass = 50
        dragged = null
        selected = null
        $(canvas).unbind('mousemove', handler.dragged)
        $(window).unbind('mouseup', handler.dropped)
        _mouseP = null

        if(mouse_is_moving){
          // console.log("was_dragged")
        } else {
          handler.clicked(e)
        }

        mouse_is_down = false

        return false
      }
    }
    $(canvas).mousedown(handler.mousedown);
    $(canvas).mousemove(handler.mousemove);

  }

}
    // helpers for figuring out where to draw arrows (thanks springy.js)
    var intersect_line_line = function(p1, p2, p3, p4)
    {
      var denom = ((p4.y - p3.y)*(p2.x - p1.x) - (p4.x - p3.x)*(p2.y - p1.y));
      if (denom === 0) return false // lines are parallel
      var ua = ((p4.x - p3.x)*(p1.y - p3.y) - (p4.y - p3.y)*(p1.x - p3.x)) / denom;
      var ub = ((p2.x - p1.x)*(p1.y - p3.y) - (p2.y - p1.y)*(p1.x - p3.x)) / denom;

      if (ua < 0 || ua > 1 || ub < 0 || ub > 1)  return false
      return arbor.Point(p1.x + ua * (p2.x - p1.x), p1.y + ua * (p2.y - p1.y));
    }

    var intersect_line_box = function(p1, p2, boxTuple)
    {
      var p3 = {x:boxTuple[0], y:boxTuple[1]},
          w = boxTuple[2],
          h = boxTuple[3]

      var tl = {x: p3.x, y: p3.y};
      var tr = {x: p3.x + w, y: p3.y};
      var bl = {x: p3.x, y: p3.y + h};
      var br = {x: p3.x + w, y: p3.y + h};

      return intersect_line_line(p1, p2, tl, tr) ||
            intersect_line_line(p1, p2, tr, br) ||
            intersect_line_line(p1, p2, br, bl) ||
            intersect_line_line(p1, p2, bl, tl) ||
            false
    }

    return that
  }    

})()

I've tried to modify the length of edges directly in the script, for instance :

edges:{
        principal:{ controverse2:{length:1}, 
              controverse3:{length:1}, 
              controverse4:{length:1} }, 
      }

It doesn't work.

Thanks for your help !

为粒子系统设置此参数

 sys.parameters({ repulsion: 1000, gravity: false, dt: 0.35 }) 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM