Skip to content

Bezier Surface Visualization (from Turbo Pascal to Delphi FireMonkey)

Today I have been presenting "FireMonkey 3D Programming" session on the SDE event in Zeist in the Netherlands. Bob Swart was so kind and invited me to present on the first session of the day in the "Delphi" conference track and I wanted to prepare a brand new demo to show off the power of FireMonkey.

There is more and more FireMonkey resources available online, including new "FireMonkey blog", FireMonkey Info page, FireMonkey quickstart tutorials and many more. Some of the best FireMonkey code pieces can be found at Anders Ohlsson blog. I especially like his EDN article on "Visualizing 3D mathematical functions using FireMonkey" that explains how to programmatically create mesh data for visualization using "TMesh" FireMonkey component. The source code for this demo is available at the Code Central.

Two decades ago I have been studying computer graphics at the Warsaw Institute of Technology and I still have some of my Turbo Pascal 5.5 source code with graphics applications for DOS. One of the projects for the labs on programming digitally controlled devices was to visualize a bicubic surface also known a "Bezier Surface". Using DosBox I was able to run my original, compiled DOS application inside my Windows 7 64-bit virtual machine.

The whole program is just one file with Turbo Pascal 5.5 code ("puns5.pas") and is using BGI for displaying graphics on the SVGA-compatible graphics card. It would not be possible to reuse the actual painting code, however I was able to extract three procedures, and some constants and variables from the the original Turbo Pascal code responsible for generating the 2-dimensional 11×11 matrix of 3D points that make up the wireframe. Bezier surface is a special case of NURBS surface that is very commonly used in 3D modeling. The shape of the surface is controlled by a 4×4 matrix of 3D control points as illustrated on wikipedia:

Below is the source code responsible for generating the 4×4 matrix of control points ("CreateBicubicMatrix2") and the resulting 11×11 matrix that approximates the bezier surface ("CountBicu" and "CreateNet").


const
  nx = 10;
  ny = 10;

type
  p3 = array[0..2] of single;
  Net = array[0..nx,0..ny] of p3;
  BicubicMatrix = array[0..3, 0..3] of p3;

procedure CreateBicubicMatrix2 (var G : BicubicMatrix) ;
var x,y,z : real; i,j : byte;
begin
  y:=-150;
  for i:=0 to 3 do
  begin
    x:=-150;
    for j:=0 to 3 do
    begin
      G[i,j,0]:=x;
      G[i,j,1]:=y;
      G[i,j,2]:=0;
      x:=x+100;
    end;
    y:=y+100
  end;
  G[0,0,2]:=250;
  G[3,0,2]:=50;
  G[0,3,2]:=50;
  G[3,3,2]:=-200;
  G[3,2,2]:=-200;
  G[2,3,2]:=-200;
end;

function CountBicu( wsp : integer; u,v: real; G : BicubicMatrix) : real;
var c,vb,u2,u3,t,t2,t3,v2,v3,k,k2,k3: real; j : byte;
begin
  c:=0;
  u2:=sqr(u);
  u3:=u*u2;
  t:=1-u;
  t2:=sqr(t);
  t3:=t*t2;
  v2:=sqr(v);
  v3:=v*v2;
  k:=1-v;
  k2:=sqr(k);
  k3:=k*k2;
  for j:=0 to 3 do
  begin
    case j of
      0: vb:=k3;
      1: vb:=3*k2*v;
      2: vb:=3*k*v2;
      3: vb:=v3
    end;
    c:= c + vb * (G[j,0,wsp]*t3 + G[j,1,wsp]*3*t2*u + G[j,2,wsp]*3*t*u2 + G[j,3,wsp]*u3 )
   end;
  Result:=c;
end;

procedure CreateNet(var N : Net; G : BicubicMatrix);
var u,v: real; i,e,f: integer;
begin
  for e:=0 to nx do
    for f:=0 to ny do
    begin
      u:=e/nx;
      v:=f/ny;
      for i:=0 to 2 do
        N[e,f,i]:=CountBicu(i,u,v,G);   { i - coord id: x, y or z }
    end;
end;

I have started from the original Anders Ohlsson source code for 3D mathematical function visualizations with FireMonkey and did some refactoring. There is a new button added for selecting "Bicubic Surface" and scroll bars for rotating the mesh in X, Y and Z dimensions.

Below is the actual source code for filling in "TMesh" component passed as the parameter with vertices, indices and material information. Below is the code responsible for creating the bezier surface programmatically using the original Anders’ texture.


const
  s = 0.1; // scale factor for display

