代码之家  ›  专栏  ›  技术社区  ›  Chris Thompson

如何从一个矩形中减去另一个矩形?

  •  4
  • Chris Thompson  · 技术社区  · 14 年前

    我有两个矩形,屏幕的边界和任务栏的边界。我需要从屏幕矩形中减去任务栏的边界矩形来确定桌面的可用工作区。基本上,我想屏幕工作区除非任务栏被隐藏。

    说屏幕矩形 X,Y,W,H = 0,0,1680,1050 X,Y,W,H is 0,1010,1680,40 . 我需要从第一个减去第二个,以确定工作区域是 0,0,1680,1010 .

    任务栏可以在屏幕的四个面中的任何一个,我知道一定有一个更好的方法,比确定任务栏的位置,然后有一个单独的代码行为四个可能的位置生成一个新的矩形。

    11 回复  |  直到 14 年前
        1
  •  1
  •   casablanca    14 年前

    我不确定有没有比你提到的更好的方法。问题是,在一般情况下,从一个矩形区域减去另一个矩形区域会在两个区域之间留下一个洞,因此结果实际上不是一个矩形。在您的例子中,您知道任务栏正好位于屏幕矩形的一侧,因此“最佳”方法实际上是确定任务栏是哪一侧,并从该一侧减去宽度/高度。

        2
  •  9
  •   Hai Feng Kao    10 年前

    假设矩形2包含在矩形1中 (如果不是,则使用两个矩形的交点作为矩形2):

    -------------------------
    |      rectangle 1      |
    |                       |
    |     -------------     |
    |     |rectangle 2|     |
    |     -------------     |
    |                       |
    |                       |
    -------------------------
    

    -------------------------
    |                       |
    |                       |
    |     -------------     |
    |     |    hole   |     |
    |     -------------     |
    |                       |
    |                       |
    -------------------------
    

    该区域可分解为4个矩形:

    -------------------------
    |          A            |
    |                       |
    |-----------------------|
    |  B  |   hole    |  C  |
    |-----------------------|
    |                       |
    |          D            |
    -------------------------
    

    如果矩形1和矩形2的三条边重合,则从减去的区域中得到一个矩形(这是您的情况)。一般来说,最多可以得到4个矩形。

    objective-c中的实现(抱歉,目前没有visual studio):

    // returns the rectangles which are part of rect1 but not part of rect2
    NSArray* rectSubtract(CGRect rect1, CGRect rect2)
    {
        if (CGRectIsEmpty(rect1)) {
            return @[];
        }
        CGRect intersectedRect = CGRectIntersection(rect1, rect2);
    
        // No intersection
        if (CGRectIsEmpty(intersectedRect)) {
            return @[[NSValue valueWithCGRect:rect1]];
        }
    
        NSMutableArray* results = [NSMutableArray new];
    
        CGRect remainder;
        CGRect subtractedArea;
        subtractedArea = rectBetween(rect1, intersectedRect, &remainder, CGRectMaxYEdge);
    
        if (!CGRectIsEmpty(subtractedArea)) {
            [results addObject:[NSValue valueWithCGRect:subtractedArea]];
        }
    
        subtractedArea = rectBetween(remainder, intersectedRect, &remainder, CGRectMinYEdge);
        if (!CGRectIsEmpty(subtractedArea)) {
            [results addObject:[NSValue valueWithCGRect:subtractedArea]];
        }
    
        subtractedArea = rectBetween(remainder, intersectedRect, &remainder, CGRectMaxXEdge);
        if (!CGRectIsEmpty(subtractedArea)) {
            [results addObject:[NSValue valueWithCGRect:subtractedArea]];
        }
    
        subtractedArea = rectBetween(remainder, intersectedRect, &remainder, CGRectMinXEdge);
        if (!CGRectIsEmpty(subtractedArea)) {
            [results addObject:[NSValue valueWithCGRect:subtractedArea]];
        }
    
        return results;
    }
    
    // returns the area between rect1 and rect2 along the edge
    CGRect rectBetween(CGRect rect1, CGRect rect2, CGRect* remainder, CGRectEdge edge)
    {
        CGRect intersectedRect = CGRectIntersection(rect1, rect2);
        if (CGRectIsEmpty(intersectedRect)) {
            return CGRectNull;
        }
    
        CGRect rect3;
        float chopAmount = 0;
        switch (edge) {
            case CGRectMaxYEdge:
                chopAmount = rect1.size.height - (intersectedRect.origin.y - rect1.origin.y);
                if (chopAmount > rect1.size.height) { chopAmount = rect1.size.height; }
                break;
            case CGRectMinYEdge:
                chopAmount = rect1.size.height - (CGRectGetMaxY(rect1) - CGRectGetMaxY(intersectedRect));
                if (chopAmount > rect1.size.height) { chopAmount = rect1.size.height; }
                break;
            case CGRectMaxXEdge:
                chopAmount = rect1.size.width - (intersectedRect.origin.x - rect1.origin.x);
                if (chopAmount > rect1.size.width) { chopAmount = rect1.size.width; }
                break;
            case CGRectMinXEdge:
                chopAmount = rect1.size.width - (CGRectGetMaxX(rect1) - CGRectGetMaxX(intersectedRect));
                if (chopAmount > rect1.size.width) { chopAmount = rect1.size.width; }
                break;
            default:
                break;
        }
    
        CGRectDivide(rect1, remainder, &rect3, chopAmount, edge);
    
        return rect3;
    }
    
        3
  •  2
  •   Andreas Reiff    13 年前

    得到你的结果会稍微困难些。。我认为你只能测试区域的可见性,或者你可以得到它的边界-但是你必须为此定义你自己的图形(我不知道在这里使用什么参数)。

    http://msdn.microsoft.com/en-us/library/system.drawing.region_methods.aspx

        4
  •  1
  •   Forrest Voight    14 年前

    除非矩形的三条边重合,否则从一个矩形中减去另一个矩形将得到一个不是矩形的形状,因此“减去矩形”的一般解决方案实际上意义不大。

    三面重合的解决方案:

    给定矩形(Ax,Ay,Aw,Ah)和(Bx,By,Bw,Bh):

    已编辑。

        5
  •  1
  •   Mighty Badaboom Karthick Raju    7 年前

    public static Rectangle[] Subtract(this Rectangle source, Rectangle[] subtractions)
    {
        Region tmp = new Region(source);
    
        foreach (var sub in subtractions)
        {
            tmp.Xor(sub);
        }
        Region src = new Region(source);
        src.Intersect(tmp);
        return src.GetRegionScans(new Matrix()).Select(Rectangle.Ceiling).ToArray();
    }
    
        6
  •  1
  •   Guglie    7 年前

    灵感来自我之前的答案:

    public Rectangle[] Subtract(Rectangle[] subtractions)
    {// space to chop = this panel
        Region src = new Region(Bounds); 
        foreach (var sub in subtractions)
        {
            Region tmp = src.Clone();
            tmp.Xor(sub);
            src.Intersect(tmp);                
        }
        return src.GetRegionScans(new Matrix()).Select(Rectangle.Ceiling).ToArray();
    }
    

    祝你有美好的一天。

        7
  •  0
  •   Michael    14 年前

    我想我不明白你想从中得到什么。NET以监视器的分辨率减去任务栏占用的空间来报告桌面的当前工作区域。

    • 如果任务栏设置为隐藏,则桌面的工作区域就是显示器的整个分辨率。
    • 如果任务栏停靠在顶部或左侧,则桌面的工作区域是监视器的整个分辨率减去任务栏的大小,然后根据需要在X/Y方向上移动。

    确定屏幕的工作区域已在.NET中公开(包括任务栏停靠在顶部或左侧时移动的X/Y坐标)。

         // set the coordinate to the upper-left of the screen
         Rectangle r = Screen.GetWorkingArea(new Point(0, 0));
    
         // the resulting rectangle will show the deviation in X/Y
         // and also the dimensions of the desktop (minus the Taskbar)
         MessageBox.Show
         (
            r.ToString()
         );
    
        8
  •  0
  •   Lie Ryan Bryan    14 年前

    该矩阵显示了每种情况下发生的情况:

    in all cases: sX = 0; sY = 0; sW = width; sH = height; 
    north: tX = 0;          tY = 0;          tW = sW;    tH = sizeH;   wX = 0;     wY = tH; wW = sW;         wH = sH - sizeH;   sizeW=0,    sizeH=size
    south: tX = 0;          tY = sH - sizeH; tW = sW;    tH = sizeH;   wX = 0;     wY = 0;  wW = sW;         wH = sH - sizeH;   sizeW=0,    sizeH=size
    east : tX = 0;          tY = 0;          tW = sizeW; tH = sH;      wX = sizeW; wY = 0;  wW = sW - sizeW; wH = sH        ;   sizeW=size, sizeH=0
    west : tX = sW - sizeW; tY = 0;          tW = sizeW; tH = sH;      wX = 0;     wY = 0;  wW = sW - sizeW; wH = sH        ;   sizeW=size, sizeH=0
    

    我们可以概括为:

    in all cases: sX = 0; sY = 0; sW = width; sH = height; 
    north: tX = 0;          tY = 0;          tW = sW;    tH = sizeH;   wX = 0;  wY = tH; wW = sW - sizeW; wH = sH - sizeH;   sizeW=0,     sizeH=value
    south: tX = 0;          tY = sH - sizeH; tW = sW;    tH = sizeH;   wX = 0;  wY = 0;  wW = sW - sizeW; wH = sH - sizeH;   sizeW=0,     sizeH=value
    east : tX = 0;          tY = 0;          tW = sizeW; tH = sH;      wX = tW; wY = 0;  wW = sW - sizeW; wH = sH - sizeH;   sizeW=value, sizeH=0
    west : tX = sW - sizeW; tY = 0;          tW = sizeW; tH = sH;      wX = 0;  wY = 0;  wW = sW - sizeW; wH = sH - sizeH;   sizeW=value, sizeH=0
    

    矩阵揭示了这个算法:

    if (east/west)   sizeW = tW;  else   sizeW = 0; 
    if (north/south) sizeH = tH;  else   sizeH = 0;
    wX = 0; wY = 0; wW = sW - sizeW; wH = sH - sizeH;
    if (east)  wX = sizeW;
    if (north) wY = sizeH;
    

    其中C/C++ +java和其他类似语言可以写成:

    sizeW = (tH == sH) ? tW : 0; 
    sizeH = (tW == sW) ? tH : 0;
    wX = (sizeH == 0 && tX == 0) ? sizeW : 0; 
    wY = (sizeW == 0 && tY == 0) ? sizeH : 0; 
    wW = sW - sizeW; 
    wH = sH - sizeH;
    

    这似乎是正确的情况下,你给。我还没有重新检查其他的案子,所以我可能搞错了。

    编辑:

    wW = sW - ((tH == sH) ? tW : 0); 
    wH = sH - ((tW == sW) ? tH : 0);
    wX = (wH == sH && tX == 0) ? tW : 0; 
    wY = (wW == sW && tY == 0) ? tH : 0; 
    
        9
  •  0
  •   zmechanic    10 年前

    这里是ActionScript中的函数。它将返回一个新的矩形,该矩形不与提供的任何矩形重叠 rectangles 列表。矩形的所有重叠部分 rect 将被减去。

    private function reduceRectangle(rectangles:Vector.<Rectangle>, rect:Rectangle):Rectangle
    {
        var rn:Rectangle = rect.clone();
    
        for each (var r:Rectangle in rectangles)
        {
            //outside
            if (r.x >= rn.right || r.y >= rn.bottom || r.right <= rn.x || r.bottom <= rn.y)
            {
                continue;
            }
    
            //within
            if (r.x <= rn.x && r.y <= rn.y && r.right >= rn.right && r.bottom >= rn.bottom)
            {
                return new Rectangle();
            }
    
            //clip right side
            if (r.x > rn.x && r.x < rn.right && r.y <= rn.y && r.bottom >= rn.bottom)
            {
                rn.width = r.x - rn.x;
            }
    
            //clip bottom side
            if (r.y > rn.y && r.y < rn.bottom && r.x <= rn.x && r.right >= rn.right)
            {
                rn.height = r.y - rn.y;
            }
    
            //clip left side
            if (r.x <= rn.x && r.right < rn.right && r.y <= rn.y && r.bottom >= rn.bottom)
            {
                var width:Number = rn.right - r.right;
                rn.x = r.right;
                rn.width = width;
            }
    
            //clip top side
            if (r.y <= rn.y && r.bottom < rn.bottom && r.x <= rn.x && r.right >= rn.right)
            {
                var height:Number = rn.bottom - r.bottom;
                rn.y = r.bottom;
                rn.height = height;
            }
        }
    
        return rn;
    }
    
        10
  •  0
  •   Community Jaime Torres    4 年前

    从一个矩形中减去一个矩形将产生一个矩形列表

    List<Rect>

        public static List<Rect> Subtract(this Rect rect, Rect subtracted)
        {
            if (rect.HasNoArea())
            {
                return _emptyList;
            }
            if (rect.Equals(subtracted))
            {
                return new List<Rect>{new Rect(0, 0, 0, 0)};
            }
            Rect intersectedRect = rect;
            intersectedRect.Intersect(subtracted);
            if (intersectedRect.HasNoArea())
            {
                return new List<Rect> { rect };
            }
            List<Rect> results = new List<Rect>();
            var topRect = GetTopRect(rect, subtracted);
            if (!topRect.HasNoArea())
            {
                results.Add(topRect);
            }
            var leftRect = GetLeftRect(rect, subtracted);
            if (!leftRect.HasNoArea())
            {
                results.Add(leftRect);
            }
            var rightRect = GetRightRect(rect, subtracted);
            if (!rightRect.HasNoArea())
            {
                results.Add(rightRect);
            }
            var bottomRect = GetBottomRect(rect, subtracted);
            if (!bottomRect.HasNoArea())
            {
                results.Add(bottomRect);
            }
            return results;
        }
    
        public static bool HasNoArea(this Rect rect)
        {
            return rect.Height < tolerance || rect.Width < tolerance;
        }
    
        private static Rect GetTopRect(Rect rect, Rect subtracted)
        {
            return new Rect(rect.Left, rect.Top, rect.Width, Math.Max(subtracted.Top, 0));
        }
    
        private static Rect GetRightRect(Rect rect, Rect subtracted)
        {
            return new Rect(subtracted.Right, subtracted.Top, Math.Max(rect.Right - subtracted.Right, 0), subtracted.Height);
        }
    
        private static Rect GetLeftRect(Rect rect, Rect subtracted)
        {
            return new Rect(rect.Left, subtracted.Top, Math.Max(subtracted.Left - rect.Left, 0), subtracted.Height);
        }
    
        private static Rect GetBottomRect(Rect rect, Rect subtracted)
        {
            return new Rect(rect.Left, subtracted.Bottom, rect.Width, Math.Max(rect.Height - subtracted.Bottom, 0));
        }
    

    请注意 Rect struct 公差可以是任何足够低的值(比如10^-6)。

    结果已经通过几个单元测试进行了验证。

        11
  •  0
  •   d7samurai    6 年前
    Rect CalcWorkArea(Rect scrn, Rect tbar)
    {
        Rect work;
    
        work.X = (tbar.X + tbar.W) % scrn.W; 
        work.Y = (tbar.Y + tbar.H) % scrn.H; 
        work.W = work.X + scrn.W - (tbar.W % scrn.W); 
        work.H = work.Y + scrn.H - (tbar.H % scrn.H);
    
        return work;
    }