Friday, August 20, 2010

MyLine Command (A Complete Example)

We have finally reached the point where we can produce an entire example.   In my previous post I explained how, conceptually, this command will work.  Now, here is the actual code for the command along with a lot of comments.

I have also copied and commented out the definitions for the relevant elements that we are going to create.

I hope that you fire up Visual Studio and try building this code; if you are impatient though and want to just run it, here is the dll.

Code Snippet - MyLine Command (A Complete Example)
  1. ////////////////////////////////////////////////////////////////////////////
  2. //
  3. //    Written by: L. Lee Saunders
  4. //    Date: 8/15/2010
  5. //    (C)2010 BeerAndPretzelGames.com
  6. //    All rights reserved
  7. //
  8. ////////////////////////////////////////////////////////////////////////////
  9. #include <windows.h>
  10.  
  11. extern "C"
  12. {  
  13.     #include <XP.H>
  14.     #include <Extend/Mysvc.h>
  15. }
  16.  
  17. #define XPID 0xF000
  18.  
  19. void XPCALL About(void);
  20. void XPCALL MYLINE(void);
  21.  
  22. char CList[]="MYLINE\0";
  23. PCMDPROC PList[]={About,MYLINE};
  24.  
  25. XP MyXP =
  26.    { 0, CList, PList, 0, 0, 0, XPID, 0, 500, 0, 0, 500 };
  27. ////////////////////////////////////////////////////////////////////////////
  28.  
  29.  
  30. ////////////////////////////////////////////////////////////////////////////
  31. FORMST(MyAPkt,"BLOG DLL commands:\n\n"
  32.        "\tMYLINE - Duplicating the Line Command #s\n\0")
  33.  
  34. void XPCALL About (void)
  35. {
  36.     FormSt(&MyAPkt,RSC(FD_MsgBox));
  37. }
  38. ////////////////////////////////////////////////////////////////////////////
  39.  
  40.  
  41. ////////////////////////////////////////////////////////////////////////////
  42. void XPCALL MYLINE2 (int Result,int Result2,int Result3);
  43. void XPCALL MYLINE3 (int Result,int Result2,int Result3);
  44.  
  45. //This is the definition of LINE2
  46. //==========================================================================
  47. //typedef struct
  48. //{
  49. //    CSTUFF    CStuff;              // entity properties
  50. //    GLINE2    Line;                // line properties
  51. //} LINE2;
  52.  
  53. //This is the definition of GLINE2
  54. //==========================================================================
  55. //typedef struct
  56. //{
  57. //    GPOINT2 p1;                    // starting point
  58. //    GPOINT2 p2;                    // ending point
  59. //} GLINE2;
  60.  
  61. //This is the definition of GPOINT2
  62. //==========================================================================
  63. //typedef struct
  64. //{
  65. //    float x;
  66. //    float y;
  67. //} GPOINT2;
  68.  
  69. //This is the definition of CSTUFF
  70. //==========================================================================
  71. //typedef struct
  72. //{
  73. //    int    ERLen;                    // entity record length
  74. //    unsigned char    EType;          // entity type code
  75. //    char    EFlags;                  // erase/select bits
  76. //    char    EFlags2;                 // extra flags
  77. //    unsigned char    EColor;         // entity color
  78. //    unsigned char    EColor2;        // fill (2nd) color
  79. //    char    EThick;                  // pen thickness 0..25.4 mm
  80. //    short    WPlane;                 // workplane (0 = XY plane)
  81. //    short    ELayer;                 // layer
  82. //    short    ELStyle;                // line style (0=solid)
  83. //    short    GroupID;                // group id (0 = not grouped)
  84. //    short    EFStyle;                // fill style (0=hollow)
  85. //    float    LWidth;                 // line width
  86. //    int    Tag;                      // entity tag id
  87. //} CSTUFF;
  88.  
  89. //This is the working structure that we will use to create all the lines
  90. LINE2 BuildL = {
  91.     sizeof(LINE2),                // entity record length
  92.     ET_LINE2,                     // entity type code
  93.     0,                            // erase/select bits
  94.     0,                            // extra flags
  95.     1,                            // entity color
  96.     1,                            // fill (2nd) color
  97.     0,                            // pen thickness 0..25.4 mm
  98.     0,                            // workplane (0 = XY plane)
  99.     1,                            // layer
  100.     0,                            // line style (0=solid)
  101.     0,                            // group id (0 = not grouped)
  102.     0,                            // fill style (0=hollow)
  103.     0.0,                          // line width
  104.     0,                            // entity tag id
  105.     0.0,                          // starting point: x
  106.     0.0,                          // starting point: y
  107.     1.0,                          // ending point: x
  108.     1.0                           // ending point: y
  109. };
  110.  
  111. FORMST(lpszFirstPoint,"1st point:\0")
  112.  
  113. FORMST(lpszNextPoint,"Next point:\0")
  114.  
  115. RDATA P1Req =
  116.    { sizeof(RDATA), RD_2DC, NULL, RDF_C, (DWORD*)&BuildL.Line.p1,
  117.     (DWORD*)&lpszFirstPoint, RDC_XH, MYLINE2, NULL, NULL, 0, NULL, 0};
  118.  
  119. RDATA P2Req =
  120.    { sizeof(RDATA), RD_2DC, NULL, RDF_C, (DWORD*)&BuildL.Line.p2,
  121.     (DWORD*)&lpszNextPoint, RDC_RBAND, MYLINE3, NULL, NULL, 0, NULL, 0};
  122.  
  123. ////////////////////////////////////////////////////////////////////////////
  124.  
  125.  
  126. ////////////////////////////////////////////////////////////////////////////
  127. void XPCALL MYLINE (void)
  128. {
  129.     ReqData(&P1Req);  //get first point
  130. }
  131. ////////////////////////////////////////////////////////////////////////////
  132.  
  133.  
  134. ////////////////////////////////////////////////////////////////////////////
  135. void XPCALL MYLINE2 (int Result,int Result2,int Result3)
  136. {
  137.     if (Result != X_OK) { CmdEnd(); return; } //If we did not get valid info
  138.     
  139.     MarkUndoAdd();  //Enable Undo
  140.     NewCsrOrg(BuildL.Line.p1.x, BuildL.Line.p1.y);  //Set cursor origin
  141.     ReqData(&P2Req);  //Get next point
  142. }
  143. ////////////////////////////////////////////////////////////////////////////
  144.  
  145.  
  146. ////////////////////////////////////////////////////////////////////////////
  147. void XPCALL MYLINE3 (int Result,int Result2,int Result3)
  148. {
  149.     pENTREC pEntRec;  //Create a new drawing database entity
  150.  
  151.     if (Result != X_OK) { CmdEnd(); return; } //If we did not get valid info
  152.  
  153.     //This gets all the current stuff like current color and assigns it
  154.     //to our line entity.
  155.     GetCStuff(&BuildL.CStuff);
  156.  
  157.     //Append a copy of our line to the DB and return a pointer to the new
  158.     //entity in the database.  We assign it to pEntRec.
  159.     pEntRec=DLApnd(NULL,(pENTREC)&BuildL);
  160.     
  161.     EDraw(pEntRec);  //Draw our line
  162.     ShowChanges(); //Needed for CC3 to "Show Changes" to the DB
  163.  
  164.     BuildL.Line.p1.x=BuildL.Line.p2.x;  //move 2nd x to 1st x
  165.     BuildL.Line.p1.y=BuildL.Line.p2.y;  //move 2nd y to 1st y
  166.     
  167.     NewCsrOrg(BuildL.Line.p1.x,BuildL.Line.p1.y);  //Set cursor origin
  168.  
  169.     ReqData(&P2Req);  //Get next point
  170. }
  171. ////////////////////////////////////////////////////////////////////////////
  172.  
  173.  
  174. ////////////////// DllMain - XP initialization & Unload code ///////////
  175. BOOL WINAPI DllMain (HINSTANCE hDLL, DWORD dwReason, LPVOID lpReserved)
  176. {
  177.     switch (dwReason)
  178.     {
  179.         case DLL_PROCESS_ATTACH:
  180.         {
  181.             MyXP.ModHdl=hDLL;
  182.             XPRegCmd(&MyXP);
  183.             break;
  184.         }
  185.         case DLL_PROCESS_DETACH:
  186.         {
  187.             XPUnregCmd(&MyXP);
  188.             break;
  189.         }
  190.     }
  191.     return TRUE;
  192. }
  193. ////////////////////////////////////////////////////////////////////////////

