Refactor globals into classes: ============================== - Identify related globals and group them together. - Add related globals to a state class as private members with m_ names instead of g_ names. - Create a single instance of the global state class with a g_ name - Add getter/setter methods - Replace references to globals with references to getter/setter methods - Look for uses of the setter method for candidates to move to the class - Look for functions that primarily use getter/setter methods on the state class; these are candidates for migration to the state class. Split user-interface from calculation: ====================================== - Identify state and functions used only for user-interface code. - Separate user-interface code and calculation code as much as possible - Create two objects for these responsibilities and have them collaborate - With a separate calculation object from the UI object, there is the possibility of packaging the calculation object in a library that has no UI dependencies. Split modules implementing multiple responsibilities: ===================================================== - Eventually we want a group of collaborating classes, with each class having a single well-defined responsibility. - What we have is a large pile of global functions implementing pieces of responsibilities split among files. - First group related functions and their global and static data together into a single module. Add glue functions as necessary to allow other functions to have access to state encapsulated by the module's static data. - If a module (file) implements more than one responsibility, split individual responsibilities into their own module (file). - Converting a module with well-defined responsibilities into an object is much easier than trying to extract multiple objects from a single module. Convert polling I/O to event-driven I/O: ======================================== - Identify input check points, generally one of two kinds: - Interrupt calculation - User input - For calculation interruption, there is generally a check of the keyboard and then some flag is set or the computation loop is exited. Defer these sorts of input situations for later. Ultimately we want to replace these with a check against a "should continue" flag so that the computation can be placed on a thread separate from the UI. - For user input, there is generally an input loop local to the routine that consists of something like: while (more input) { switch (input key) { case FIK_key: handle_key(); } } This is almost an event dispatch loop except that the "event" pump is local to each routine. Refactor this into a form like that in the intro screen by implementing a class that derives from AbstractDialog. The class represents the current input context by implementing two virtual methods: one that processes input and one that is called when there is no input (idle processing). Once all the user input has been transformed into classes derived from AbstractDialog, there exists the possibility of implementing a single event processing loop for all dialogs that dispatches to the appropriate AbstractDialog as key events are received from the windowing system event loop. The main difficulty lies in transforming the main loop of the application. Here the user-interface code is called in a nested fashion from the calculation code. The event loop needs to be moved all the way to the top-level main application function and call into the calculation code at the appropriate time. This is probably the most elaborate refactoring and will need to be performed incrementally over some time. Enhance single document model to multiple document model: ========================================================= - Once all the code has been refactored into a set of collaborating objects, it will be possible to relax the constraint of having a single set of global objects and instantiate multiple collections of objects, each collection representing the rendering state of a single "document". - The model-view-controller pattern can be used to implement multiple fractal renderings happening concurrently. - Introducing a multiple-document model will be nearly impossible until all the global state is sorted out into objects, so don't try until then. Replace homebrew complex type with std::complex: =================================================== - std::complex implements the basic functions we care about - Boost implements more functions on std::complex that we can leverage - Any missing functions we need can be implemented for std::complex using our procedural code Replace use of long integers with FixedPoint class: =================================================== - Encapsulate the concept of a fixed-point integer into a class; this will hide all the use of g_fudge, shifting/multiplying/dividing by the scale factor into a single location. - Implement natural arithmetic operators and conversions for the FixedPoint class - Recast existing integer fractal code into using FixedPoint class - Strive to get the same expression for floating-point code as for fixed-point code. If this can be obtained, then you can parameterize the whole mess by data type and create a fractal type that can be instantiated for FixedPoint, float, bignum, etc., as long as the underlying numerical type supports all the operators and functions used. Replace homebrew bignum/bigflt with GNU GMP library and other layers: ===================================================================== - The current bignum library is limited in the way it allocates arbitrary precision objects from an internal fixed-size stack. - Eventually replace the bignum code with the GNU GMP library and the C++ layers built on top of it. Get a bigflt and use std::complex to implement complex bignums. == Replace sound output with cross-platform sound library == Refactor g_current_fractal_specific and g_fractal_specific to be const, read-only == Replace magic leading character in parameter prompts with a set of flags for each prompt: '#' implies %-12lu formatting (unsigned integer), '+' implies %-12d formatting (signed integer)