Check whether two 2d direction vectors are parallel

The cross product of two parallel direction vectors has zero length. The magnitude (length) of the cross product is the area enclosed by the vectors. Two parallel direction vectors obviously do not have a surface since they can be considered the same origin.

In 2D the z axis is the normal to the x and y axis. The z-axis is calculated by using the cross vector of both vectors.


The cross vector Z-component can be calculated using:
z = A_x B_y - A_y B_x

if z is very small, the vectors are parallel.

2D Line intersection using vectors

In this note I will explain how to find the intersection point P  between two line segments. Note that this method will also calculate intersections on extended line segments.

As a reminder the cross product is the area of the parallelogram enclosed by the two As a reminder, the cross product is the area of the parallelogram enclosed by the two vectors. In 2D graphics, we will calculate only the z-component of the cross-vector, which will be called the cross-product in this note.

It can be calculated using the following formula:

a\times b = a_x  b_y - a_y b_x

Calculating the actual intersection:

As an example we will calculate the intersection point P of line segments AB and CD as shown in image 1.

Image 1

We will consider the line segments as vectors, this gives us the following vectors.

AB =\begin{pmatrix}6\\3\end{pmatrix}, CD =\begin{pmatrix}-4\\4\end{pmatrix}

To calculate the intersection point we will first calculate the area of the parallelogram formed by AB and CD as shown in image 2.

Image 2

The area can be calculated using the cross product of AB \times CD

AB\times CD = \begin{pmatrix}6\\3\end{pmatrix}\times\begin{pmatrix}-4\\4\end{pmatrix} = (6.4)-(-4.3) = 24-(-12) = 36

Calculating the offset on segment CD

We will now calculate the area below vector AB as seen in image 3

Image 3

It can be seen the offset on segment on CD will be equal to this area divided by the total area calculated earlier. Fortunately we can easily calculate the area by using the area shown in image 4.

Image 4

The areas shown in image 3 and image 4 are the same. Note that image 4 shows the parallelogram formed by AC and AB, we will use the cross-product to calculate it.

We will introduce vector AC for this.

AC =\begin{pmatrix}7-2\\3-2\end{pmatrix} = \begin{pmatrix}5\\1\end{pmatrix}

AC\times AB = \begin{pmatrix}5\\1\end{pmatrix}\times\begin{pmatrix}6\\3\end{pmatrix} = (5.3)-(1.6) = 15-6 = 9

Now we are almost done. We have both areas (9 and 36) so we can create the offset

offset = \frac{9}{36}CD which can be simplified to offset = \frac{1}{4}

If we multiply the offset with CD we find the point on the vector CD.

Since the line segment CD does not start on \begin{pmatrix}0\\0\end{pmatrix} the actual point needs to be moved by C

P = C +  \frac{1}{4}CD

Let’s check whether it is correct

P = C + \frac{1}{4}CD = \begin{pmatrix}7\\3\end{pmatrix} + \begin{pmatrix} \frac{1}{4}(-4)\\ \frac{1}{4}4\end{pmatrix} = \begin{pmatrix}6\\4\end{pmatrix}

As a general formula:

\boxed{P = C + \frac{AC\times AB}{AB \times CD}CD}

The offset on CD can be negative or larger then one. In that case the intersection is on the extension of line segment CD. This is an advantage of this method.

Calculating the offset on segment AB

For the offset of P on AB we repeat the steps above.

We know calculate the area as shown at image 5

Image 5

Which equals the area shown in image 6.

Image 6

AC\times CD = \begin{pmatrix}5\\1\end{pmatrix}\times\begin{pmatrix}-4\\4\end{pmatrix} = (5.4)-(1.-4) = 20-(-4) =24

P = A + \frac{24}{36}AB \to  P = A + \frac{2}{3}AB

Let’s check whether it is correct

P = A + \frac{2}{3}AB = \begin{pmatrix}2\\2\end{pmatrix} + \begin{pmatrix} \frac{2}{3}(6)\\ \frac{2}{3}3\end{pmatrix} = \begin{pmatrix}6\\4\end{pmatrix}

As a general formula:

\boxed{P = A + \frac{AC\times CD}{AB \times CD}AB}

Find out where a vector intersects a horizontal (or vertical) line

Of course the line intersection method can be used but in case of horizontal or vertical lines a quicker solution is available.

Assume  the vector V1 defined by points P1 and P2. and the horizontal line is  through point P

The offset of the intersection is:

offset = \dfrac{P_y - P1_y}{P2_y - P1_y}

Lets say P1 is 10,10 P2 20,20 and P.y 0,15

In this case  (15-10)/(20-10)  is 0.5

If P1 and P2 is revered it will be (15-20)/(10-20) = -5/-10 = 0.5.

Note:
There is a  special case if the vector is parallel or on the horizontal line, in that case, there is no intersection. In that case, p2.y – p1y is zero. This situation must be checked since it will also prevent a division by zero. You probably also want to limit very small values of this value since it may result in very large (positive or negatively) offset values.

Vertical lines:
In case of a vertical line replace all .y above by x.

Calculating the intersection point:
The offset when the vector is hit is always >= 0.0 and <= 1.0,  otherwise, it is on the extended vector.

Calculating the intersection point on the(extended)  vector is trivial

x = p1_x +(p2_x-p1_x) * offset
y = p1_y +(p2_y-p1_y) * offset

Get a offset (or point) on a vector perpendicular to a point

 

To find the offset on a vector perpendicular to a point use the dot product

Assume the vector is defined by P1 and P2  and the point by P

in 2d

            (P.x - P1.x) * (P2.x - P1.x) + (P.y - P1.y) * (P2.y - P1.y)
offset =    -----------------------------------------------------------------
            (P2.x - P1.x) * (P2.x - P1.x) + (P2.y - P1.y) * (P2.y - P1.y)