Sunday, August 15, 2010

Beads – How to Wait for Input

[Note: This is copied from the "FastCAD XP Programming Reference" that comes with the XP toolkit.  I copied this, with editing only for clarity, because it was so descriptive]

Nowhere in the Windows programming environment will you find a greater need to change your thinking than with regard to getting input.

In Windows, our programs receive messages that inform us of discrete user input - a mouse movement, button click, or a keyboard press. Other than dialog boxes, there is no way provided to wait for a complete word or line of text or other aggregate input in your program and then continue. We must write our program so that each keystroke is processed and then control is returned all the way to Windows.

This means that in a piece of code (creating a text entity, for example) we can not have one procedure that calls another to get a line of text and then continues with the next instruction when it is available, as would be done in a DOS or Unix program. The current position within that first command procedure is normally maintained on the stack, perhaps as a nested chain of procedure calls.

In Windows, after every event (such as a single key press), we have to return all the way up the stack back to Windows! This is why most Windows programs do all of their input through dialog boxes.

The FastCAD solution to this problem is to think of a command as a string of "beads" - little pieces of a command that each do one small part of the overall command, each a piece that can be done in response to an appropriate input event.

Lets take as an example a command to create a line entity. In traditional programming, its logic might be:

LineCommand:

prompt the user for the first point
- Get the first point if there was a problem getting it, end the command

