Friday, 26 August 2011

Fluent Interfaces example using Delphi part I

I was introduced to Fluent interfaces when I was developing with Java using Dependency Injection and somebody told me that it could be really interesting if I post an example using Delphi and chaining method, and here I am!. I have developed a simple example that uses chaining methods to populate a THouse object with TDoors and TWindows objects with the hope it will make it easy to understand the concept. There are several examples on the net, but I couldn't find any interesting enough. So this simple example will try to introduce you to "how to develop fluent interfaces using method chaining" with the aim of writing more readable code. The overview of the architecture is the following one, using UML notation:


The Fluent Interface unit example is as follows (uFI.pas) using generics and interfaces:

// ***********************************************
// @author  jordi corbilla
// @comment Fluent interface composition example
//
// ***********************************************

unit uFI;

interface

uses
    Graphics, Contnrs, Generics.Collections, Classes;

type
    TWindow = class;

    IWindow = interface
        function SetColor(color: TColor): TWindow;
        function ToString(): string;
    end;

    TWindow = class(TInterfacedObject, IWindow)
    private
        FColor: TColor;
    public
        function SetColor(color: TColor): TWindow;
        function ToString(): string; override;
        class function New: TWindow;
    end;

    TDoor = class;

    IDoor = interface
        function SetColor(color: TColor): TDoor;
        function ToString(): string;
    end;

    TDoor = class(TInterfacedObject, IDoor)
    private
        FColor: TColor;
    public
        function SetColor(color: TColor): TDoor;
        function ToString(): string; override;
        class function New: TDoor;

    end;

    IHouse = interface
        function AddWindow(window: TWindow): IHouse;
        function AddDoor(door: TDoor): IHouse;
        function ToString(): String;
    end;

    THouseItemsList<T> = class(TObjectList)
        function ToString(): string; override;
    end;

    TDoorList = class(THouseItemsList<IDoor>)
        function ToString(): string; override;
    public

    end;

    TWindowList = class(THouseItemsList<IWindow>)
        function ToString(): string; override;
    end;

    THouse = class(TInterfacedObject, IHouse)
    private
        FDoorList: TDoorList;
        FWindowList: TWindowList;
    protected
        function AddWindow(window: TWindow): IHouse;
        function AddDoor(door: TDoor): IHouse;
    public
        property WindowList: TWindowList read FWindowList;
        property DoorList: TDoorList read FDoorList;
        class function New: IHouse;
        function ToString(): String; override;
        constructor Create();
        destructor Destroy(); override;
    end;

implementation

uses
    SysUtils;

{ THouse }

function THouse.AddDoor(door: TDoor): IHouse;
begin
    Self.FDoorList.Add(door);
    result := Self;
end;

function THouse.AddWindow(window: TWindow): IHouse;
begin
    Self.FWindowList.Add(window);
    result := Self;
end;

constructor THouse.Create;
begin
    FDoorList := TDoorList.Create;
    FWindowList := TWindowList.Create;
end;

destructor THouse.Destroy;
begin
    FreeAndNil(FDoorList);
    FreeAndNil(FWindowList);
    inherited;
end;

class function THouse.New: IHouse;
begin
    result := Create;
end;

function THouse.ToString: String;
var
    description: TStringList;
begin
    description := TStringList.Create;
    try
        description.Add(Self.FDoorList.ToString);
        description.Add(Self.FWindowList.ToString);
    finally
        result := description.Text;
        description.Free;
    end;
end;

{ TWindow }

class function TWindow.New: TWindow;
begin
    result := Create;
end;

function TWindow.SetColor(color: TColor): TWindow;
begin
    Self.FColor := color;
    result := Self;
end;

function TWindow.ToString: string;
begin
    result := 'Window color ' + ColorToString(FColor) + sLineBreak;
end;

{ TDoor }

class function TDoor.New: TDoor;
begin
    result := Create;
end;

function TDoor.SetColor(color: TColor): TDoor;
begin
    Self.FColor := color;
    result := Self;
end;

function TDoor.ToString: string;
begin
    result := 'Door color ' + ColorToString(FColor) + sLineBreak;
end;

{ THouseItemsList }

function THouseItemsList<T>.ToString: string;
var
    i: Integer;
    description: String;
begin
    for i := 0 to Self.count - 1 do
        description := description + ' ' + Self[i].ToString;
    result := description;
end;

{ TDoorList }

function TDoorList.ToString: string;
var
    description: TStringList;
begin
    description := TStringList.Create;
    try
        description.Add('Number of doors: ' + inttostr(Self.count));
        description.Add('Descriptions: ');
        description.Add( inherited ToString);
    finally
        result := description.Text;
        FreeAndNil(description);
    end;
end;

{ TWindowList }

function TWindowList.ToString: string;
var
    description: TStringList;
begin
    description := TStringList.Create;
    try
        description.Add('Number of Windows: ' + inttostr(Self.count));
        description.Add('Descriptions: ');
        description.Add( inherited ToString);
    finally
        result := description.Text;
        FreeAndNil(description);
    end;
end;

end.

Now it comes when we can use the object using FI notation in a more readable way:

var
    House: IHouse;
