How to find angle of rotation to make an SVG arc "stand up" with css3d

301 views Asked by At

enter image description here

I have arcs connecting nodes in a d3 force directed graph, the whole SVG is transformed with CSS. Now the arcs doesn't feel nice as its like lying in the ground, so I want to get them "stand up", or rotate X,Y,Z to get the effect.

I have set the transform origin to the center of the line connecting the nodes, but now I am stuck with finding the angles at which I have to rotate the arcs to make them stand up. Any idea/formula on finding the angles is much appreciated.

The code I am having right now inside the tick function looks like:

  force.on("tick", function() {
    path.attr("d", function (d) {
      var dx = d.target.x - d.source.x,
          dy = d.target.y - d.source.y,
          dr = Math.sqrt(dx * dx + dy * dy) -200;
      return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
      })
      .attr('style', function(d){
          var rotateX = 0, rotateY = 0, rotateZ = 0;
          return "transform-origin:" + (d.source.x + d.target.x)/2 + "px " + (d.source.y + d.target.y)/2 + "px" + 
                 ";transform:rotateX("+rotateX+"deg) rotateY("+rotateY+"deg) rotateZ("+rotateZ+"deg)";
      });

rotateX, rotateY and rotateZ angles are what I am looking for!

1

There are 1 answers

2
vals On BEST ANSWER

I have no practical knowledge of d3, so I have solved the problem for a standard javascript layout.

Hopefully the procedure should work the same.

On the demo, click 2 times to set the begin and end of the arc. Then click again to see it rotating in 3d.

You can repeat the cycle how many times you want.

In the transform calculus we just calculate the angle in the plane with atan2 and the horizontal and vertical differences. The real trick is to set the 90 degrees that will get the element vertical after (in notation) the plane rotation.

Note that in the snippet I am not applying 90 degress, but 80, so that the arc is still visible when viewed from above.

var sequence = 0;
var x1, y1, x2, y2;

function getCursorPosition(event) {
  var x = event.clientX;
  var y = event.clientY;
  var ele = document.getElementById('container');
  if (sequence == 0) {
    x1 = x;
    y1 = y;
    sequence = 1;
    ele.classList.remove("animate");
  } else if (sequence == 1) {
    x2 = x;
    y2 = y;
    sequence = 2;
    Compute();
  } else {
    ele.classList.add("animate");
    sequence = 0;
  }
}

function Compute() {
  var ele = document.getElementById('inner');
  var width = Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));

  ele.style.width = width + "px ";
  ele.style.height = width / 2 + "px ";
  ele.style.left = x1 + "px ";
  ele.style.top = y1 + "px ";
  var angle = Math.atan2(y2 - y1, x2 - x1);
  var deg = angle * 180.0 / Math.PI;

  ele.style.transform = "rotate(" + deg + "deg) rotateX(80deg) "
    /*
        var style = "width:" + width + "px ";
        style += "left: " + x1 + "px ";
        style += "top: " + y1 + "px ";
        ele.setAttribute("style",style);
    */
}
.container {
  width: 600px;
  height: 400px;
  border: solid 1px green;
  perspective: 1000px;
  transform-style: preserve-3d;
  position: absolute;
  background-image: repeating-linear-gradient(white 0px, wheat 50px, white 100px);
}
#inner {
  width: 100px;
  height: 50px;
  border-radius: 0px 0px 100px 100px;
  background-color: green;
  position: absolute;
  transform-origin: left top;
  transform-style: preserve-3d;
}
.animate {
  animation: rota 15s 1;
}
@keyframes rota {
  from {
    transform: perspective(1000px) rotateX(0deg);
  }
  to {
    transform: perspective(1000px) rotateX(360deg);
  }
}
<div class="container" id="container" onclick="getCursorPosition(event)">
  <div id="inner">
  </div>
</div>