prompt the user for the second point
- Get the second point if there was a problem getting it, end the command

Create the line entity

Append it to the drawing database

Draw the line entity

End the command

In FastCAD, we have an API service, ReqData, that handles data requests. We call ReqData with a packet that tells it about the data request, and then we go back to Windows.

When the data becomes available, ReqData will call another procedure specified in our original request packet to continue with our command. This means that we write our command as a series of procedures, linked together by RDATA packets, each one executing when its data becomes available, and returning to Windows when it needs to wait for more input.

The sequence looks like this:
==========================
Event: Action (Bead procedure):
- LINE function entered
- - Request first point by calling ReqData
- Line function ends

User enters the first request for a point:

Event: Action (Bead procedure):
- Second LINE function entered
- - Did the user cancel the request?
- - - End Command
- - Request second point by calling ReqData
- Second Line function ends

User enters the second request for a point:

Event: Action (Bead procedure):
- Third LINE function entered
- - Did the user cancel the request?
- - - End Command
- - Else
- - - Create the line entity
- - - Append it to the drawing database
- - - Draw the line entity
- - - End Command
-  Third Line function ends

The LINE command is implemented in 3 beads, each of which is called in response to a single event, such as a mouse click or a RETURN keypress. The ReqData service provides all of the intermediate beads that track mouse movement and build up strings of characters into words like "LINE" for you.

Next post, we will start to implement the LINE command.

Tuesday, August 3, 2010

Ok, you requested data, now what did you get?

Ok, so you have messed around with the RDATA function and you want to select an element, like a circle.  So you create a new RDATA function with the fallowing parameters:
  • Cursor:  RDC_PICK
  • Flags:  RDF_C
  • Type:  RD_Pick1
 Now create a data store variable of type pEntRec.

A pEntRec type is a very important type in writing XP's.  The pEntRec type is the generic entity pointer that can point to any type of drawing entity.  So, if you selected a circle with your RDATA call, pEntRec will point to the the circle entry within the "Drawing Database"

The "Drawing Database" is the collection of all the visible elements in the map.  When we select elements, Campaign Cartographer returns a pointer to the selected element in the "Drawing Database".

Once you get a pointer to an entity, you can edit, interrogate or delete it.

Every entity shares some common data - appropriately named: CSTUFF

CStuff is defined as:
Code Snippet - CSTUFF
  1. typedef struct
  2. {
  3.     int              ERLen;        // entity record length
  4.     unsigned char    EType;        // entity type code
  5.     char             EFlags;       // erase/select bits
  6.     char             EFlags2;      // extra flags
  7.     char             EColor;       // entity color
  8.     char             EColor2;      // fill (2nd) color
  9.     char             EThick;       // pen thickness 0..25.4 mm
  10.     short            WPlane;       // workplane (0 = XY plane)
  11.     short            ELayer;       // layer
  12.     short            ELStyle;      // line style (0=solid)
  13.     short            GroupID;      // group id (0 = not grouped)
  14.     short            EFStyle;      // fill style (0=hollow)
  15.     float            LWidth;       // line width
  16.     int              Tag;          // entity tag id
  17. } CSTUFF;


