Monday, March 26, 2012

GraphicsPath.IsVisible() method wont work when using zoom matrix with a big zoom factor in GDI+

When programming for a graphical application, I noticed that the GraphicsPath.IsVisible() method, sometimes,  is not working fine for small path figures when using scale matrix for the Graphics of the object or Control.

I googled for the problem and found a solution in Java. Then I translated the code to C# as an extension method to the GraphicsPath. It works fine now. The translated code is as follows:

/// <summary>
/// Indicates whether the graphics path contains the specified point or not.
/// </summary>
/// <param name="graphicsPath">The graphics path to check if it contains the point.</param>
/// <param name="location">The specified location to check if it is inside the path or not.</param>
/// <returns>True if the point is within the graphics path, otherwise false.</returns>
public static bool Contains(this GraphicsPath graphicsPath, PointF location)
{
    GraphicsPath actualPath = (GraphicsPath)graphicsPath.Clone();
    bool oddTransitions = false;

    actualPath.Flatten(null, .25f);

    for (int i = 0, j = actualPath.PointCount - 1; i < actualPath.PointCount; j = i++)
        if ((actualPath.PathPoints[i].Y < location.Y && actualPath.PathPoints[j].Y >= location.Y) ||
            (actualPath.PathPoints[j].Y < location.Y && actualPath.PathPoints[i].Y >= location.Y))
            if (actualPath.PathPoints[i].X + 
                ((location.Y - actualPath.PathPoints[i].Y) /
                (actualPath.PathPoints[j].Y - actualPath.PathPoints[i].Y) *
                (actualPath.PathPoints[j].X - actualPath.PathPoints[i].X)) < location.X)
                oddTransitions = !oddTransitions;

    return oddTransitions;
}

I cloned and flattened the original path to be sure the result is correct even if it contains Beziers. Hope it can help you.

Enjoy programming.


C# Methods for +18 !!!

Today, while programming, I was very busy with a problem. When I trying to use StringBuilder.Append method, IntelliSence showed me necessary information about the method: 










Suddenly, I focused to the (+ 18 overloads)
Wow, can I use this method? Am I over 18? Yes, I can! 
These are questions and answer I asked myself when I saw that. 


No worry, everybody can use the method at anytime ;-) 


Enjoy programming!



Sunday, March 11, 2012

Minimum scale factor for the shapes in vector graphical applications

There are two scenarios when scaling shapes in graphical applications:
  1. The application always keeps the original values of the shape including all of the changes applied on it. In this situation, there is no matter how much the minimum scale factor should be. At any time, the original values can be used to change the shape.
  2. The application uses the latest shape values for each change. Obviously, when you scale the shape and set the scale factor to 0 (horizontally and/or vertically), the shape width and/or height will be changed to 0 too. So, if you try to apply another changes on the shape (e.g. try to rescale it to a bigger shape), the width and/or height of the will not be changed as they are now 0 and multiplication of them to any scale factor will be 0 too.
    In this scenario, you should consider a minimum scale factor to prevent disappearing the shape. 

Saturday, March 10, 2012

How to zoom in GDI+ painting?

When you are creating a GDI+ painting control, you may need to add some facilities to zoom in the painting area and scroll inside it.
It's simple. I show you how to do it with a few lines of code:
I supposed that the amount of zooming is set to the zoomFactor variable. First of all, you need to transform the Graphics object of the OnPaint event handler, and the amount of the scrolls are set as the value of your scrollable control horizonal and vertical scroll bar values.
So add the below code to the overriden OnPaint event handler:

/// <summary>
/// Occures when the entire control needs repainting.
/// </summary>
/// <param name="e">A PaintEventArgs that contains the event data.</param>
protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);
    e.Graphics.Transform = this.SetGraphicsMatrix();
    e.Graphics.TranslateTransform(
        (float)Math.Round(
            this.Unzoom(AutoScrollPosition.X) - AutoScrollPosition.X, 
            MidpointRounding.AwayFromZero),
        (float)Math.Round(
            this.Unzoom(AutoScrollPosition.Y) - AutoScrollPosition.Y, 
            MidpointRounding.AwayFromZero));
    // Your paintings here
}
 
