The DataCAD Developer Network (DDN) is an online resource for information and support for DCAL® (DataCAD Applications Language) developers as well as anyone interested in creating fonts, toolbars, hatch patterns, or linetypes for use in DataCAD.
#11247 by devinder
Tue Apr 25, 2006 9:35 am
Hi,
Here it goes and I hope this is understandable. I am a programmer :)

1. WantType/dcalstate: This is just a unique ID assigned to a function so that DATACAD knows which function it has to call next. So if you are writing a complex macro then you will need more of these states, as in the case of AEC_MODL macro.

2. act : This could be one of (Afirst, Alast, Aagain, AlSize).
AlSize -> Called only once when state is called for the first time by DataCAD. Macro should tell DataCAD how much local memory it should allocate for your pointer to local record that pl uses. All your variables should for this state should be defined in local record (eg. ArrowL). Though you can have all your variables as global I would not recommend it because in complex macros where you can have multiple states, manageability and re-usability of same code can become an issue.
Afirst -> After the memory has been allocated it is time for initializing it. AFirst tells you can initialize all your parameters at this point. It is in AFirst you specify the first local state number, the state is in. This is the right time to read any INI settings for your macro.
Alast -> This is the last call from DataCAD to this subroutine. Users should clear out any temporary data they have generated or finish any unfinished process. Memory allocated for pl local record (allocated by Alsize) will be freed when DataCAD gets control.
Aagain -> This tells pl and pargs are active variable records and hold information and macro writer decides how the flow of program works.

3. pl : This is a pointer to local record, which can contain variables and track in what state the program is in, while moving from one menu level to another. (eg. pl is a pointer to ArrowL).

4. pargs : This is a pointer to the record that is passed by your calling function to a new menu. This is used when you want to send/update parameters from one state to another state. Eg. when calling getpoint function you pass a variable which is type getpointArg, this variable holds the mouse click, mouse status or keyboard status. This status is set in a different dcalstate called getpoint.

DataCAD controls these states/variables and you should not update these variables directly.

Hope this helps.
#11270 by devinder
Wed Apr 26, 2006 12:56 pm
DataCAD looks for 'Main' procedure inside the DMX calls and makes a calls with 'XDcalStateBegin'. There is also 'XDcalStateEnd' which represents the end of DCAL states. These states represents these indexes are reserved for user macros. Thus a single macro you can have up-to 100 states. These meaning to these indexes are given by macro author. Any number except in the range (XDcalStateBegin to XDcalStateEnd) are controlled by DataCAD and you should not worry about them. Example after you call getpoint:
ex. getpoint(l.getpargs,iwant);
the value for iwant gets set to 3 by DataCAD. This value can change at any time in future but will not effect your code as long as you use getpoint. Using iwant = 3 in future can cause macro not to work properly.

If you follow AEC_MODL you will see there are about 25 states, starting from XDcalStateBegin. Main procedure in aec_modl_main.pas and all constants defined in winddoor.pas.

We have used asint, afloat so it is easier to change the scope of variable from integer to longint or extended in the future. This is not likely to happen soon. You should also not change the scope, otherwise they will mismatch with DataCAD and cause DataCAD to crash or misbehave. You can define your local variables as integer, double, if you wish.

I would recommend debugging AEC_MODL macro to give more idea. Sometimes visualization gives more feedback than words.
#11273 by David A. Giesselman
Wed Apr 26, 2006 1:23 pm
StudioPM wrote:Now a question of "method": is this the right way to ask this kind of questions? Would you suggest another way, such as a direct contact somewhere/somehow? Thanks.


Hello Pietro,

I would ask that you please continue to use this forum as your main line of communicaton. An open discussion of this type may prove useful to others looking to develop tools and applications using DCAL for Delphi.

Best regards,
Dave
#11278 by Miguel Palaoro
Wed Apr 26, 2006 2:14 pm
David A. Giesselman wrote:...I would ask that you please continue to use this forum as your main line of communicaton. An open discussion of this type may prove useful to others looking to develop tools and applications using DCAL for Delphi.


Clap, Clap, Clap!

