Как вычесть прямоугольник из другого? - PullRequest
4 голосов
/ 22 сентября 2010

Я пытаюсь определить рабочую область рабочего стола, даже когда панель задач скрыта.

У меня есть два прямоугольника, границы экрана и границы панели задач. Мне нужно вычесть границы панели задач Rectangle из экрана 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 ]

8 голосов
/ 05 марта 2013

Предполагая, что прямоугольник 2 содержится в прямоугольнике 1 (если нет, используйте пересечение обоих прямоугольников в качестве прямоугольника 2):

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

Если вы вычесть прямоугольник 2 из прямоугольника 1, вы получите областьс отверстием:

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

Эта область может быть разбита на 4 прямоугольника:

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

Если прямоугольник 1 и прямоугольник 2 совпадают с трех сторон, вы получите 1 прямоугольник из вычтенногоплощадь (в вашем случае).В общем случае вы получите не более 4 прямоугольников.

Реализация в target-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;
}
2 голосов
/ 24 мая 2011

Вы можете использовать System.Drawing.Region. У него есть метод Exclude, который принимает System.Drawing.Rectanlge в качестве параметра и имеет некоторые другие отличные функции.

Получить ваш результат будет немного сложнее ... Я думаю, что вы можете проверить только видимость с регионами, или вы могли бы получить ее границы - но вам нужно было бы определить свою собственную Графику для этого (и я не знаю, какие параметры использовать здесь).

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

1 голос
/ 03 января 2018

Вдохновленный предыдущими ответами, я придумал:

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

Хорошего дня.

1 голос
/ 08 апреля 2017

Это простое расширение для вычитания прямоугольников друг от друга.

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();
}
1 голос
/ 22 сентября 2010

Эта матрица показывает, что происходит в каждом случае:

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;

Кажется, это правильно для случая, который вы дали. Я не перепроверил другие случаи, поэтому я мог ошибиться.

EDIT:

немного подумав, кажется, я нашел оптимальный выбор:

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; 
1 голос
/ 22 сентября 2010

Я не уверен, что есть лучший метод, чем тот, который вы упомянули. Проблема в том, что в общем случае вычитание прямоугольной области из другой оставляет дыру где-то посередине, поэтому результат на самом деле не является прямоугольником. В вашем случае вы знаете, что панель задач точно вписывается в одну из сторон прямоугольника экрана, поэтому «лучший» способ - это выяснить, с какой стороны он находится, и вычесть ширину / высоту с этой стороны.

1 голос
/ 22 сентября 2010

Если только 3 стороны прямоугольников не совпадают, вычитание одного прямоугольника из другого приведет к форме, которая не является прямоугольником, поэтому общее решение для "вычитания прямоугольников" на самом деле не имеет особого смысла.

Решение для трех сторон совпадения:

Данные прямоугольники (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)

Edited.

0 голосов
/ 07 сентября 2018
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;
}
0 голосов
/ 06 августа 2017

Вычитание 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));
    }

Обратите внимание, что Rect является struct, поэтому оно копируется по значению. Допуск может быть любым достаточно низким (например, 10 ^ -6).

Результаты были проверены несколькими модульными тестами.

С точки зрения производительности, безусловно, есть место для улучшения.

0 голосов
/ 27 марта 2014

А вот и функция в 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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...