function ScaleCol(c: single): single;
begin
  // scale color value "c" from "-25..25" to "0..1"
  c := c + 25;
  Result := c / 50;
end;

procedure GenerateBicuMesh(const AMesh: TMesh);
var G: BicubicMatrix; N: Net; bmp: TBitmap;
  i,j: integer; p: TPoint3D;
  sizeN: integer; vertid, ixid: integer;
  verts : array [0..3] of TPoint3D;
begin
  CreateBicubicMatrix2(G);
  CreateNet(N,G);

  bmp := TBitmap.Create(1,360);
  for i := 0 to 359 do
    bmp.Pixels[0,i] := CorrectColor(HSLtoRGB(i/360,0.75,0.5));

  AMesh.Material.Texture := bmp;

  sizeN := (nx+1) * (ny+1);
  AMesh.Data.VertexBuffer.Length := 4 * sizeN;
  AMesh.Data.IndexBuffer.Length := 6 * sizeN;

  for i := 0 to nx-1 do
    for j := 0 to ny-1 do
    begin
      vertid := (i*(ny+1) + j) * 4;
      ixid := (i*(ny+1) + j) * 6;

      verts[0] := Point3D( N[i,j,0]*s, N[i,j,1]*s, N[i,j,2]*s);
      verts[1] := Point3D( N[i+1,j,0]*s, N[i+1,j,1]*s, N[i+1,j,2]*s);
      verts[2] := Point3D( N[i,j+1,0]*s, N[i,j+1,1]*s, N[i,j+1,2]*s);
      verts[3] := Point3D( N[i+1,j+1,0]*s, N[i+1,j+1,1]*s, N[i+1,j+1,2]*s);

      with AMesh.Data do begin
        with VertexBuffer do begin
          Vertices[vertid]   := verts[0];
          Vertices[vertid+1] := verts[1];;
          Vertices[vertid+2] := verts[2];;
          Vertices[vertid+3] := verts[3];;
        end;

        with VertexBuffer do begin
          TexCoord0[vertid]   := PointF(0, ScaleCol(verts[0].Z));
          TexCoord0[vertid+1] := PointF(0, ScaleCol(verts[1].Z));
          TexCoord0[vertid+2] := PointF(0, ScaleCol(verts[2].Z));
          TexCoord0[vertid+3] := PointF(0, ScaleCol(verts[3].Z));
        end;

        IndexBuffer[ixid]   := vertid;
        IndexBuffer[ixid+1] := vertid+1;
        IndexBuffer[ixid+2] := vertid+2;
        IndexBuffer[ixid+3] := vertid+2;
        IndexBuffer[ixid+4] := vertid+1;
        IndexBuffer[ixid+5] := vertid+3;
      end;
    end;
end;

Below is the screenshot from the "Bezier Surface" visualization in Delphi FireMonkey 3D application.

The full source code of the "Bezier Surface" demo can be found at the Embarcadero Code Central.

Building awesome Mac clients for web services built with Visual Studio and C#

If your customers are asking you about a “Mac” version of your existing Windows application, you may want to look at the new Embarcadero FireMonkey application development platform. FireMonkey makes it very easy to create great looking, native, compiled applications for Mac as well as for Windows. FireMonkey is a great way for building clients application with advanced GUI features like programmable effects and animations to your existing web services implemented with Microsoft Visual Studio 2010.

RAD Studio XE2 comes with multiple compilers, including Windows 32/64-bit compilers and native OS X compiler. Basically you just need to select active target platform and recompile your project and you can build applications that natively run on Windows and OS X.

That’s truly unique capability. Being able to target Windows and OS X from the same Delphi or C++Builder code is something that no other IDE on the market can currently provide.

I have put together a tutorial and a demo that features a simple ASP.NET 4.0 C# web service and a Mac OS X client application for it built with Delphi XE2 and FireMonkey. I have also recorded the whole process of building the ASP.NET web service and Mac client for it using Camtasia. The recording is just one 25-minute long mp4 file that you can download from the Code Central.

Delphi Developer Days 2012 in Amsterdam today and tomorrow

I’m just sitting at the first day of Delphi Developer Days 2012 workshop organized by Cary Jensen and Marco Cantu in the city centre of Amsterdam. This is the second stop of the tour. Earlier this week DDD 2012 were delivered in London and next stops are Washington DC, Chicago, Frankfurt and Rome. Check out the details at http://www.delphideveloperdays.com/

I had the privilege to do a short "RAD Studio XE2" keynote presentation and also took this as the opportunity to introduce to the audience new Embarcadero Sales Director for Benelux, Alexander Goes, who used to work with me back in the Borland days in Amstelveen office. Embarcadero is clearly growing and one of the signs of it is reopening of the Embarcadero office in Amstelveen for Benelux countries.

