System Overview - PathogenDavid/Periodic GitHub Wiki
The main app architecture of the Periodic project can be distilled into four parts:
- Sifteo Cube Management
- Element Cube
- Element Core
- Reaction Processing
Architecture
Sifteo Cube Management (main.cpp - PeriodicCore above)
This portion of the app is responsible for listening to Sifteo SDK events and dispatching them to the reaction processing system. It also wrangles the ElementCubes and triggers their rendering.
Its most important role is that it figures out what elements are involved in a reaction, and creates a Reaction instance for processing.
Element Cube (ElementCube)
An ElementCube wraps an instance of Element primarily to provide rendering facilities for that Element and to associate it with a specific Cube. It also provides some utility methods for managing the state of the element on the cube.
Element Core (Element)
An Element is primarily responsible for handling the database of available elements on the periodic table. It is also responsible for maintaining Bond information with other Elements and mutating its own state to a specific Reaction, Compound pair. Additionally, Element contains many utility functions for getting information about Elements it has bonds with.
Reaction Processing (Reaction, Element, Compound, Bond, BondSolution)
By far the most complex portion of the Periodic system is the reaction processing.
Following is a short list of important associations in the reaction processing system.
- Every
Elementis involved in exactly oneReactionat most, allElements in aReactionare connected (perhaps indirectly) by aBond. - Each
Elementhas up to fourBonds with otherElements. EachBondmay have manyBondSolutions associated between it and aCompound. - A
Compoundis a potential result of aReaction. EachReactionmay contain manyCompounds. TheReaction.Processmethod determines whichCompoundwill be displayed to the user, thisCompoundwill become thecurrentCompoundfor eachElementit is associated with and thatCompoundis what will be used to mutate theElements inElement.ApplyCompound.
Flow of events
Follows is a rough overview of the flow of control, starting in PeriodicCore.ProcessNeighborhood:
- A
Reactionis created with a singleElementin it, representing the rootElement. - All neighbors of that
Elementare added to theReactionviaElement.AddBond. - Perform 2 recursively for each of those newly added neighbor
Elements until no more new cubes can be added. - Call
Reaction.Processto process the reaction. - Process the
Elements in thisReactionto see if they might form aCompound. If we find a (potential)Compound, we create a new one and set all the relevant bond information for thatCompoundin eachElementinvolved in theCompound. - Determine the ideal
Compoundto be displayed to the user. (Right now, this algorithm is "Prefer theCompoundwith the most elements that doesn't contain potential bonds.") - Apply that
Compoundto relevantElements viaCompound.Apply, which invokesElement.ApplyCompound.
Details of potential reaction processing
Please refer to the reaction possibility engine page for details on how it works.
General Notes
Static Allocation
Since by default, the Sifteo SDK doesn't have any facilities for dynamic memory allocation, everything needed by the app is allocated statically at compile time. Therefore, reactions manipulate the existing instance of Element rather than creating a new Element with the relevant attributes.
Because of this, we differentiate elements between raw base elements (which are in the element database) and mutated elements (which are the ones used in reactions.) All mutated elements have an associated raw base element. A mutated element can be reset to default using Element::ResetToBasicState or by changing it to a new raw element with one of the Element::GetRawElement methods.
As of v0.2s2, some basic dyanmic memory allocation via the ObjectPool class is available. The object pool is detailed in the following section.
If you do attempt to allocate a non-ObjectPool class dynamically (with new), you will get the following cryptic error:EXEC : -!- Slinky error : Taking address of undefined symbol '_Znwm'
Dynamic Allocation via ObjectPool
As of v0.2s2, some basic dynamic memory allocation facilities are available via the ObjectPool class. This class allows using new and delete to allocate objects from a statically-sized pool. The objects are still allocated statically by the compiler, but it is easier to instantiate and destroy the objects at runtime, which can make code easier to read.
To enable a type to use the object pool, simply include ObjectPool.h and publicly inherit it from the ObjectPool type. You will need to specify your type and the desired size of the object pool.
For example, the following will allow allocating up to 10 instances of SomeObject at runtime.
class SomeObject : public ObjectPool<SomeObject, 10>
{ /* ... */ }
Note: The ObjectPool has not been tested with child types, and may behave incorrectly if you attempt to do so.
Standard C Library
Because the Sifteo SDK does not include the standard C library, we don't use any of it. Any standard functions you see, such as strlen or strcmp are actually implemented within periodic.cpp.
Therefore: You should be wary of developing / testing outside of the Sifteo environment as you may develop code that won't compile.
