Back to Entering Data Structures
Entering Method Code
There are 3 Methods in this application (and 1 Callback Function which we will deal with in a separate section) Generator, Calc and Pixellate.
Generator
Generator is the easiest so we will start here.
Generator is triggered to run by the GUI signalling the semaphore. It should only run once (however, an enhancement would be to 'reset' the application). Each of the NB methods is responsible for initializing 3 data stores Top, Mid and Bottom for its band. Since we have already defined an Initialise() function for the data types we simply need to construct the Records and initialize the data.
Double Click the Method (or select Edit Code->Process... in the Right Click menu)
MyCircuit_GeneratorMthdElemProc.cpp
...
Uns MyCircuit_GeneratorMthdElem::Process()
{
// TODO: Add custom process code
// construct and initialise output
this->Trigger2_StripTopTst1Rec().Construct( 1 );
this->Trigger2_StripTopTst1Rec().Initialise();
this->Trigger2_MidBandTst1Rec().Construct( NUM_ROWS );
this->Trigger2_MidbandTst1Rec().Initialise();
this->Trigger2_StripBottomTst1Rec().Construct( 1 );
this->Trigger2_StripBottomTst1Rec().Initialise();
return TRUE;
}
Pixellate
The Pixellate method is the next simplest method. It simply calculates a pixel color value for each cell. To do this we require a color lookup table, as these are constants they can be held in workspace.
Note the numbering in the access function names, Trigger2 is always the curved face in an Sequential Collector Method and Trigger1 the flat face. However, in a Random Collector Method the distinction between Trigger1 and Trigger2 is less obvious. It is not usually a problem if the faces connect to different object names. If they connect to the same object (ie for read and write) then checking the Consumer Label for one of the connections will reveal its trigger name.
The number in the Tst part of the name is a counter for the number of connections to that object, which is why we connected the circuit in a particular order.
Using the Visual Studio Intellisense feature (invoked by using the this-> ) usually allows the correct names to be identified quickly. If there are any problems compiling this function it will probably be due to the connection numbering to the Pixels TST.
MyCircuit_PixellateMthdElemProc.cpp
The CalcPixels function could be added to a separate utilities library file
void CalcPixels( Char*& OutArray, Float*& InArray, Uns* ColourMap,
const Uns GRID, const Uns COLOUR_PLANES, const Uns LOOKUP_LEVELS )
{
for ( Uns c = 0; c < GRID; ++c )
{
// scale in value cast
Float Value = *InArray * LOOKUP_LEVELS;
// cast to unsigned int
Uns Idx = Value > 0.0f ? (Uns) Value : 0;
if ( Idx >= LOOKUP_LEVELS )
Idx = LOOKUP_LEVELS - 1;
// get color from look up table
memcpy( OutArray, &ColourMap[Idx], COLOUR_PLANES );
// increment pointers
++InArray;
OutArray += COLOUR_PLANES;
}
}
Uns MyCircuit_PixellateMthdElem::Process()
{
// TODO: Add custom process code
// construct and take reference to output
this->Trigger1_PixelsTst2Rec().Construct();
Pixels& pixels = this->Trigger1_PixelsTst2Rec().Data();
// pointer to first location in pixmap - calc functions will increment pointer
Char* outArray = pixels.Values();
// pointer to first location in in data - calc functions will increment pointer
Float* inArray = NULL;
// pointer to in data
Band* band = NULL;
// get colourMap from Workspace
Uns* colourMap = this->Workspace().Data().Colours();
for ( Uns b = 0; b < NB; ++b )
{
// bottom strip
band = this->Trigger2_StripBottomTst2Rec( b ).Struct();
inArray = band->Values();
CalcPixels( outArray, inArray, colourMap, GRID_SIZE, NUM_COLOUR_PLANES, NUM_LOOKUP_LEVELS );
// mid beam - repeat for each row
band = this->Trigger2_MidBandTst2Rec( b ).Struct();
inArray = band->Values();
for ( Uns r = 0; r < NUM_ROWS; ++r )
{
CalcPixels( outArray, inArray, colourMap, GRID_SIZE, NUM_COLOUR_PLANES, NUM_LOOKUP_LEVELS );
}
// top strip
band = this->Trigger2_StripTopTst2Rec( b ).Struct();
inArray = band->Values();
CalcPixels( outArray, inArray, colourMap, GRID_SIZE, NUM_COLOUR_PLANES, NUM_LOOKUP_LEVELS );
}
// update output frame number - use last band
pixels.FrameCount() = band->FrameCount();
return TRUE;
}
Calc
Calc calculates the wave parameters for each cell in each row, using data from its 8 neighbors (and itself) and writes the result to the next frame. Boundary cells need to handle the missing neighbor boundaries.
MyCircuit_CalcMthdElemProc.cpp
Dont worry about the details of F() or Wave() these are just providing a mathematical function to generate a wave dissipation plot. They is neither physically accurate or optimized.
The F and Wave functions could be added to a separate utilities library file
Float F( Float* Top, Float* Mid, Float* Btm )
{
const Uns L = 0;
const Uns M = 1;
const Uns R = 2;
const Float scale = 0.707f; // 1 / sqrt( 2 )
Float sum = scale * Top[L] + Top[M] + scale * Top[R] +
Mid[L] + Mid[R] +
scale * Btm[L] + Btm[M] + scale * Btm[R];
sum *= 0.14644f; // magic number
sum -= Mid[M] * 0.01f;
if ( sum > 1.0f )
sum = 1.0f;
return sum;
}
void Wave( Band* TopOut, Band* MidOut, Band* BtmOut,
Band* BtmPrvIn, Band* TopIn, Band* MidIn, Band* BtmIn, Band* TopNxtIn,
const Uns GRID, const Uns NROWS )
{
Uns r_m1;
Uns r_0;
Uns r_p1;
const Uns c_first = 0;
const Uns c_last = GRID - 1;
// btm strip
r_p1 = 0;
if ( PrvTopIn == NULL )
{
// boundary strip
for ( Uns c = c_first; c <= c_last; ++c )
{
BtmOut[c] = MidIn[r_p1+c] * 0.5f;
}
}
else
{
// boundary cells
BtmOut[c_first] = BtmIn[c_first+1] * 0.5f;
BtmOut[c_last] = BtmIn[c_last-1] * 0.5f;
// rest of row
for ( Uns c = 1; c < c_last; ++c )
{
const Uns c_1 = c-1;
BtmOut[c] = F( &MidIn[r_p1+c_1], &BtmIn[c_1], &PrvTopIn[c_1] );
}
}
// btm of mid band
r_p1 = GRID;
r_0 = 0;
// boundary cells
MidOut[r_0+c_first] = MidIn[r_0+c_first+1] * 0.5f;
MidOut[r_0+c_last] = MidIn[r_0+c_last-1] * 0.5f;
// rest of row
for ( Uns c = 1; c < c_last; ++c )
{
const Uns c_1 = c-1;
MidOut[r_0+c] = F( &MidIn[r_p1+c_1], &MidIn[r_0+c_1], &BtmIn[c_1] );
}
// mid of mid band
r_m1 = 0;
r_0 = GRID;
r_p1 = r_0 + GRID;
for ( Uns r = 1; r < NROWS-1; ++r )
{
// boundary cells
MidOut[r_0+c_first] = MidIn[r_0+c_first+1] * 0.5f;
MidOut[r_0+c_last] = MidIn[r_0+c_last-1] * 0.5f;
// rest of row
for ( Uns c = 1; c < c_last; ++c )
{
const Uns c_1 = c-1;
MidOut[r_0+c] = F( &MidIn[r_p1+c_1], &MidIn[r_0+c_1], &MidIn[r_m1+c_1] );
}
r_m1 = r_0;
r_0 = r_p1;
r_p1 += GRID;
}
// top of mid band
// boundary cells
MidOut[r_0+c_first] = MidIn[r_0+c_first+1] * 0.5f;
MidOut[r_0+c_last] = MidIn[r_0+c_last-1] * 0.5f;
// rest of row
for ( Uns c = 1; c < c_last; ++c )
{
const Uns c_1 = c-1;
MidOut[r_0+c] = F( &TopIn[c_1], &MidIn[r_0+c_1], &MidIn[r_m1+c_1] );
}
// top strip
r_m1 = GRID*(NROWS-1);
if ( NxtBtmIn == NULL )
{
// boundary strip
for ( Uns c = c_first; c <= c_last; ++c )
{
TopOut[c] = MidIn[r_m1+c] * 0.5f;
}
}
else
{
// boundary cells
TopOut[c_first] = TopIn[c_first+1] * 0.5f;
TopOut[c_last] = TopIn[c_last-1] * 0.5f;
// rest of row
for ( Uns c = 1; c < c_last; ++c )
{
const Uns c_1 = c-1;
TopOut[c] = F( &NxtBtmIn[c_1], &TopIn[c_1], &MidIn[r_m1 + c_1] );
}
}
}
...
Uns MyCircuit_CalcMthdElem::Process()
{
// TODO: Add custom process code
// make references to input stores
Float* btmNxtIn = (ElemNum() < NB-1) ? this->Trigger1_StripTopTst2Rec( 1 ).Data().Values : NULL; // NULL if last band
Float* topIn = this->Trigger1_StripTopTst2Rec( 0 ).Data().Values();
Float* midIn = this->Trigger1_MidBandTst2Rec( 0 ).Data().Values();
Float* btmIn = this->Trigger1_StripBottomTst2Rec( 0 ).Data().Values();
Float* topPrvIn = (ElemNum() > 0) ? this->Trigger1_StripBottomTst2Rec( 1 ).Data().Values() : NULL; // NULL if first band
Uns frameNum = 1 + this->Trigger1_MidBandTst2Rec( 0 ).Data().FrameCount();
// construct output data
this->Trigger2_StripTopTst3Rec().Construct( 1 );
this->Trigger2_StripTopTst3Rec().Initialise( frameNum );
this->Trigger2_MidBandTst3Rec().Construct( NUM_ROWS );
this->Trigger2_MidBandTst3Rec().Initialise( frameNum );
this->Trigger2_StripBottomTst3Rec().Construct( 1 );
this->Trigger2_StripBottomTst3Rec().Initialise( frameNum );
// calculate Wave
Wave( this->Trigger2_StripTopTst3Rec().Data().Values(),
this->Trigger2_MidBandTst3Rec().Data().Values(),
this->Trigger2_StripBottomTst3Rec().Data().Values(),
btmNxtIn,
topIn,
midIn,
btmIn,
topPrvIn,
GRID_SIZE,
NUM_ROWS );
// random inject of droplet
unsigned int row = NUM_ROWS * rand() / RAND_MAX;
unsigned int col = GRID_SIZE * rand() / RAND_MAX;
this->Trigger2_MidBandTst3Rec().Data().Values()[row*GRID_SIZE+col] = 1.0f * rand() / RAND_MAX;
return TRUE;
}
Entering Callback Function Code