Miguel
#11307 by devinder
Fri Apr 28, 2006 10:14 am
Image to infinity and beyond
#11320 by devinder
Mon May 01, 2006 4:23 pm
All the wrterr/wrtmsg/... made its way into DCAL for Delphi but new procedure SetLocalSize/SetArgs/... never made it to the manual. If you need any information regarding a subroutine i will be glad to help you out.

Here is an Arrow example that depicts use of Arguments passed from one function to another. I have passed parameters for colors from one function to another. You will also notice that new local state machine does not use Aagain at all because that subroutine doesn't have much to do. Some new records and functions have been added.

Hope this helps.

Code: Select alllibrary arrow;

uses
  //Sharemem, { Do not use. All DataCAD parameters limited to short strings}
  Windows,
  Dialogs,
  Forms,
  URecords in '..\..\Headers\URecords.pas',
  UInterfaces in '..\..\Headers\UInterfaces.pas',
  UConstants in '..\..\Headers\UConstants.pas',
  UInterfacesRecords in '..\..\Headers\UInterfacesRecords.pas',
  UVariables in '..\..\Headers\UVariables.pas';

{$E .dmx}

const
   XDcalStateColor = XDcalStateBegin + 1;
type
   ArrowColorL = record
               state : asint;
            end;
   PArrowColorL = ^ArrowColorL;
   ArrowColorArg = record
              msg : str80;
              isfill : boolean;
              fillcolor : asint;
              end;
   PArrowColorArg = ^ArrowColorArg;
   ArrowL = record
      state: asint;
      pnt: array [1..2] of point;
      numpnt: asint;
      arrwidth: afloat;        { the width of the arrow }
      getclr : ArrowColorArg;
      case byte of
         0: (getp: getpointarg);
         1: (getd: getdisarg);
   end;
   PArrowL = ^ArrowL;

PROCEDURE callgetarrowcolor(var gc : ArrowColorArg; msg : str80; var iwant : wanttype);
begin
   iwant := XDcalStateColor;
   SetArgs(@gc);
   gc.msg := msg;
end;

PROCEDURE addarrow(pnt1, pnt2: point; Width: afloat; isfill : boolean; fillcolor : asint);
{
   Input parameters: pnt1 - location of center tail of arrow
                     pnt2 - location of arrow tip
                     width - maximum width of arrow

   Note: This procedure uses NO global variables, only parameters which
         are passed to it.

                                                  o p5
                                                  |\
          p7                                      | \
            o-------------------------------------o  \
            |                                   p6    \
            |                                          \
       pnt1 o (local origin)                            o p4, pnt2
            |                                          /
            |                                   p2    /
            o-------------------------------------o  /
          p1                                      | /
                                                  |/
                                                  o p3
}
VAR
   ent: entity;
   pnt: array [1..7] of point;
   tmppnt: point;
   i: integer;
   ang: afloat;
   len: afloat;
   cose: afloat;
   sine: afloat;
   atr : attrib;
