Tuesday, September 28, 2010

The Spiral Command - In Three Parts (Part #1)

The Spiral Command - In Three Parts (Part #1)
In this post and the next two posts, I will be building a spiral command.  In this post we will be dealing with the algorithm.  When we are done with this post, we will have a command that draws a spiral that would have 20 loops if it started in the middle, but we also told the algorithm to not draw the innermost 10 loops.  We have also hard currently coded the command to draw the spiral with a distance of 10 between each loop.

I will be using the name MYSPIRALM for this temporary command.  When finished, this will be the command for the macro version.

Note:  There is a naming convention that states if you have both a dialog driven command and a comand that is all command line for macros, the macro version ends with an M.

 Hardcoded values:
  • Distance Between Loops: 10
  • Number of Inner Loops Not Drawn: 10
  • Total Number of Loops: 20
  • Clockwise/Counter Clockwise: Clockwise
  • Growth Between Loops: None
Our next post will implement RecData's to get the values that we currently are hardcoded.  Then, we will finish up with a dialog driven command.

Friday, August 20, 2010MyLine 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

Code Snippet - MySpiralM Command
  1. ////////////////////////////////////////////////////////////////////////////
  2. //
  3. //    File Name: MySpiral.cpp
  4. //    Written by: L. Lee Saunders
  5. //    (C)2010 Beer & Pretzel Games
  6. //    All rights reserved
  7. //
  8. ////////////////////////////////////////////////////////////////////////////
  9.  
  10. #include <windows.h>
  11. #include <math.h>
  12.  
  13. extern "C"
  14. {
  15.     #include <xp.h>
  16.     #include <Extend/Mysvc.h>
  17. }
  18.  
  19. extern XP MyXP;
  20.  
  21. ////////////////////////////////////////////////////////////////////////////
  22.   
  23. void XPCALL DrawSpiral (int Result,int Result2,int Result3);
  24. GPOINT2 CenterPoint;
  25.  
  26. FORMST(lpszCenterPoint,"Center Point:\0")
  27.   
  28. RDATA PCenterReq =
  29.    { sizeof(RDATA), RD_2DC, NULL, RDF_C, (DWORD*)&CenterPoint,
  30.   (DWORD*)&lpszCenterPoint, RDC_XH, DrawSpiral, NULL, NULL, 0, NULL, 0};
  31. ////////////////////////////////////////////////////////////////////////////
  32.   
  33.   
  34. ////////////////////////////////////////////////////////////////////////////
  35. void XPCALL MySpiralM (void)
  36. {
  37.     ReqData(&PCenterReq);  //get center point
  38. }
  39. ////////////////////////////////////////////////////////////////////////////
  40.  
  41.  
  42. ////////////////////////////////////////////////////////////////////////////
  43. void XPCALL DrawSpiral (int Result,int Result2,int Result3)
  44. {
  45.     if (Result != X_OK) { CmdEnd(); return; }
  46.  
  47.     float Growth = 1;
  48.     float LoopDistance = 10;
  49.  
  50.     int Rotations = 20;
  51.     int InnerLoopsToSkip = 10;
  52.     const char * Direction = "0\0";
  53.  
  54.     pENTREC pEntRec;  //Create a new drawing database entity
  55.     
  56.     float CenterX = CenterPoint.x;
  57.     float CenterY = CenterPoint.y;
  58.  
  59.     int iDirection;
  60.  
  61.     MarkUndo();
  62.  
  63.     // Insert an empty path into the drawing list
  64.     pEntRec=DLApndE(NULL, ET_PATH2, sizeof(PATH2)+sizeof(GPOINT2));
  65.  
  66.     // Get the common stuff
  67.     GetCStuff(pEntRec);
  68.  
  69.     // Set the smoothing to Parabolic Blend (through-point)
  70.     pEntRec->Path.Path.SmType = SM_PB;
  71.  
  72.     // Set the centerpoint of the spiral
  73.     pEntRec->Path.Path.Nodes[0].x = CenterPoint.x;    
  74.     pEntRec->Path.Path.Nodes[0].y = CenterPoint.y;
  75.  
  76.     // How many points to draw per circle
  77.     const double STEPS_PER_ROTATION = 50;
  78.     
  79.     // Amount to add to angle at each step
  80.     double increment = M_PI/STEPS_PER_ROTATION;  // Splitting up
  81.  
  82.     double theta = 2*M_PI*InnerLoopsToSkip;  // This allows for an open area
  83.     // inside the spiral
  84.  
  85.     // Determines if the loops rotates clockwise or counter clockwise
  86.     iDirection = strcmp(Direction, "0") == 0 ? 1 : -1;
  87.  
  88.     while(theta < (Rotations * 2) * M_PI)
  89.     {
  90.         pEntRec=DLResize(pEntRec, pEntRec->Path.CStuff.ERLen +
  91.           sizeof(GPOINT2));
  92.  
  93.         pEntRec->Path.Path.Nodes[pEntRec->Path.Path.Count].x=(float)
  94.           (CenterX + theta * cos(theta) * (1/(M_PI * 2)) * LoopDistance *
  95.           iDirection);
  96.         pEntRec->Path.Path.Nodes[pEntRec->Path.Path.Count].y=(float)
  97.           (CenterY + theta * sin(theta) * (1/(M_PI * 2)) * LoopDistance);
  98.         
  99.         pEntRec->Path.Path.EParm=(float)pEntRec->Path.Path.Count;
  100.         pEntRec->Path.Path.Count++;
  101.         
  102.         LoopDistance = LoopDistance * Growth;
  103.         theta = theta + increment; //If theta grows faster then increment,
  104.         //it will have wider spaces between each loop    
  105.     }
  106.                 
  107.     EDraw(pEntRec);  //Draw our line
  108.     ShowChanges();   //Needed for CC3 to "Show Changes" to the DB
  109.  
  110.     CmdEnd();
  111.     return;
  112. }
  113. ////////////////////////////////////////////////////////////////////////////

Thursday, September 2, 2010

How to walk the "Drawing List"

In trying to decide what element of CC3 XP programming I was going to blog about next, I started to re-read all of the old posts on the cc2-dev-l email list @ groups.yahoo.com. 

Wow, I had forgotten how much valuable information was out there. If you have the time (And it will take a bit of time) just start at the beginning and read through the threads.  I promise that you will learn something – I did!

I found a request by Linda Kekumu.  It seemed that she had a large number of maps that had lots of Notes attached to them and they were wrong.  She needed a quick way to clear out all the notes on a map, instead of manually removing them one at a time.  Peter responded with this dash of code.

The reason I selected it for this post is that it is probably the smallest piece of code that displays how to use DLScan.  Now DLScan does exactly as the name says, it scans the “Drawing List” (i.e. the drawing database) and calls a function for every “identified” element.  I say “identified” because there are times when you only want to look at a subset of the entire Drawing List.

In this new XP command, we scan the Drawing List with the DLS_Std flag, telling DLScan that we want to scan the Drawing List for “all standard all non-erased elements”.

Then in our called function (ClearNotesScan) we check the EType of each identified  element (pEntRec) .  If the EType equals ET_Note, we know it is a Note and we call DLErase to remove the element from the Drawing List.

So, with this, you can now scan through a map and look at every identified element!

Code Snippet – MyClearNotes Command
  1. /////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Program: CC3DeveloperBlog.dll
  4. // File Name: MyClearNotes.cpp
  5. // Written by Peter Olsson (Copied here by L. Lee Saunders)
  6. // (C)2010 Beer &amp; Pretzel Games
  7. // All rights reserved
  8. //
  9. /////////////////////////////////////////////////////////////////////////////

  10. #include <windows.h>
  11. #include <math.h>

  12. extern"C"
  13. {
  14.     #include <xp.h>
  15.     #include <math.h>
  16. }
  17. extern XP MyXP;
  18. /////////////////////////////////////////////////////////////////////////////


  19. /////////////////////////////////////////////////////////////////////////////
  20. DWORD XPCALL ClearNotesScan(hDLIST hDList, pENTREC pEntRec, PARM p1, PARM p2)
  21. {
  22.     //If the Entity Record is of type "Note" then erase the Entity from DL
  23.     if(pEntRec->CStuff.EType==ET_NOTE) { DLErase(pEntRec); }
  24.     return 0;
  25. }
  26. /////////////////////////////////////////////////////////////////////////////


  27. /////////////////////////////////////////////////////////////////////////////
  28. void XPCALL MyClearNotes(void)
  29. {
  30.     //Description copied from FCW32.TXT
  31.     //---------------------------------------------------------------
  32.     // ClearSel - Clear All Selection bits
  33.     //---------------------------------------------------------------
  34.     ClearSel();

  35.     //This sets an undo point so that every action after this to the end of
  36.     //the command will be reversed if the user selects UNDO.
  37.     MarkUndo();

  38.     //Drawing list scan. We are interested in only the first three parameters
  39.     // Param #1: Drawing list - If null, it is the main list
  40.     // Param #2: Our Callback function for every identified element in the DL
  41.     // Param #3: DSFlags. How we identify what DL elements we want
  42.     DLScan(NULL, ClearNotesScan, DLS_Std, 0, 0);

  43.     //End the command
  44.     CmdEnd();
  45. }
  46. /////////////////////////////////////////////////////////////////////////////