数学找到两个盒子之间的边缘

时间:2012-02-11 11:38:34

标签: javascript math canvas

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

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

这是可视化我的问题的图纸: 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];       
        }

    }

2 个答案:

答案 0 :(得分:3)

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

我们的目标是从两个矩形A&amp;的边缘画一条线。 B将通过他们的中心画出来。 因此,我们必须确定穿过Rect的边缘穿过的位置。 我们可以假设我们的Rect是一个包含xy的对象,偏离左上角,widthheight作为维度偏移。

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

之上写的{{3}}

我真的希望,这可以帮助你解决问题;)

答案 1 :(得分:1)

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

two boxes

显然,您想要从Rect A的右边缘到左边缘绘制线条/箭头  Rect B,有点像这样:

two boxes with a link

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

center of edges highlighted

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
}

更有趣的问题是如何确定,在更动态的场景中,您可能想要从哪条边缘到哪条边线绘制线条。

如果您的盒子从左到右堆积,给定的解决方案将适合, 但您可能需要检查边缘的最小距离,以确定可能的最佳箭头。