or 3d

            (P.x - P1.x) * (P2.x - P1.x) + (P.y - P1.y) * (P2.y - P1.y) + (P.z - P1.z) * (P2.z- P1.z)
offset =    ---------------------------------------------------------------------------------------------------
            (P2.x - P1.x) * (P2.x - P1.x) + (P2.y - P1.y) * (P2.y - P1.y) + (P2.z - P1.z) * (P2.z - P1.z)

notes:

If vector P1,P2 has been normalized (length 1) the division by the length is obviously not necessary. If not the result is divided the square length. Although divided by the length (square root) the result will equal that of a normalized vector. Keep in mind that the dot project is a projection on the vector thus requiring a additional division by the length!

 

Calculating the point on the vector can be done using

P.x =  P1.x + (P2.x - P1.x) * o;

P.y =  P1.y + (P2.y - P1.y) * o;

P.z =  P1.z + (P2.z - P1.z) * o;  (3d)

Using a BackgroundWorker (thread)

The BackgroundWorker class can be used to start a background worker thread. Worker threads are typically used to perform lengthy task which would otherwise block the GUI or to perform simultaneous processing. Nowadays most computers have dual or quad core CPU’s Single thread application will not benefit from the additional cores.

Backround worker is a wrapper class for thread management. It can be dragged on the form in the designer or created in code. It is important to remember one BackgroundWorker instance can only serve one thread simultaneously!

Some code

void init()
{
  //Create a new instance, can also be done in the designer
   bw = new BackgroundWorker();

   //This event will be the main body of the thread
   bw.DoWork +=new DoWorkEventHandler(bw_DoWork);

   //Called when the thread ends gracefuly
   bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
   //Set to true to allow the thread to be canceled
   bw.CancellationPending = true;
   //Must be set to allow the DoWork event to call the ReportProgress method.
   bw.WorkerReportsProgress = true;

   //Called when the DoWork event calls ReportProgress
   bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);

   //Run the thread (DoWork event)
   bw.RunWorkerAsync();
}

The code above just setups the event handlers. There are other event handler for e.g. cancellation support.
The BackGroundWorker object is shared between the two threads.

The following function is called in the context of the thread which created the BackgroundWorker, typically the GUI thread.

void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
   MessageBox.Show("Progress " +  e.ProgressPercentage);
}

You can use the e.UserState which by default is the argument optionally passed to the RunWorkerAsync method. If the bw.ReportProgress is called with an extra argument this will be  the e.UserState object. It is strongly recommended to use a value type or to clone a reference object since it may be accessed simultaneously otherwise. The worker thread will NOT wait for this event to complete! (which is a good thing!)

The following event is called in the context of the creating thread also. It is called when the thread completes. But only when the threads exits itself not when it’s terminated forcefully.

void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
   MessageBox.Show("Complete");
}

This code runs in the worker thread, it is the actual threads body.

void bw_DoWork(object sender, DoWorkEventArgs e)
{
   BackgroundWorker bw = sender as BackgroundWorker;

   int t;
   for (t = 0; t < 100; t += 10)
   {
      if (e.CancellationPending)
      {
         e.Cancel = true;
         return;
      }

      bw.ReportProgress(t);
      System.Threading.Thread.Sleep(1000);
   }
}

The CancellationPending value is set by the CancelAsync method called from the creating thread.

.NET and painting from an API viewpoint

When .NET is used on the windows platform it’s a layer on top  of the windows API. Understanding how this works will help you to speed up painting.

The windows API  works with messages. These message are processed by the message queue.  Often the message handler is one function with a long switch statement processing the messages required.

Messages related to painting

Messages of importance are the WM_ERASEBKGND and the WM_PAINT.

WM_ERASEBKGND   clears the background and paint it. It is called when a part of the control needs to be repainted. The default handling uses the brush assigned to the background and paints. When an API InvalidateRect is used to invalidate a rectangle the last parameter indicates whether this message is generated.

WM_PAINT paints the contents, it uses a clipping rectangle. The GDI API can clip all contents on this rectangle. Although clipping is not totally free it reduces the time to repaint significantly.

How does this translate to .NET.

.NET supports two events which map directly to the events.

OnPaintBackground

Called from the WM_ERASEBKGND event unless double buffering or SetStyle is used to set the style AllPaintingInWmPaint. In that case this event will be fired from the WM_PAINT message, this may reducing some flicker since there can be some time between the two messages. It is to be assumed that setting the form property DoubleBuffered to true will also set this style implicit .

Paint

Used to paint the contents. It uses a clipping rectangle to prevent unneeded painting. It is important to remember that if you invalidate the form yourself using the Invalidate() method (equivalent to an InvalidateRect API call) using the parameter less version will invalidate the entire client area!

When using invalidate always supply an rectangle or region if needed. Multiple invalidates will be combined in one clipping rectangle. Typically the bounding rectangle.

It is also important to remember that invalidate() will not repaint directly, use the update method to redraw (the equivalent of the UpdateWindow() API call.

Double buffering

When double buffering is used the graphics drawn is first drawn on a bitmap which is then drawn on screen. Although theoretically this would be slower requiring an extra step, drawing on the bitmap (in memory) is often faster then to video memory the net result may be faster.  Since there will be less flicker since the screen buffer is accessed less often and bitmap drawing is a very efficient graphics operation double buffering is almost always a good idea .

It is still very important to use the Invalidate with rectangles even when double buffering.  Admittedly, drawing the entire bitmap is fast but when dragging a lot of bitmaps need to be painted!  Also if possible do not rely on clipping to much. Your paint handler has a parameter with the clipping rectangle. If a lot of objects are to be painted a quick check on this boundary can save precious time creating brushes, pens etc.