Javascript 用数学方法找出两个盒子之间的边

Javascript 用数学方法找出两个盒子之间的边,javascript,math,canvas,Javascript,Math,Canvas,我正在构建原型工具来绘制简单的图表 我需要在两个盒子之间画一个箭头,问题是我必须找到两个盒子的边缘,这样箭头线就不会与盒子相交 这是将我的问题可视化的图形: 如何找到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

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

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

这是将我的问题可视化的图形:

如何找到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];       
        }

    }
var-box1={x:1,y:10,w:30,h:30};
var-box2={x:100,y:110,w:30,h:30};
var edge1=findBoxEdge(框1,框2,1,0);
var edge2=findBoxEdge(框1,框2,2,0);
函数findBoxEdge(框1、框2、框、距离){
var c1=box1.x+box1.w/2;
变量d1=box1.y+box1.h/2;
var c2=box2.x+box2.w/2;
var d2=box2.y+box2.h/2;
变量w,h,delta_x,delta_y,s,c,e,ox,oy,d;
如果(框==1){
w=框1.w/2;
h=box1.h/2;
}否则{
w=箱2.w/2;
h=box2.h/2;
}
如果(框==1){
δx=c2-c1;
δy=d2-d1;
}否则{
δx=c1-c2;
δy=d1-d2;
}
w+=5;
h+=5;
//交叉点位于顶部或底部
如果(w*Math.abs(delta_y)>h*Math.abs(delta_x)){
如果(增量y>0){
s=[h*δx/δy,h];
c=“顶部”;
}
否则{
s=[-1*h*delta_x/delta_y,-1*h];
c=“底部”;
}
} 
否则{
//十字路口在左边或右边
如果(增量x>0){
s=[w,w*delta_y/delta_x];
c=“对”;
}
否则{
s=[-1*w,-1*delta_y/delta_x];
c=“左”;
}
}
if(typeof(distance)!=“未定义”){
//对于2e的2平行距离
e=距离;
如果(delta_y==0)ox=0;
else-ox=e*Math.sqrt(1+Math.pow(delta_x/delta_y,2))
如果(delta_x==0)oy=0;
else oy=e*Math.sqrt(1+Math.pow(delta_y/delta_x,2))

如果(delta_y!=0&&Math.abs(ox+h*(delta_x/delta_y))要在这些框之间画一条线,首先必须定义线的位置

显然,您希望从矩形A的右边缘到矩形A的左边缘绘制直线/箭头 矩形B,有点像这样:

假设您知道原点(左上
矩形
的{x,y})及其
大小
(宽度和高度),首先要确定边缘中心的位置:

var rectA, rectB; // I assume you have those data
var rectARightEdgeCenter = {
  // x is simply the origin's x plus the width
  x: rectA.origin.x + rectA.size.width,
  // for y you need to add only half the height to origin.y
  y: rectA.origin.y + rectA.size.height / 2.0
}
var rectBLeftEdgeCenter = {
  // x will be simply the origin's x
  x: rectB.origin.x,
  // y is half the height added to the origin's y, just as before
  y: rectB.origin.y + rectB.size.height / 2.0
}

更有趣的问题是,如何确定在更动态的场景中,从哪条边到另一条边,您可能希望绘制线

如果你的盒子只是从左到右堆积起来,那么给定的解决方案就合适了,
但您可能需要检查边的最小距离,以确定可能的最佳箭头。

要在这些框之间画一条线,首先必须定义线的位置

显然,您希望从矩形A的右边缘到矩形A的左边缘绘制直线/箭头 矩形B,有点像这样:

假设您知道原点(左上
矩形
的{x,y})及其
大小
(宽度和高度),首先要确定边缘中心的位置:

var rectA, rectB; // I assume you have those data
var rectARightEdgeCenter = {
  // x is simply the origin's x plus the width
  x: rectA.origin.x + rectA.size.width,
  // for y you need to add only half the height to origin.y
  y: rectA.origin.y + rectA.size.height / 2.0
}
var rectBLeftEdgeCenter = {
  // x will be simply the origin's x
  x: rectB.origin.x,
  // y is half the height added to the origin's y, just as before
  y: rectB.origin.y + rectB.size.height / 2.0
}

更有趣的问题是,如何确定在更动态的场景中,从哪条边到另一条边,您可能希望绘制线

如果你的盒子只是从左到右堆积起来,那么给定的解决方案就合适了, 但您可能需要检查边的最小距离,以确定可能的最佳箭头。

我们的目标是从两个矩形a和B的边缘画一条线,这两个矩形a和B将通过其中心绘制。 因此,我们必须确定直线穿过矩形边缘的位置。 我们可以假设我们的
Rect
是一个对象,包含
x
y
作为从左上边缘的偏移量,以及
宽度
高度
作为尺寸偏移量

这可以通过以下代码完成。您应该仔细查看的方法是
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);
};
//从点和矩形类型开始,因为它们简化了计算
变量点=函数(x,y){
返回{x:x,y:y};
};
var Rect=函数(x,y,w,h){
返回{x:x,y:y,宽度:w,高度:h};
};
var isLeftOf=函数(pt1,pt2){return pt1.x