begin
    House := THouse.New.AddWindow(TWindow.New.SetColor(clFuchsia)).AddDoor(TDoor.New.SetColor(clWhite))
        .AddWindow(TWindow.New.SetColor(clRed)).AddDoor(TDoor.New.SetColor(clGreen));
    Memo1.Lines.Add(House.ToString);
    House := nil;

    House := THouse.New;
    House.AddWindow(TWindow.New.SetColor(clRed)).AddWindow(TWindow.New.SetColor(clBlue)).AddWindow(TWindow.New.SetColor(clGreen));
    House.AddDoor(TDoor.New.SetColor(clRed)).AddDoor(TDoor.New.SetColor(clBlue)).AddDoor(TDoor.New.SetColor(clGreen));
    Memo1.Lines.Add(House.ToString);
    House := nil;

Notice that now, we can chain our objects, composing the structure of the House by using the AddWindow and AddDoor method. To display the result I'm using the ToString method that will do the job for my purpose.

And the result of the previous execution:


Number of doors: 2
Descriptions: 
 Door color clWhite
 Door color clGreen


Number of Windows: 2
Descriptions: 
 Window color clFuchsia
 Window color clRed


Number of doors: 3
Descriptions: 
 Door color clRed
 Door color clBlue
 Door color clGreen


Number of Windows: 3
Descriptions: 
 Window color clRed
 Window color clBlue
 Window color clGreen


Just play with it and you'll experience the powerfulness of this method. I hope you like it.

I'm looking forward to your comments.
Jordi

Monday, 22 August 2011

Building my own Delphi Physics Engine part IX

I'm quite familiar with GDI, the graphics provided under Microsoft and which is widely used in Windows applications. TDPE is now ready to use Direct2D as now it's fully supported by Delphi 2010 and XE. According to MSDN, Direct2D is: a hardware-accelerated, immediate-mode, 2-D graphics API that provides high performance and high quality rendering for 2-D geometry, bitmaps, and text. The Direct2D API is designed to interoperate well with GDI, GDI+, and Direct3D.
As I'm testing this under Windows Vista, I've had to upgrade my windows version to Service Pack 2 and then I've installed the following update (KB971512 - The Windows Graphics, Imaging, and XPS Library contain the latest advancements in modern graphics technologies for gaming, multimedia, imaging and printing applications.). Direct2D is already installed in Windows 7.

We can use the same methods from TCanvas for our TDirect2DCanvas as they derive from the same ancestor TCustomCanvas. But you need to check the documentation for differences in the way they work.

A simple way to use Direct2D is as follows:

uses
    Direct2D, D2D1;

procedure Paint();
begin
    if TDirect2DCanvas.Supported then
    begin
        d2dCanvas := TDirect2DCanvas.Create(Canvas, ClientRect);
        try
            d2dCanvas.RenderTarget.beginDraw;
            d2dCanvas.RenderTarget.Clear(D2D1ColorF(clBlack));
            d2dCanvas.RenderTarget.SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
            // drawing goes here
            Paint(d2dCanvas);
            d2dCanvas.RenderTarget.EndDraw;
        finally
            FreeAndNil(d2dCanvas);
        end;
    end;
end;

Here is the result of the Physics Engine using a Direct2D Render and a GDI Render:





You will see the difference when switching from GDI to Direct2D as the performance is quite different. When it comes to Direct2D, my computer is getting quite slower as my graphic card is not powerful at all and we can see that it takes a while to render the image.


GDI uses Pixel graphics but Direct2D can supports vector graphics as well, in which mathematical formulas are used to draw the lines and curves. Vector graphics provides high quality rendering independent of resolution of the device, while the pixelated graphics has dependency with resolution which may results in choppy graphics.

Most of the GDI APIs are not using anti-aliasing and transparency. Ofcrouse there are functions to do so but always there’s programming cost for taking advantage of these features. Also if we apply transparency and anti-aliasing, the computations are done using CPU. Direct2D can take advantage of graphics hardware and delegate the computationally intensive tasks to GPU


Get the latest version here: ThundaxBallDemo v1.584.
Interaction:
'd' will drop a box with a random colour and a constant angular velocity.
'f' will drop a circle with a random colour and a constant angular velocity.
'mouse interaction' mouse is able to grab items.
'q' Enable/Disable GDI or Direct2D rendering.

Note: You need to have installed Direct2D to run the app using Direct2D, otherwise GDI will be used.

Related links:

Wednesday, 10 August 2011

Building my own Delphi Physics Engine part VIII

TDPE is almost ready and it will not take long to release the first stable library!. I am eager to continue working on the project trying to sort out different physic scenarios  to make my dynamic engine even more realistic. Do not hesitate to give it a try as you will find the experience quite interesting. I am still working on the n-edge object and the cutting tool and trying to refactor all the code to cope with a layer architecture. Using this concept, I want to structure my system in a way that every component works using a certain level of abstraction. As you see in the next figure, every object is allocated in the screen while there is another layer which is responsible for the collision detection and another one for the forces interaction. You will notice in the video that there are still little issues to tackle regarding the object interaction as sometimes we can experience overlapping.