The attendees at DDD in Amsterdam are not only from the Netherlands, but also from other countries like Belgium, Norway, Denmark and even Greece. Marco and Cary are great Delphi experts and speakers. Beyond live sessions every attendee also received 450 pages printed book with the contents of all the sessions and more.

Marco showed a very nice FireMonkey demo application running on iPad. Depending on iPad position the direction of light components is changing, so the lighting on the sphere changes. I loved it:-)

Right now Marco goes into the inner working of FireMonkey styles… That’s super interesting stuff to see how flexible FireMonkey styles are…

Delphi event in Romania for the first time ever!

The history is happening right now, right here! I’m just back from Bucharest, the capital of Romania. Yesterday it was a pure pleasure to present "RAD Studio XE2 in Action LIVE!" to the crowd of enthusiastic developers. It looks it was the first Delphi/C++Builder event ever in Romania!

The audience was very technical and positive. There was no end to questions about Delphi, C++Builder, FireMonkey, future plans for RAD Studio and many other, sometimes very low-level.

The event was divided into three logical parts:

  • Embarcadero and RAD Studio XE2 Overview
  • FireMonkey Deep Dive
  • RAD Cloud Services

After the brief introduction to Embarcadero/CodeGear/Borland history we moved to the Embarcadero products, including developer tools, database tools, InterBase and AppWave. In the RAD Studio XE2 overview I have focused on native development with Delphi/C++Builder/FireMonkey and web development with RadPHP. During the demo part I have presented the new Delphi 64-bit compiler, VCL styles, new "TZipFile" and Documentation Insight, followed by the "hello world" RadPHP application and some jQueryMobile demos.

After the break it was all about FireMonkey. Slides mixed with demos. Compiling FireMonkey application for Windows and Mac OS X from the same source code was probably the biggest hit, plus many other demos illustrating key FireMonkey concepts like parenting, animations, effects, styles, 3D basics and advanced scientific and business visualizations.

In the last part of the event my goal was to showcase the most important features of "Enterprise" version of Delphi, C++Builder and RAD Studio, including connectivity to remote databases with dbExpress and LiveBindings, new Cloud API and DataSnap architecture, including mobile connectors and callbacks.

Great thanks to Embarcadero partner in Romania - SCOP - for helping to make this event happen! Great job Gabriela! I’m looking forward to the second, third and more events in Romania!

RAD Studio in Action LIVE in Olomouc (CZ) and Breukelen (NL)

That was a really good week! RAD Studio XE2 is such an incredible release that I keep travelling and showing Delphi and FireMonkey to developers around Europe.

On Tuesday, I have been to Olomouc in Moravia in Czech Republic. There were close to 120 enthusiastic Delphi programmers gathered. Everybody liked the new FireMonkey architecture and possibilities for building great looking native apps for Windows, Mac and iOS from the same source code!

On Wednesday I had a big pleasure to demonstrate FireMonkey and RAD Cloud to around 130 developers during the RAD Studio XE2 event in Breukelen (just next to Amsterdam) with David I.

The future of Delphi, FireMonkey and native code development seems to be so bright, so the organizers prepared orange sun glasses for all attendees:-)

Next week I’m flying to Romania to present "RAD Studio XE2 in Action LIVE!" on Thursday, March 15th. I see you there:-)

Delphi and FireMonkey in Warsaw this week was great!

I’m back from Warsaw where I have been presenting Delphi XE2 and FireMonkey to Polish developers. In fact it was the third event in Warsaw about Delphi and FireMonkey since the release of RAD Studio XE2 back in September 2011!

That’s amazing to watch happy faces of Delphi developers seeing all the great "visually stunning" FireMonkey HD and 3D demos, including excellent "Gallery 3D" components presented a while ago by JT on YouTube.

Next week I’m off for one week of skiing in Austria and then I’m back on the road again with "RAD Studio XE2 in Action LIVE!" events in Czech Republic on March 6th and on the following day in Holland with David I.

FireMonkey Molecule Viewer demo source code

Today is Valentine’s Day. For Delphi programmers and supporters this is the Delphi Birthday. Delphi just turned 17. I’m wondering how the next one is going to look like;-)

I had a pleasure and the privilege to present live during the "Delphi Birthday Webinar" event just a moment ago and promised online to publish the source code of the FireMonkey Molecule Viewer demo. That’s currently work in progress, so please do not consider this as a finished viewer, but you can use as a starting point for your applications.

You can download the demo from Code Central from here: http://cc.embarcadero.com/item/28750.