SetGraphicsMatrix() creates a matrix to scale the painting area, and synchronizes thetop-left corner of the painting area with the horizontal and vertical scroll bars:
 
/// <summary>
/// Creates a matrix to scale the painting area and synchronize
/// the top-left corner of the control with the scroll bars.
/// </summary>
/// <returns>The translated matrix.</returns>
private Matrix SetGraphicsMatrix()
{
    Matrix matrix = new System.Drawing.Drawing2D.Matrix();
    matrix.Scale(this.zoomFactor, this.zoomFactor);
            matrix.Translate(-this.HorizontalScroll.Value, 
                             -this.VerticalScroll.Value);
    return matrix;
}
 
Using the above codes, your painting will be drawn and zoomed correctly on your scrollable control. 





What if you need to obtain the location of a point on your scrolled and zoomed control?
The Unzoom method calculates a value describing its original value when the painting area is unzoomed (zoomFactor = 1). Below you can find two overloads of it:
 
/// <summary>
/// Calculates the original value of a float value when zooming.
/// </summary>
/// <param name="value">The value which its original value 
/// should be calculated.</param>
/// <returns>The original value of the float value when zooming.</returns>
internal float Unzoom(float value)
{
    return value / this.zoomFactor;
}


/// <summary>
/// Calculates the original values of a location point when zooming.
/// </summary>
/// <param name="location">The location which its original values 
/// should be calculated.</param>
/// <returns>The original values of the location when zooming.</returns>
internal PointF Unzoom(PointF location)
{
    return new PointF(location.X / this.zoomFactor, 
                      location.Y / this.zoomFactor);
}
 
And finaly, you may need to know the location of a point on your control considering the scroll values. To do this, Use ScrolledLocation method:
 
/// <summary>
/// Calculates the correct values of a location
/// when Canvas2D control is scrolled.
/// </summary>
/// <param name="location">The specified location.</param>
/// <returns>The calculated location after scrolling.</returns>
internal PointF ScrolledLocation(PointF location)
{
    return new PointF(location.X - AutoScrollPosition.X, 
        location.Y - AutoScrollPosition.Y);
}
 
Although ScrolledLocation method calculates the location of a point when your control is scrolled, it does not work correctly when the zoomFactor is not equal to 1. To resolve this problem, you need to combine it with Unzoom method.
For example: To get the correct location of mouse when you click on the control, use the below piece of code:

PointF PaintLocation = this.Unzoom(
     this.ScrolledLocation(this.PointToClient(MousePosition)));
 
That's all. Enjoy your programming.


Saturday, March 3, 2012

Training Videos: A nice idea to learn

Some days ago, I received a message from my friend that PluralSight opens its training videos to the public freely for just one day on 29 Feb 2012 as the additional day of the leap year. 
I watched two of them. Nice, really nice. In my opinion, learning via training videos can save lots of time. Of course, the quality of the video and the speaker's diction is very important.


Thanks to PluralSight people who gave me such a nice experience. :)



Friday, March 2, 2012

JCL Library: BIOS and Processor - Wrong Results

Today, I tried to produce a unique ID for each computer. I used JCLSysInfo Delphi programming library to achieve this. I tested the below commands on a Windows 7 x86 Ultimate:
GetProcessorArchitecture();
and
GetNativeSystemInfo(sysInfo);
sysInfo.dwProcessorType
But when I restarted my computer and boot it via a Windows 7 x64 Ultimate, the results were quite different. So don’t use these two commands if you need to get hardware information. The results are depend on the operating system.
I also tested the below BIOS commands:
GetBIOSCopyright());
GetBIOSName());
GetBIOSExtendedInfo());
The only result I get from them were an empty string.
FYI: My computer CPU type is: Intel(R) Core(TM)2 Duo CPU T9400 @ 2.53GHz