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

时间:2010-09-21 23:39:07

标签: c# math

即使隐藏了任务栏,我也在尝试确定桌面的工作区域。

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

说出屏幕矩形X,Y,W,H = 0,0,1680,1050和任务栏X,Y,W,H is 0,1010,1680,40。我需要从第一个中减去第二个,以确定工作区域为0,0,1680,1010

任务栏可以位于屏幕四边之一,我知道必须有一个更好的方法,而不是确定任务栏的位置,然后有一个单独的代码行为四个中的每一个生成一个新的矩形可能的立场。

11 个答案:

答案 0 :(得分:8)

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

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

如果从矩形1中减去矩形2,您将得到一个带孔的区域:

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

这个区域可以分解成4个矩形:

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

如果矩形1和矩形2的三边重合,则从减去的区域得到1个矩形(这是你的情况)。通常,您最多可以获得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;
}

答案 1 :(得分:2)

您可以使用System.Drawing.Region。这有一个Exclude方法,它将System.Drawing.Rectanlge作为参数并具有一些漂亮的其他功能。

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

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

答案 2 :(得分:1)

除非矩形的三个边是重合的,否则从另一个矩形中减去一个矩形将最终形成一个不是矩形的形状,因此“减去矩形”的一般解决方案并没有多大意义。

三方的解决方案一致:

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

(max(Ax,Bx),max(Ay,By),min(Ax + Aw,Bx + Bw) - max(Ax,Bx),min(Ay + Ah,By + Bh) - max(Ay ,By)

编辑。

答案 3 :(得分:1)

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

答案 4 :(得分:1)

此矩阵显示每种情况下会发生什么:

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; 

答案 5 :(得分:1)

这是一个简单的扩展,可以相互减去矩形。

res25.map(r => {
   Location(r.getStruct(1).getDouble(0), r.getStruct(1).getDouble(1))
}).show(1)

答案 6 :(得分:1)

受到以前的答案的启发,我提出了:

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)

我想我对你试图推导出来的东西感到困惑。 .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)

这是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;
}

答案 9 :(得分:0)

从Rect中减去一个Rect,生成一个Rect

列表

正如海风高告诉的那样,减法的结果可以看作是List<Rect>

这是一个C#实现:

    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));
    }

请注意,Rectstruct,因此会按值复制。 容差可以是足够低的任何东西(如10 ^ -6)。

结果已通过多次单元测试进行测试。

在表现方面,肯定有改进的余地。

答案 10 :(得分:0)

        public List<Models.ObjectModel> GetAll()
    {
        //return _db.Object
        //    .Include(o => o.ObjectTags)
        //    .ThenInclude(ot => ot.Tag)
        //    .ToList();
        return _db.Object
            .Include(o => o.ObjectTags)
            .ThenInclude(ot => ot.Tag)
            .Select(r => new Models.ObjectModel
            {
                ObjectId = r.ObjectId,
                Name = r.Name,
                Tags = r.ObjectTags.Select(ot => ot.Tag).ToList()
            })
            .ToList();
    }