BEGIN
   {
      This section of code calculates the x and y values for the 7 points
      of the arrow.  All of these calculations assume a local origin
      which is coincident with the center of the tail of the arrow.  The
      7 points are translated and rotated to reflect the position of the
      two input points pnt1 and pnt2.
   }

   len := distance(pnt1, pnt2);    { arrow length from end to tip }
   ang := angle(pnt1, pnt2);       { angle arrow makes with the +x-axis }

   pnt[1].x := 0.0;
   pnt[1].y := -Width / 4.0;

   pnt[2].x := len - Width / 2.0;
   pnt[2].y := -Width / 4.0;

   pnt[3].x := len - Width / 2.0;
   pnt[3].y := -Width / 2.0;

   pnt[4].x := len;
   pnt[4].y := 0.0;

   pnt[5].x := len - Width / 2.0;
   pnt[5].y := Width / 2.0;

   pnt[6].x := len - Width / 2.0;
   pnt[6].y := Width / 4.0;

   pnt[7].x := 0.0;
   pnt[7].y := Width / 4.0;

   {
      This section of code rotates points 1 thru 7 about the local origin
      of the arrow by the angle of the line pnt1--pnt2, then translates
      each point from the local origin to the point pnt1.
   }

   cose := cos(ang);   { pre-calculate these for speed }
   sine := sin(ang);

   FOR i := 1 TO 7 DO
   BEGIN
      tmppnt := pnt[i];
      pnt[i].x := pnt1.x + tmppnt.x * cose - tmppnt.y * sine;
      pnt[i].y := pnt1.y + tmppnt.x * sine + tmppnt.y * cose;
      pnt[i].z := zbase;
   END;

   {
      This section of code creates polygon entity which outline
      the arrow.  Note that stopgroup is called before and after this
      process so that the entity will become its own group.
      Note that ent_init is first called for entity added to the
      database. The current settings for linetype, linecolor, lineweight,
      and lineovershoot will be assumed.
   }

   stopgroup;                    { make the entity into a group }
   ent_init(ent, entply);
   ent.plynpnt := 7;
   For  i := 1 to 7 do
   begin
      ent.plypnt[i] := pnt[i];
      ent.plyisln[i] := True;
   end;
   ent_add(ent);
   if isfill then
   begin
      atr := InitSolidFillAttribute(RGBtoDCADRGB(fillcolor),0,0);
      atr_add2ent(ent,atr);
   end;
   ent_draw(ent, drmode_white);    { draw the polygon on the screen }
   stopgroup;                    { make the entity into a group }
END;

Function GetColor(var Color : asint) : boolean;
Var
   ClrD : TColorDialog;
Begin
   result := false;
   ClrD := TColorDialog.Create(Application);
   ClrD.Color := Color;
   ClrD.Options := [cdFullOpen];
   If ClrD.Execute then
   begin
      Color := ClrD.Color;
      result := true;
   end;
   ClrD.Free;
end;


Function arrow_Main(act: action; pl, pargs: Pointer): wantType;
var
   retval: asint;
   l: PArrowL;
BEGIN
   l := PArrowL(pl);
   if act = aagain then
   begin
      case l.state of
         1:
            begin
               if l.getp.Result = res_escape then
               begin
                  case l.getp.key of
                     f1: l.state := 2;
                     f3: l.getclr.isfill := not l.getclr.isfill;
                     f4: l.state := 3;
                     s0: l.state := 0;
                  end;
               end
               else if l.getp.Result = res_normal then
               begin
                  l.pnt[l.numpnt] := l.getp.curs;
                  if l.numpnt = 1 then
                  begin
                     l.numpnt := 2;
                  end
                  else
                  begin
                     addarrow(l.pnt[1], l.pnt[2], l.arrwidth, l.getclr.isfill, l.getclr.fillcolor);
                     l.numpnt := 1;
                  end;
               end;
            end;
         2,3: l.state := 1;
         else l.state := 0;
      end;
   end;
   if act = afirst then
   begin
      l.state := 1;
      l.numpnt := 1;
      l.getclr.isfill := false;
      l.getclr.fillcolor := RGB(255,255,255);
      l.arrwidth := 2.0 * onefoot;  { first time initialize arrwidth to 2 feet }
   end
   else if act = alsize then
   begin
      SetLocalSize(sizeof(l^));
   end;
   if act <> alsize then
   begin
      wrterr('DCAL Macro ARROW - Version 2.0.');
      case l.state of
         1:
            begin
               wrtlvl('Arrow');    { the 8 character identifier on the message line }
               lblsinit;               { initialize the function key labels }
               lblset(1, 'Width','Set Width of Arrow');   { set the function key labels }
               Lblsett(3,'Fill Color',l.getclr.isfill);
               if l.getclr.isfill then
                  Lblset(4,'Color');
               lblset(20, 'Exit');
               lblson;                 { turn on the function keys }
               { prompt the user for what to do }
               if l.numpnt = 1 then
               begin
                  wrtmsg('Enter first point of arrow centerline.');
               end
               else
               begin
                  wrtmsg('Enter second point of arrow centerline.');
                  { tell DataCAD to use one of its internal dragging routines }
                  dragBar(l.pnt[1], l.arrwidth / 2.0, l.arrwidth / 2.0, 0.0, 0.0,
                     True, currentstate.color{linecolor});
               end;
               getpoint(l.getp, retval);
            end;
         2:
            begin
               wrtmsg('Enter arrow width: ');
               getdis(l.arrwidth, l.getd, retval);
            end;
         3:
            begin
               callgetarrowcolor(l.getclr, 'Select arrow color', retval);
            end;
         else
            retval := XDone;
      end;
   end;
   Result := retval;
