0

In this example, I try to call getBBox in text node but it never been called due to the text node is not SVGGraphicsElement.

I am confused why text node is not SVGGraphicsElement! how can I call getBBox for this usecase?

test()
function test() {
  var data ={ 
    "nodes":  [
      {id: "A",name:'AAAA'},
      {id: "B",name:'BBBB'},
      {id: "C",name:'CCCC'},
      {id:"D",name:'DDDD'}],  
    "links":  [
      {source: "A", target: "B"},    
      {source: "B", target: "C"},   
      {source: "C", target: "A"},   
      {source: "D", target: "A"}]}

  var height = 250; var width = 400;

  var svg = d3.select("body").append("svg")   
  .attr("width",width)
  .attr("height",height)
  .style('border','1px solid red')

  var simulation = d3.forceSimulation()
  .force("link", d3.forceLink().id(function(d) { return d.id; }).distance(50))
  .force("charge", d3.forceManyBody())
  .force("center", d3.forceCenter(width / 3, height / 2));

  const zagzag = (from, to) => {
    let dx = to.x - from.x;
    let dy = to.y - from.y;
    if (Math.abs(dx) > Math.abs(dy)) {
      let mx,my,nx,ny,sx,sy
      sx = (dx>0)?1:-1
      sy = (dy>0)?1:-1
      mx = from.x + from.bbox.width/2*sx
      my = from.y
      nx = to.x - to.bbox.width/2*sx
      ny = to.y
      dx = nx - mx;
      dy = ny - my;

      return `M ${mx},${my} l ${dx/2},0 0,${dy} ${dx/2},0 `;
    }else{//vertical
      let mx,my,nx,ny,sx,sy
      sx = (dx>0)?1:-1
      sy = (dy>0)?1:-1
      mx = from.x 
      my = from.y + from.bbox.height/2*sy
      nx = to.x
      ny = to.y - to.bbox.height/2*sy
      dx = nx - mx;
      dy = ny - my;
      return `M ${mx},${my} l 0,${dy/2} ${dx},0 0,${dy/2}`; 
    }
  };

  const linelink = (from, to) => {
    const dx = to.x - from.x;
    const dy = to.y - from.y;
    let fX, fY, tX, tY;
    const sx = (dx > 0)?1:-1
    const sy = (dy > 0)?1:-1;

    if (from.bbox.width / from.bbox.height > Math.abs(dx / dy)) {
      fX = from.x + from.bbox.height * dx / dy / 2*sy;
      fY = from.y + from.bbox.height / 2 * sy;
    }
    else {
      fX = from.x + from.bbox.width / 2 * sx;
      fY = from.y + from.bbox.width * dy / dx / 2 * sx;
    }

    if (to.bbox.width / to.bbox.height > Math.abs(dx / dy)) {
      tX = to.x - to.bbox.height * dx / dy / 2 * sy;
      tY = to.y - to.bbox.height / 2 * sy;
    }
    else {
      tX = to.x - to.bbox.width / 2 * sx;
      tY = to.y - to.bbox.width * dy / dx / 2 * sx;
    }
    return ['M',fX,fY,'L',tX,tY].join(' ');  
  };

  var link = svg.append("g")
  .selectAll("line")
  .data(data.links)
  .enter().append("path")
  .attr('fill','none')
  .attr("stroke","black")

  var g = svg.append("g")
  .selectAll("text")
  .data(data.nodes)
  .enter()

  var bbox = d3.local()
  var node = g.append("text")
  .text(d => d.name)
  .attr('text-anchor','middle')
  .attr("alignment-baseline", "central")
  .attr("dominant-baseline", "central")
  .each((d,i,n) => {
    if (n[i] instanceof SVGGraphicsElement) { 
      var bbox = n[i].getBBox();
      bbox.width += 12
      bbox.height += 8
      d.bbox = bbox
      g.append('rect')
        .attr('id','rect_'+d.id)
        .attr('x',50*i) //debug code
        .attr('y',10) //debug code
        .attr('width',bbox.width)
        .attr('height',bbox.height)
        .attr('stroke','crimson')
        .attr('stroke-width',2)
        .attr('fill','none')
    }
  })
  .call(d3.drag()
        .on("drag", dragged)
        .on("end", dragended))

  simulation.nodes(data.nodes)
    .on("tick", ticked1)
    .alphaDecay(0)
    .force("link")
    .links(data.links);

  function ticked1() {
    link
      .attr("x1", function(d) { return d.source.x; })
      .attr("y1", function(d) { return d.source.y; })
      .attr("x2", function(d) { return d.target.x; })
      .attr("y2", function(d) { return d.target.y; })
      .attr('d',d =>{
      var path = linelink(d.source,d.target)
      return path
    })
    node
      .attr("x", function(d) { return d.x; })
      .attr("y", function(d) { return d.y; })
      .each(d => {
      var rect = d3.select('#rect_'+d.id)
      rect.attr('x',d.x-d.bbox.width/2)
      rect.attr('y',d.y-d.bbox.height/2)
    })
  }    

  function dragged(event,d) {
    d.fx = event.x;
    d.fy = event.y;
  }

  function dragended(event, d) {
    //d.fx = null;
    //d.fy = null;
  }

}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js"></script>
lucky1928
  • 8,708
  • 10
  • 43
  • 92
  • 1
    In your arrow function `this` is just the simulation. That has been asked here several times, please have a look at my explanation here: https://stackoverflow.com/a/45262284/5768908 – Gerardo Furtado Jun 22 '21 at 01:23
  • @Gerardo Furtado Great, thanks, I updated my question. the rect now works but after update the x/y attribute, it looks like created another copy, the original rects still there. I think it should be update location to the target. I add logs there and it only create 4 rects, but totally 8 rects on screen! – lucky1928 Jun 22 '21 at 14:18

0 Answers0