Delphi DataSnap REST server and jQueryMobile web client tutorial available

I have just put together a little EDN article about using jQueryMobile JavaScript library in your Delphi XE2 "DataSnap REST Application" projects. The idea was to demonstrate how to call server-side functions implemented in native Delphi code from the JavaScript client embedded in a web page using very popular jQueryMobile framework for pages optimized for web browsers running in smartphones.

Here are the screenshots from the resulting "Hello World" jQueryMobile-enabled web application:

This article is really a logical continuation to my DelphiLabs episode where I have demonstrated how to generate jQueryMobile code on-the-fly in Delphi, but I have not included the possibility to call DataSnap server methods. Now the demo is complete and I can safely dive into the exciting new world of FireMonkey, cross-platform development and going mobile:-)

Enjoy!

FireMonkey in Lisbon today

"FireMonkey in Action LIVE" is hitting the road again! I’m in Lisbon now and it was a very exciting day. I have demonstrated RAD Studio XE2, Delphi and FireMonkey to a fully packed room of developers in Lisbon. The energy levels were very high and it was a pure pleasure to demonstrate some cool new FireMonkey 3D demos and also to spend a significant chunk of time showing DataSnap for building multitier applications with Delphi, HTML5 and jQueryMobile.

Great thanks to Sergio Transmontano from Danysoft-Portugal for being such a great host today:-)

Go Delphi! Go FireMonkey! Go InterBase!

"24 Hours of Delphi" today and my "FireMonkey End-to-End Scenarios"

It is a really long day today:-) I’m now still listening to "24 Hours of Delphi" online webinar hosted by David I and Anders Ohlsson from Developer Relations, and it is almost midnight now. Very good stuff!

I have promised during my session to upload "FireMonkey End-to-End Scenarios" demos. Both demos and slides are now available for download from this link: http://cc.embarcadero.com/item/28674

Overview

With FireMonkey you can create great looking HD and 3D user interfaces, but at the end, in enterprise applications, it is all about data. How data is accessed and how changes to data are persisted. FireMonkey applications can use different strategies to persist data. For example they can use local storage, remote relational database, SOAP web services server or DataSnap server. In all scenarios the user interface and data objects are the same. The data in a FireMonkey application is stored in a "TClientDataSet" component with some test "employee" data. This data is manipulated using "TStringGrid" and "TBindNavigator" visual components, which are connected to the client dataset using LiveBindings "TBindDBGridLink". Different scenarios show different ways of persisting data stored in the application.

Scenario 1: Local Storage

In this scenario we use TClientDataSet methods to persist data in a local file. There are two buttons in the user interface that call "SaveToFile" and "LoadFromFile" methods to update and refresh data.

Scenario 2: RDBMS client

In this scenario the client dataset component is connected to a "TDataSetProvider" component, which in turn is connected to a dbExpress "TSQLTable" component connected a a "TSQLConnection" component. The connection is configured to point to a demo "mastsql" InterBase database and the table component is pointing to "EMPLOYEE" table. By reopening client dataset and reconnecting to a database using checkboxes you can refresh data, and you can update data using a button that calls "TClientDataSet.ApplyUpdates" method that sends data to the underlying data store.

Scenario 3: Cloud storage

In this scenario we add to a FireMonkey application a data module with cloud data storage access logic. In Delphi XE2 the new CloudAPI has been introduced that abstracts away the underlying implementation of cloud storage. Here I’m using my Amazon Web Services S3 account to store a string value read from the user interface. Instead of storing the actual contents of the TClientDataSet component in the cloud storage, I’m actually using for testing the contents of the "Edit1" component. In order to use this example you need to make sure that valid credentials are declared in the "TCloudCustomConfig" class. You would also need to manually create a bucket and a file in the S3. The bucket and file names should also be entered into the config information.

Scenario 4: SOAP web services

In this scenario there are two projects. There is a Delphi XE2 SOAP Web Server project that implements two methods that "get" and "set" a string value. This value is persisted in a local file on the server. The client is our FireMonkey client application from the previous scenarios. In order to access SOAP web services, we need to use "WSDL Importer" wizard and generate client access code and use it in the user interface to persist an arbitrary string.

Scenario 5: DataSnap client

This scenario is similar to the previous one. This time the server is a standalone Delphi XE2 DataSnap server that provides two server methods for getting and setting a string value. The server functionality implementation is the same as in the previous example. In order to access DataSnap server functionality from our FireMonkey client application, we need to add client code to it using "DataSnap Client Module" wizard.

Bad Behavior has blocked 2 access attempts in the last 7 days.

Close