end;

Function arrow_getcolor(act: action; pl, pargs: Pointer): wantType;
var
   args : PArrowColorArg;
BEGIN
   if act = afirst then
   begin         
      args := PArrowColorArg(pargs);
      wrtmsg(args.msg);
      lblsinit;               { initialize the function key labels }
      lblson;
      args.isfill := GetColor(args^.fillcolor);
   end
   else if act = alsize then
   begin
      SetLocalSize(sizeof(ArrowColorL));
   end;
   Result := Xdone;
end;

Function Main(dcalstate: asint; act: action; pl, pargs: Pointer): wantType; stdcall;
begin
   case dcalstate of
      XDcalStateBegin: Result := arrow_main(act, pl, pargs);
      XDcalStateColor: Result := arrow_getcolor(act, pl, pargs);
      else Result := XDone;{Necessary}
   end;
end;


exports
   Main;{This is the entry function that will be called by DataCAD. All DCAL Dlls
   will have this function.}
   
begin

END.
#11365 by Jsosnowski
Wed May 03, 2006 8:30 pm
The recent activity in this discussion has drawn me to tackle the actual interface between Datacad and the macros. The previous discussions have been very helpful, Thank You!

I am not clear on the uses of the Pl versus Parg variables in the main call to Datacad. the arg parameters i see as the arguments necessary for datacad to use its various input procedures as defined in their type definitions in UInterfacesrecords. The Pl pointer, however, I am not as clear about. IN the arrows example the Pl variable links to ArrowL record. IN AEC_Modl it appears to link with the procedure DCALMacro. What is the difference between these two pointer paramters
? Something isn't clicking for me.
EDIT:
After posting this I reread everything above including devinders post from April 25th. I would swear that wasn't there before! and very clear! THANKS.

So if I interpret this correctly pl is used by the macro to record its internal progress between loops of the execution and Datacad does nothing with it except pass it back in the next loop, similar to state variable. Datacad is only interested in the contents of Parg. My question here is what is the difference between the state variable in the ArrowL record and the DCALState parameter in the main procedure? By the way, what does the "L" in "ArrowL" stand for?
#11371 by devinder
Thu May 04, 2006 9:13 am
You are right. Pl is used to track the local variables of the state.
Pargs is used between 2 DCAL states. My last post showed this as an example where color parameter is passed from arrow_main to arrow_getcolor using pargs.
When multiple states are involved, the states variables (pl) are stored in DataCAD's internal stack. When you move back to a command the variables are popped out of this stack.
The state variable in ArrowL could be considered as a sub state of DCAL state over which DataCAD has no control. Users can decide what index to define. You can start your state from 1001 or 1234, it does not matter to DataCAD. This state variable is user driven. The DCAL state variable is DataCAD driven.
The character "L" has been used to represent that this is a local record and only one function has access to it. We have records that are used globally example getpointarg or savevar records which are not suffixed with "L". It is just a programming nomenclature. Ex. ArrowL is used only by Arrow_main and likewise ArrowColorL is used by arrow_getcolor. There is no restriction that you cannot use the same record in 2 places, but I would tell you that the contents of pl would be different when used in 2 different DCAL states. Updating one will not cause the other to update automatically
#11603 by Jsosnowski
Wed May 17, 2006 1:02 pm
devinder wrote: ......
4. pargs : This is a pointer to the record that is passed by your calling function to a new menu. This is used when you want to send/update parameters from one state to another state. Eg. when calling getpoint function you pass a variable which is type getpointArg, this variable holds the mouse click, mouse status or keyboard status. This status is set in a different dcalstate called getpoint.
......