In the figure above, you can see that now the items are not rigid and they interact with each other as they were real items. Notice that the square item will go through the round shape of the circle.



Enjoy the video!.

Get the latest executable here: ThundaxBallDemo v1.504.
Interaction:
'd' will drop a box with a random color and a constant angular velocity
'f' will drop a circle with a random color and a constant angular velocity
'mouse interaction' mouse is able to grab items

Related links:

Sunday, 10 July 2011

Continuous Integration for your Delphi projects using Jenkins CI

Going on with the utilization of Msbuild, I'm putting forward this post using Jenkins CI (Continuous Integration) previously known as Hudson, for your Delphi projects under Windows Vista. In a nutshell, Jenkins provides an easy-to-use so-called continuous integration system, making it easier for developers to integrate changes to the project, and making it easier for users to obtain a fresh build. The automated, continuous build increases the productivity. (Source: What is Jenkins?). With these simple steps I'll lead you throughout the whole installation and configuration of your first Delphi built project using Jenkins CI and MsBuild.

1 . Download the latest version of Jenkins CI.
Use the following link to download the latest windows installer, in my case v1.419. The default installation path is C:\Program Files\Jenkins and there you'll find the service wrapper jenkins.exe and the Java web archive (jenkins.war) that is very useful to start the Jenkins if you have difficulties installing it with a container. It's very easy to run Jenkins by itself using the command: java -jar jenkins.war (which uses Winstone as a container).


2. Setting up environment variables.
To make sure that Jenkins runs perfectly, we need to set up the JENKINS_HOME environment variable:

3. Adding administrator privileges to Jenkins windows service.
We need to be sure that the service has enough permissions to be running. To do this, go to console and type down "services.msc". Then look for Jenkins service and add the log on information using an administrator account.
I had a lot of troubles trying to execute MSBuild from Jenkins until I realised that the service didn't have enough permissions.

4. Installing Jenkins CI plug-ins.
Once everything is done, we can go on with Jenkins and open a web explorer and type http://localhost:8080. Now we need to add the following plug-ins: Jenkins MSBuild Plugin and Hudson Setenv Plugin. To install them go to "Manage Jenkins"-> "Manage Pluggins" -> Available and look for them. Then, save the changes at the end of the page and reload Jenkins.
Once installed, you'll find them under the Installed section:

5. Configure MSBuild.
Now the plug-in is ready, and we need to supply the path of our MSBuild. Go to "Manage Jenkins" -> "Configure Jenkins" and add the Msbuild information in the MSbuild section:

6. Adding environment variables.
As we are trying to build Delphi projects we need to set up Jenkins with the same environment variables included in rsvars.bat batch file. Go to "Manage Jenkins" -> "Configure Jenkins" and add the following environment variables according to your Delphi version:

7. Setting up your project.
Start a new job with your project description and choose the "Build a free-style software project" type. And add the following MSbuild information to build your project (dproj file):

8. Build your Project.
Everything is ready to do the build. Now execute the "Build now" option and you'll see your project being compiled and built using MSBuild.

Now, you can add your Delphi projects and improve the quality of your software.

Related Links:

Monday, 4 July 2011

Delphi Msbuild

Msbuild has been included since Delphi 2007 to automate our Delphi builds and manage our projects in a better way. I have developed a custom application to provide automated Builds for Delphi (2007 / 2009 / 2010 / XE -Win32) using MSBuild. The app called Thundax Delphi Builder is available here for free. This little tool generates a batch file to call the list of projects and it displays the information using a TMemo getting the info from the console window using a pipe with the capture method from my previous post.
If you take a look at my application, you will see a output section and a configuration section. In the configuration section we need to add the list of projects to compile/build, the path of our Delphi binaries and the different MSBuild commands we want to execute.


As soon as all the parameters are correct, we can hit the "Build" button. The Build button generates a batch file called build.bat. This batch file contains the list of all our projects with the correct commands for MSBuild. Just take a look at the generated batch file and you will understand what is this doing.

First of all, it sets the environment variables for the target compiler. It means that if you enter in the "Bin" section  the path to your DelphiXE binaries, it will set MSBuild for DelphiXE. To do this, we only need to run rsvars.bat batch file which contains:

@SET BDS=C:\Program Files (x86)\Embarcadero\RAD Studio\7.0
@SET BDSCOMMONDIR=C:\Users\Public\Documents\RAD Studio\7.0
@SET FrameworkDir=C:\Windows\Microsoft.NET\Framework\v2.0.50727
@SET FrameworkVersion=v2.0.50727
@SET FrameworkSDKDir=
@SET PATH=%FrameworkDir%;%FrameworkSDKDir%;%PATH%
@SET LANGDIR=EN

Of course this tools is just a "little tool" to help you understand how Msbuild works and how to invoke it from the command line. I use continuous integration tools and some day I will talk about one of them, for example Hudson continuous integration.

PS: If you want to download the app, download my Calc Hash to make sure that the MD5 hash is correct. To check it, download the app, remove the " (0c3c3ab27a6000d8881974a411959828)." hash and check the file with the calc. If the app is correct, you should get the same hash.

Related links: