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:
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:
Calculating the actual intersection:
As an example we will calculate the intersection point of line segments and as shown in image 1.
Image 1
We will consider the line segments as vectors, this gives us the following vectors.
,
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
Calculating the offset on segment
We will now calculate the area below vector as seen in image 3
Image 3
It can be seen the offset on segment on 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 and , we will use the cross-product to calculate it.
We will introduce vector for this.
Now we are almost done. We have both areas (9 and 36) so we can create the offset
which can be simplified to
If we multiply the offset with we find the point on the vector .
Since the line segment does not start on the actual point needs to be moved by
Let’s check whether it is correct
As a general formula:
The offset on can be negative or larger then one. In that case the intersection is on the extension of line segment . This is an advantage of this method.
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:
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
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
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.
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.
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.