For example you have a pEntRec named pENTREC and you want to get the entities color:
pENTREC->CStuff.EColor

Sunday, July 25, 2010

Requesting data from CC3 - RDATA

Last post I introduced the two most important functions exposed by CC3: RDATA & FORMST.

In this post I'll discuss the internals of RDATA.

Here are the important elements you need to configure for every time you declare an RDATA.

  • Name - This is simply the name the holds all the separate RDATA elements together
  • Type - This is where you declare what kind of data you are requesting
  • Flags - These flags are the switches that tweak the RDATA call to do exactly what you want
  • Data Store - Where to "put" what you have requested after it has been selected
  • Prompt - This is the text that will be displayed to the user
  • FormSt declaration - This is where you decide if you want to include the prompt declaration. If you already have exactly the prompt you want already declared, you can reuse it instead of creating a new one
  • Cursor - Obviously selecting a single item you want a different cursor than if you want them to type in some text
  • Return Procedure

When I first started to write XP's, Super Guru Peter Olsen sent me a little tool that he wrote to automatically generate the RDATA call. When I was preparing for this post, I asked Peter if he still had the app. Well, he did, but he suggested that I create an online version of the tool. So, here is a Silverlight 4 application that generates an RDATA structure for you.



In my next post, I'll use the tool to generate our RDATA and select our first element. Plus, we will start to discuss what it is exactly, that we get back from our ReqData call.

Saturday, July 24, 2010

Part 2 of a Campaign Cartographer 3 XP - Or, the best part for last.

In a previous post, I discussed how an XP is in three distinct parts. This post is about the second part: Where the rubber meets the road.

Because this is the main part of the XP, describing this section in any detail would be like trying to describing how any a website works by describing how HTML works.

But, there are two main functions of every XP. Input and Communicating with the user.

Input: Feeding data from you map into your XP is a major function. If you cannot select elements or locations (points), you cannot have much of an impact on your drawing.

Communication: Having the ability to select something without telling the user what your command is expecting the user to do.

These two functions are accomplished using RDATA (Request Data) and FORMST (Format String). Using these two together, we can prompt the user, with FORMST, that we currently want a type of data (a string, a point, an element etc.) and with RDATA, we are informing CC3 that it needs to let the user collect the data we want.

Here is a simple example that implements an XP command to enter a text word:

Code Snippet: XP dll Part #2
  1. void XPCALL GotText(int Result, int Result1, int Result2);
  2.  
  3. FORMST(lpszGetText, "Enter Text:")
  4.  
  5. RDATA rGetText = {sizeof(RDATA), RD_TxWord, NULL, RDF_C, (DWORD *)Text,
  6. (DWORD *)&lpszGetText, RDC_NONE, GotText, NULL, NULL, 0, NULL, 0};
  7.  
  8. void XPCALL GetText(void)
  9. {
  10.     ReqData(&rGetText);
  11. }
  12.  
  13. void XPCALL GotText(int Result, int Result1, int Result2)
  14. {
  15.     if(Result==X_OK)
  16.     {
  17.     }
  18.     else
  19.     {
  20.         CmdEnd();
  21.     }
  22. }

Line 1: This is the prototype for the method that RDATA will call when it has gotten a hold of the data you requested.

Line 3: This is the FORMST declaration. This declaration creates a variable (lpszGetText) and assigns it the text "Enter Text:".  True, we could have created a string with this text, but RDATA is expecting a FORMST variable.  Plus, when we get further into the power of FORMST, you will see that is it not merely a replacement for a char array.

Lines 5 & 6:  This is the RDATA declaration.  When you learn to read it, you will see that it says it is going to go get a "Word" (a string of characters without whitespace), store it in the char array Text (not declared in the code snippet), diplaying no visible cursor, displaying the text in the FORMST as our prompt and calling our method, GotText, when the user is done entering the request.

Line 8 - 11: This is our commands original method.  It would have been declared and prototyped in Part 1 of the XP code.

Line 10:  This is the call that invokes our RDATA.

Line 13 - 22: This is our method that RDATA will call when it has gotten a hold of the data you requested.

Line 15:  This line shows that the Result parameter contains the "Status" of the request we made.  If it is "OK" then we can further process what we got.

Line 20:  CmdEnd() is the XP command that notifies CC3 that our XP function has come to an end.

Tuesday, July 20, 2010

Part 3 of a Campaign Cartographer 3 XP

In a previous post, I discussed how an XP is in three distinct parts. This post is about the third part: XP DllMain Boilerplate.

Here is the third part (right below your usings) of an Example XP dll. Remember that this is the very last part of a XP dll:

Code Snippet: XP dll Part #3
  1. #pragma unmanaged
  2. BOOL WINAPI DllMain (HINSTANCE hDLL, DWORD dwReason, LPVOID lpReserved)
  3. {
  4.     switch (dwReason)
  5.     {
  6.         case DLL_PROCESS_ATTACH:
  7.         {
  8.             MyXP.ModHdl=hDLL;
  9.             XPRegCmd(&MyXP);
  10.             break;
  11.         }
  12.         case DLL_PROCESS_DETACH:
  13.         {
  14.             XPUnregCmd(&MyXP);
  15.             break;
  16.         }
  17.     }
  18.     return TRUE;
  19. }

In truth, there is only one part that is optional. That is Line 1.

Line 1: This line is only needed if you are mixing standard C/C++ with .net code.

Otherwise, just copy it all at the bottom of the file and it will just work.

Sunday, July 18, 2010

Part 1 of a Campaign Cartographer 3 XP

In the last post, I discussed how an XP is in three distinct parts. This post is about the first part: XP Command declarations.

Here is the first part (right below your usings) of an Example XP dll:

Code Snippet: XP dll Part #1
  1. #define XPID 0xF000
  2.  
  3. void XPCALL About(void);
  4. void XPCALL Example1(void);
  5. void XPCALL Example2(void);
  6.  
  7. char CList[]="EX1\0EX2\0\0";
  8. PCMDPROC PList[]={About, Example1, Example2};
  9.  
  10. XP MyXP = { 0, CList, PList, 0, 0, 0, XPID, 0, 500, 0, 0, 500 };


Line 1: This defines the Xp ID. In the official documentation from Evolution Computing, the description is:

If your XP is going to be a commercial product, you will need to obtain an XPID number from Evolution Computing.

XPID numbers are unique XP module identifiers that allow FastCAD to identify which XPs should provide support for which custom entities and tools. That is their only purpose. Numbers in the range 0xF000-0xFFFF are available for unregistered use, but will not guarantee uniqueness against other products available to customers.

The only requirement Evolution Computing makes to issue a registered XPID number is that we know the product name, the company providing support, and that the product using it is about to be released for commercial sale. Please do not ask us for a registered XPID until your product is ready for sale.

So, unless you are about ready to sell your new add-on, just use the default.

Lines 3 through 5: These are the prototypes for the entry functions into your new commands. The first one, About, is required. It is called by the XPCFG command. If you have never tried it, run it now.

Go ahead, I'll wait.

Did you run it? If you did, you saw a list of all the loaded XP's and if you selected one of those XP's and clicked the about button, you just ran the about function for that XP. Its a good practice to keep the message box that is displayed up-to-date with a list of the commands you coded in your XP.

You may have also noticed that all the prototypes, except for the name, are exactly the same. This is because when a XP command is activated, it is up to the command to retrieve any and all data it needs, so the prototype is just defining the activation function. Think of it as your commands' Main() function.

Lines 7 & 8: These lines define CList and PList. CList is a string that defines the commands you are exposing to CC3. In the above example those commands are: "EX1" and "EX2". PList is a list of procedure names.

Notice the first one is "About". Remember, "About" is required and it must be the first procedure in the list. The next two procedures are "Example1" and "Example2". These procedures are, because of CList, mapped to the CC3 commands "EX1" and "EX2. If CList listed them in the reverse order: "EX2\0EX1\0\0", then "EX2" would be mapped to "Example1". So just remember that they are order dependant and that the first command always matches with the first procedure AFTER "About", the second command always matches with the second procedure AFTER "About" ... "

And lastly Line 10 is some required boilerplate that takes CList and PList and exposes them to CC3.