:?: I'm a bit confused:
1. You state above ...Eg. when calling getpoint ...This status is set in a different dcalstate called getpoint - what is the difference here?
2. Why exactly is pargs needed? Some datacad interfaces routines appear to work within a macro loop and access data or perform actions from within a single iteration. What is the key to distinguish direct access inside the macro and the need to loop through and use the pargs record types? Is there a resource you can direct me to that explains this process of operating through the dll?
#11630 by devinder
Thu May 18, 2006 8:59 am
Check out the aec_modl macro. door_main refers to g_d variable. this variable is passed to various calling functions (see below)

Code: Select all
      ...
      ...
      d := addr(g_d);
      ...
      ...


         5:
            Begin
               call_door_unit_data(d, retval);
            End;
         6:
            Begin
               call_door_casing_data(d, retval);
            End;
         7:
            Begin
               call_door_trim_data(d, retval);
            End;
         8:
            Begin
               call_door_head_data(d, retval);
            End;
         9:
            Begin
               call_door_jamb_data(d, retval);
            End;
         10:
            Begin
               call_door_sill_data(d, retval);
            End;
         11:
            Begin
               call_door_stop_data(d, retval);
            End;
         12:
            Begin
               call_door_door_data(d, retval);
            End;
         13:
            Begin   
               call_door_knob_data(d, retval);
            End;


Though this variable is a global variable, all of the calling functions refers to g_d indirectly through the use of pargs. This is useful when you have 2 or more same data-types and want to update one of them. Example if you are working with 3 different door-types at the same type, you would want to update the parameters of each of them individually without updating the other door parameters. In that case you will duplicate the code. This becomes difficult to manage when to need to update something. Using pargs simply send the pointer of the door type variable you want to update.
If State machine would have not been involved in DataCAD this process is similar to:
Procedure AnyFunc(var d : door_type);
where you pass the variable and get the modified results back in same variable.

Hope this helps
#11677 by Jsosnowski
Thu May 18, 2006 5:46 pm
Thanks Devinder, part is helpful and part is still not clear.

Part of the problem may be semantics. What do you mean by "State Machine"? I am trying to distinguish when this loop through the main macro function passing all relevant variables and the pargsd values is needed and when it is not. IN DCAL v1 I would use getpoint directly from within the macro and manage the input loop within the main macro only returning control when the user exits the macro. IN this version Datacad controls the loop and places data in pargs ( I assume this is what you refer to as the State machine). Why is this necessary?

I don't expect you to tutor me, but would appreciate if you could steer me to any resources that could explain it better.
#11794 by Jsosnowski
Wed May 24, 2006 2:22 pm
devinder wrote:Check out the aec_modl macro. door_main refers to g_d variable. this variable is passed to various calling functions (see below)
......
Though this variable is a global variable, all of the calling functions refers to g_d indirectly through the use of pargs. This is useful when you have 2 or more same data-types and want to update one of them. Example if you are working with 3 different door-types at the same type, you would want to update the parameters of each of them individually without updating the other door parameters. In that case you will duplicate the code. This becomes difficult to manage when to need to update something. Using pargs simply send the pointer of the door type variable you want to update.
If State machine would have not been involved in DataCAD this process is similar to:
Procedure AnyFunc(var d : door_type);
where you pass the variable and get the modified results back in same variable.

Hope this helps


CLICK! Maybe.
Are you saying that other than that the parameters have been changed to allow management of multiple instances of a macro, methods such as getpoint could have retained their orignal parameter format to pass the variables? If so, I think I was looking too deep for a problem/condition that isn't really there. PLEASE CONFIRM, this is a major piece of the remaining puzzle for me.

This also raises a second question, Will DataCAd allow multiple macros to be open at the same time, now or in an upcoming version?

Who is online

Users browsing this forum: No registered users and 27 guests

About DataCAD Forum

The DataCAD Forum is a FREE online community we provide to enhance your experience with DataCAD.

We hope you'll visit often to get answers, share ideas, and interact with other DataCAD users around the world.

DataCAD

Software for Architects Since 1984