2

I found out how to draw Rectangles and some code to find when two rectangles overlap but I can't connect these procedures.

I have the two rectangles that I wanted but then a cannot determine whether these intersect, to then add this information to a ListBox.

Here is my code:

public partial class Form1 : Form
{
    Graphics g;
    Pen p;
    Point cursor;

    int k = 0;
    Point[] tocke = new Point[2];
    public Form1()
    {
        InitializeComponent();
        g = this.CreateGraphics();
        p = new Pen(Color.Black, 3);
    }

    private void Form1_MouseMove(object sender, MouseEventArgs e)
    {
        cursor = this.PointToClient(Cursor.Position);
        statusMisa.Text = "X: " + cursor.X + " Y: " + cursor.Y;
    }

    private void Form1_Click(object sender, EventArgs e)
    {
        bool Preklapanje(int l1x, int l1y, int l2x, int l2y, int r1x, int r1y, int r2x, int r2y)
        {
            if (l1x >= r2x || l2x >= r1x)
            {
                return false;
            }

            if (l1y <= r2y || l2y <= r1y)
            {
                return false;
            }
            return true;
        }

        List<int> pozX = new List<int>();
        List<int> pozY = new List<int>();
        if (checkCrtanje.Checked == true)
        {
            Rectangle rect = new Rectangle(cursor.X - 50, cursor.Y - 50, 100, 100);
            if (k < 2)
            {
                g.DrawRectangle(p, rect);
                tocke[k++] = new Point(cursor.X, cursor.Y);
                listBox1.Items.Add("X: " + cursor.X + " Y: " + cursor.Y);
                pozX.Add(cursor.X);
                pozY.Add(cursor.Y);
            }
            else
            {
                MessageBox.Show("Možeš nacrtati samo dva kvadrata!");
            }
        }

        if (k == 3)
        {
            if (Preklapanje(pozX[0] - 50, pozY[0] - 50, pozX[0] + 50, pozY[0] + 50, pozX[1] - 50, pozY[1] - 50, pozX[1] + 50, pozY[1] + 50))
            {
                listBox1.Items.Add("Preklapaju se.");
            }
            else
            {
                listBox1.Items.Add("Ne preklapaju se.");
            }
        }
    }
}
Jimi
  • 29,621
  • 8
  • 43
  • 61
  • 1
    There is a built-in function: Rectangle.Intersects(Rectangle) - Also: Using this.CreateGraphics() usually is not recommended.. - To fix your code, your best friend, the [debugger](https://msdn.microsoft.com/en-us/library/y740d9d3.aspx), should help! – TaW Jul 17 '20 at 02:25

1 Answers1

1

► As noted, you shouldn't use CreateGraphics() to draw on a Control's surface: this object becomes invalid (belongs to the past) as soon as the Control where the drawing is performed is invalidated (is repainted).

All Controls that have a drawable surface, raise a Paint event and have an OnPaint method that we can override to perform custom painting (the OnPaint method is responsible to raise the Paint event, so we need to handle just one).

The argument of both the event and the method, represents a PaintEventArgs object, which provides a fresh Graphics object that we can use to paint.

We can call the Invalidate() method to repaint a Control when needed. This method causes the generation of a new PaintEventArgs object (thus, a new Graphics object). After, the OnPaint method is called, which - in turn - raises the Paint event.

► To determine whether a Rectangle intersects another (or more than one), we can use the Rectangle.IntersetWith() method (it returns true or false) and the Rectangle.Interset() method → this is used to generate a Rectangle that represents the intersection of two other rectangles.

See also:
Rectangle.Contains([Rectangle])
Rectangle.Union([Rectangle a], [Rectangle b]).

Here, I'm using a few collections to store the shapes currently drawn and their intersections (just rectangles, but you can build more complex shapes using GraphicsPath objects):

  • A List<Rectangle> (rects) which stores the Rectangles already created.
  • A List<Rectangle> (intersections), to store the intersections which belong to the past (intersections already drawn).
  • A List<Rectangle> (currentIntersects), used to temporarily store the intersection generated when a new Rectangle shaped is being drawn, so we can use different colors (as soon as we release the Mouse Button, this collection is fixed and added to the intersections collection).
  • A Rectangle structure (currentRect) which represents the Rectangle that is currently being drawn on the surface (when the Mouse Button is released, this object is added to the rects collection).
  • A Point structure (startPosition), used to store the initial position of the Rectangle currently drawn. It's reset when the OnMouseDown method is called (when a new Rectangle shape is generated).

► To use this code, create a new Form and paste the code you find here in its Code file. No need to subscribe to any event: since we're drawing on a Form, I'm overriding its methods (OnPaint, OnMouseDown, OnMouseUp, OnMouseMove), no event is used.
You can do the same with a Custom Control or a UserControl.

To add these collection, or just the intersections collection, to e.g., a ListBox, to handle the collections visually, see here (the currentIntersects and intersections collections already contain the information):

How to call a method that uses PaintEventArgs and coordinates variables

NOTE:
► Here, in the OnPaint method override, I'm not calling base.OnPaint(), so the event is not generated. This speeds up the process a bit, but keep in mind that subscribing to the Form's Paint event is useless.

► You need to activate double-buffering: (set DoubleBuffered = true), otherwise you'll notice a lot of flickering (this is quite normal).

This is how it works:

Drawing Rectangles Intersections


public partial class FormDrawings : Form
{
    private List<Rectangle> rects = new List<Rectangle>();
    private List<Rectangle> intersections = new List<Rectangle>();
    private List<Rectangle> currentIntersects = new List<Rectangle>();

    private Rectangle currentRect = Rectangle.Empty;
    private Point startPosition = Point.Empty;
    private float penSize = 2.0f;

    protected override void OnMouseDown(MouseEventArgs e)
    {
        base.OnMouseDown(e);
        if (e.Button == MouseButtons.Left) {
            startPosition = e.Location;
            currentRect = new Rectangle(startPosition, Size.Empty);
        }
    }

    protected override void OnMouseMove(MouseEventArgs e)
    {
        base.OnMouseMove(e);
        if (e.Button == MouseButtons.Left) {
            if (e.Y < startPosition.Y) { currentRect.Location = new Point(currentRect.X, e.Y); }
            if (e.X < startPosition.X) { currentRect.Location = new Point(e.X, currentRect.Y); }

            currentRect.Size = new Size(Math.Abs(startPosition.X - e.X), Math.Abs(startPosition.Y - e.Y));

            currentIntersects.Clear();
            foreach (var rect in rects) {
                if (currentRect.IntersectsWith(rect)) {
                    currentIntersects.Add(Rectangle.Intersect(currentRect, rect));
                }
            }
            this.Invalidate();
        }
    }

    protected override void OnMouseUp(MouseEventArgs e)
    {
        base.OnMouseUp(e);
        if (currentRect.Size != Size.Empty) rects.Add(currentRect);
        if (currentIntersects.Count > 0) {
            intersections.AddRange(currentIntersects);
        }
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        using (var borderPen = new Pen(Color.LightGreen, penSize))
        using (var iBrush = new SolidBrush(Color.FromArgb(128, Color.Orange)))
        using (var crBrush = new SolidBrush(Color.FromArgb(128, Color.DeepSkyBlue))) {

            intersections.ForEach(r => e.Graphics.FillRectangle(iBrush, r));
            currentIntersects.ForEach(r => e.Graphics.FillRectangle(crBrush, r));

            e.Graphics.DrawRectangle(borderPen, currentRect);
            rects.ForEach(r => e.Graphics.DrawRectangle(borderPen, r));
        }
    }
}
Jimi
  • 29,621
  • 8
  • 43
  • 61
  • Thank you. I will try that way. The only problem is that I have to use algorithm for drawing shapes, not the methods we already have. I'm curious if there is other way of doing this overlapping algorithm so I can compare their execution speed? – Marijana Rendulić Jul 17 '20 at 07:13
  • When you see, somewhere (even the MSDN Docs) code that draws shapes on a canvas which uses `CreateGraphics()`, it's just an example, not real code: it shortens the code, you're supposed to implement it fully. `CreateGraphics()` can be used, of course, but just to draw something that is not supposed to persists (if you minimize or maximize the Form, or otherwise cause it to repaint, it's all is gone). It's also used to measure Text, since you just need it for an instant, then you dispose of it (which, btw, is mandatory). – Jimi Jul 17 '20 at 07:18
  • I'm curious how to get top left corner and bottom right corner coordinates of rectangle so I can implement them in algorithm for finding intersection of rectangles? – Marijana Rendulić Jul 17 '20 at 13:04
  • ?? It's already implemented here: `if (currentRect.IntersectsWith(rect)) { currentIntersects.Add(Rectangle.Intersect(currentRect, rect)); }`. So, `Rectangle.Intersect(currentRect, rect)` returns a Rectangle which represents the intersection of other two rectangles. – Jimi Jul 17 '20 at 13:08
  • Yes, but I need to use algorithm that you can see in my code. Not something that's already there in VS. – Marijana Rendulić Jul 17 '20 at 13:35
  • As you like: the Top/Left corner is represented by the `Rectangle.Location` property, the Bottom/Right corner by the `Rectangle.Bottom` and `Rectangle.Right` properties, so `Point rightBottom = new Point(Rectangle.Right, Rectangle.Bottom);` – Jimi Jul 17 '20 at 13:42