javascript - 寻找两个盒子之间的边缘的数学

标签 javascript math canvas

我正在构建原型(prototype)工具来绘制简单的图表。

我需要在两个盒子之间画一个箭头,问题是我必须找到两个盒子的边缘,以便箭头线不与盒子相交。

这是可视化我的问题的图: Algorithm

如何求 x1,y1 和 x2,y2 ?

--更新--

经过 2 天的寻找解决方案后,这是我使用的示例和函数:

var box1 = { x:1,y:10,w:30,h:30 };
var box2 = { x:100,y:110,w:30,h:30 };

var edge1 = findBoxEdge(box1,box2,1,0);
var edge2 = findBoxEdge(box1,box2,2,0);

function findBoxEdge(box1,box2,box,distant) {
    var c1 = box1.x + box1.w/2;
    var d1 = box1.y + box1.h/2;
    var c2 = box2.x + box2.w/2;
    var d2 = box2.y + box2.h/2;
    var w,h,delta_x,delta_y,s,c,e,ox,oy,d;

    if (box == 1) {
        w = box1.w/2;
        h = box1.h/2;
    } else {
        w = box2.w/2;
        h = box2.h/2;
    }

    if (box == 1) {
        delta_x = c2-c1;
        delta_y = d2-d1;
    } else {
        delta_x = c1-c2;
        delta_y = d1-d2;
    }
    w+=5;
    h+=5;

    //intersection is on the top or bottom
    if (w*Math.abs(delta_y) > h * Math.abs(delta_x)) {
        if (delta_y > 0) {
            s = [h*delta_x/delta_y,h];
            c = "top";
        }
        else {
            s = [-1*h*delta_x/delta_y,-1*h];
            c = "bottom";
        }
    } 
    else {
    //intersection is on the left or right
        if (delta_x > 0) {
            s = [w,w*delta_y/delta_x];
            c = "right";
        }
        else {
            s = [-1*w,-1*delta_y/delta_x];
            c = "left";
        }
    }

    if (typeof(distant) != "undefined") {
        //for 2 paralel distant of 2e
        e = distant;

        if (delta_y == 0) ox = 0;
        else ox = e*Math.sqrt(1+Math.pow(delta_x/delta_y,2))

        if (delta_x == 0) oy = 0;
        else oy = e*Math.sqrt(1+Math.pow(delta_y/delta_x,2))

        if (delta_y != 0 && Math.abs(ox + h * (delta_x/delta_y)) <= w) {
            d = [sgn(delta_y)*(ox + h * (delta_x/delta_y)),sgn(delta_y)*h];
        } 
        else if (Math.abs(-1*oy + (w * delta_y/delta_x)) <= h) {
            d = [sgn(delta_x)*w,sgn(delta_x)*(-1*oy + w * (delta_y/delta_x))];
        }
        if (delta_y != 0 && Math.abs(-1*ox+(h * (delta_x/delta_y))) <= w) {
            d = [sgn(delta_y)*(-1*ox + h * (delta_x/delta_y)),sgn(delta_y)*h];
        }
        else if (Math.abs(oy + (w * delta_y/delta_x)) <= h) {
            d = [sgn(delta_x)*w,sgn(delta_x)*(oy + w * (delta_y/delta_x))];
        }

        if (box == 1) {
            return [Math.round(c1 +d[0]),Math.round(d1 +d[1]),c];
        } else {
            return [Math.round(c2 +d[0]),Math.round(d2 +d[1]),c];       
        }
    } else {
        if (box == 1) {
            return [Math.round(c1 +s[0]),Math.round(d1 +s[1]),c];
        } else {
            return [Math.round(c2 +s[0]),Math.round(d2 +s[1]),c];       
        }

    }

最佳答案

tl;dr -> Look at the jsbin code-example

我们的目标是从两个矩形 A 和 B 的边缘画一条线,并穿过它们的中心。 因此,我们必须确定线穿过矩形边缘的位置。 我们可以假设我们的 Rect 是一个包含 xy 的对象,作为距左上角的偏移量和 width高度作为尺寸偏移。

different rectangles

这可以通过以下代码来完成。您应该仔细查看的方法是 pointOnEdge

// starting with Point and Rectangle Types, as they ease calculation
var Point = function(x, y) { 
  return { x: x, y: y }; 
};
var Rect  = function(x, y, w, h) {
  return { x: x, y: y, width: w, height: h };
};
var isLeftOf = function(pt1, pt2) { return pt1.x < pt2.x; };
var isAbove  = function(pt1, pt2) { return pt1.y < pt2.y; };
var centerOf = function(rect) {
  return Point(
    rect.x + rect.width / 2,
    rect.y + rect.height / 2
  );
};
var gradient = function(pt1, pt2) {
  return (pt2.y - pt1.y) / (pt2.x - pt1.x);
};    
var aspectRatio = function(rect) { return rect.height / rect.width; };

// now, this is where the fun takes place
var pointOnEdge = function(fromRect, toRect) {
  var centerA = centerOf(fromRect),
      centerB = centerOf(toRect),
      // calculate the gradient from rectA to rectB
      gradA2B = gradient(centerA, centerB),
      // grab the aspectRatio of rectA
      // as we want any dimensions to work with the script
      aspectA = aspectRatio(fromRect),

      // grab the half values, as they are used for the additional point
      h05 = fromRect.width / 2,
      w05 = fromRect.height / 2,

      // the norm is the normalized gradient honoring the aspect Ratio of rectA
      normA2B = Math.abs(gradA2B / aspectA),

      // the additional point
      add = Point(
        // when the rectA is left of rectB we move right, else left
        (isLeftOf(centerA, centerB) ? 1 : -1) * h05,
        // when the rectA is below
        (isAbove(centerA, centerB)  ? 1 : -1) * w05
      );

  // norm values are absolute, thus we can compare whether they are
  // greater or less than 1
  if (normA2B < 1) {
    // when they are less then 1 multiply the y component with the norm
    add.y *= normA2B;
  } else {
    // otherwise divide the x component by the norm
    add.x /= normA2B;
  }
  // this way we will stay on the edge with at least one component of the result
  // while the other component is shifted towards the center

  return Point(centerA.x + add.x, centerA.y + add.y);
};

我写了一个jsbin ,你可以用一些盒子来测试(下半部分,在ready方法中):

您可能想看看 a little Geometry helper我前段时间在 prototype.js 上面写过

我真的希望这能帮助您解决问题;)

关于javascript - 寻找两个盒子之间的边缘的数学,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9239899/

相关文章:

javascript - Chrome 不会加载 jquery 或 soundcloud api

gwt - 更改绘制 Canvas 的线宽(gwt)

html - 使用 HTML5 Canvas 动画作为背景

javascript - 我们如何在运行时修改Windows应用商店应用程序包的 Assets

javascript - 声明变量时使用 var 和不使用 var 有什么区别?

javascript - 如何转到 json 数组中的下一个和上一个值?

计算具有不同成员数的组的点总和的算法

c - 编写余弦定律计算

math - 使用 d3.js 绘制 0 到 360 度的圆弧

HTML5 Canvas - origin-clean 标志如何设置为 false?