Ins and outs of atom pointer referencing

All topics on coding 4Dscript in Enterprise Dynamics.
Post Reply
Assil
Posts: 3
Joined: Tuesday 19 April, 2011 - 14:09

Ins and outs of atom pointer referencing

Post by Assil »

Ins and outs of atom pointer referencing
Level: advanced

Sometimes it is desirable to use direct atom referencing instead of referencing via channels. So how can this be achieved?

In order to make pointer referencing work, we first need to understand the nature of pointers. So, what are pointers?
The pointer value is a large integer value, issued automatically by ED to each atom whenever it is created. So, for example, when a user drags a Server-atom into the model, an instance of the Server-atom is created (the new instance is visually represented in the model, while the original Server-atom is still sitting in the library tree). This new instance gets its own unique pointer value. This value is used by ED internally to uniquely identify this new atom. Note, the key word here is “unique”, which is a very nice feature that can come in handy when building models. So let’s try utilizing this ED functionality.

In order to make pointer referencing work, we need to take care of the following things:
1) Each atom needs to have a reference to a related atom
2) Update the pointer values when the related atom is created (think of saving and reopening of a model)

Let’s say that we have an atom named “Worker” which needs to have the following references:
• A reference to its schedule (an atom containing the table with times the worker works)
• A reference to a team that the worker belongs to

Now, let’s add attribute “Schedule” and attribute “BelongsToTeam” and make the following functions:

Worker_GetSchedule(e1)

Code: Select all

vtp(Att([Schedule], p(1)))
Worker_SetSchedule(e1, e2)

Code: Select all

Att([Schedule], p(1)) := p(2)
Worker_GetBelongsToTeam(e1)

Code: Select all

vtp(Att([BelongsToTeam], p(1)))
Worker_SetBelongsToTeam (e1, e2)

Code: Select all

Att([BelongsToTeam], p(1)) := p(2)
Now, let us define a global variable “atmMotherWorker” which will refer to the mother-atom of all worker-atoms. So add the following line to the OnInit event of the worker-atom:

Code: Select all

Dim([atmMotherWorker], vbAtom, c)
Now, make the following method:
Worker_Create(e1, e2)

Code: Select all

Do(
   Var([atmContainer], vbAtom, p(1)), {this is the atom, the worker will physically be placed in}
   Var([atmTeam], vbAtom, p(2)),
   Var([atmNewWorker], vbAtom),
   Var([atmSchedule], vbAtom),
   atmNewWorker := CreateAtom(atmMotherWorker, atmContainer),
   Worker_SetBelongsToTeam(atmNewWorker, atmTeam),
   Team_AddWorker(atmTeam, atmNewWorker),
   { create subatom }
   atmSchedule := CreateAtom(Baseclass, atmNewWorker, [Schedule]),
   Worker_SetSchedule(atmNewWorker, atmShedule)
)
Now, every time a new worker is created, the attributes “BelongsToTeam” and “Schedule” are updated. These attributes hold references to corresponding objects. Note, that we also update the atom atmTeam with the reference to the new worker (this is outside the scope of this article).

Now, whenever we want to view the schedule of the worker or get its team, we merely need to call the appropriate Get-method.
However, if we would try to save the model containing some workers, schedules and teams and would want to reopen it, we would find out that the references within the attributes are no longer valid. This is the case, since ED automatically assigns new pointer values to the atoms upon their creation. There is no way we can override this functionality. However, we can update the attributes “Schedule” and “BelongsToTeam” to match the new pointer values. For this we need to do the following:

1) Adjust the save-model-functionality so that, prior to saving, each atom will saves its pointer value into the “ptrName”-attribute. For this, the following code can be used.

Code: Select all

   ForAtomTreeUnder(
    Model,
    Do(
     Sets,
     if(
      Not(AttributeExists(s, [ptrName])),
      AddAttribute(s, [ptrName])
     ),
     Att([ptrname], s) := String(s)
    )
   )
2) Adjust the open-model-functionality. In words, after the standard loading procedure has finished loading all atoms, call method “UpdatePointerReferences” which is listed here under. The method loops over all atoms in the model, then loads the old pointer values together with the new ones to a temporary table, then loops again through all the atoms to update the references.

UpdatePointerReferences()

Code: Select all

Do(
 var([atmTemporaryLookupTable], vbAtom),
 var([atmTreeAtomBeingScanned], vbAtom),
 var([valNrOfAtomsinModel], vbValue, 0),
 var([valCounter], vbValue, 0), 
 var([valMinimumPointerValue], vbValue, 0),
 var([valMaximumPointerValue], vbValue, 0),
 CreateAtom(Baseclass, atmTemporaryLookupTable, [TemporaryLookupTable]),
 
 {Determine the total number of atoms in the model}
 ForAtomTreeUnder(
  Model,
  Do(
   valNrOfAtomsinModel := valNrOfAtomsinModel + 1
  )
 ),

 {resize the temporary lookup table}
 SetTable(valNrOfAtomsinModel, 2, atmTemporaryLookupTable),
 
 { Set old and new pointer values in the look-up table }
 ForAtomTreeUnder(
  Model,
  Do(
   valCounter := valCounter + 1,
   SetCell(valCounter, 1, a, atmTemporaryLookupTable),
   SetCell(valCounter, 2, att([ptrName], a), atmTemporaryLookupTable)
  )
 ),
 
 { Sort column 2 (ascending) of the look-up table }
QuickSortTable(atmTemporaryLookupTable, 2, 1),

 { Determine minumum and maximum oldpointervalue in column 2 of look-up table }
 valMinimumPointerValue := Cell(1, 2, atmTemporaryLookupTable, 1),
 valMaximumPointerValue := Cell(valNrOfAtomsinModel, 2, atmTemporaryLookupTable, 1),
 
 { Loop over all atoms in Model and replace old pointer values with the new ones }  
 ForAtomTreeUnder(
  Model,
  Do(
   atmTreeAtomBeingScanned:= a,
   var([valNrOfAttributes], vbValue, 0),
   var([valIndexFound], vbValue, 0),
   var([valValueBeingScanned], vbValue, 0),
   
   valNrOfAttributes := NrOfAttributes(atmTreeAtomBeingScanned),
   
   { Loop over attributes of current atom and replace old pointer values with the new ones }
   For(
    valCounter:= 1, valCounter <= valNrOfAttributes,
    Inc(valCounter),
    Do(
     valValueBeingScanned := att(valCounter, atmTreeAtomBeingScanned),
     If(
      And(
       valValueBeingScanned >= valMinimumPointerValue,
       valValueBeingScanned <= valMaximumPointerValue
      ),
      Do(
       { In case old pointer value within range, apply binary search to find index }
       valIndexFound := BinarySearch(atmTemporaryLookupTable, 2, valValueBeingScanned),
       If(
        valIndexFound <> 0,
        Do(
         { Replace old pointer value with new }
         SetAtt(valCounter, Cell(valIndexFound, 1, atmTemporaryLookupTable), atmTreeAtomBeingScanned)
        )
       )
      )
     )
    )
   )
  )
 )
)
Note, the methods “QuickSortTable” and “BinarySearch” are outside the scope of this thread. I have posted these methods in a different thread.

Well, that’s about it. I hope this thread will help you with the concept of pointer references in ED.
Post Reply