Extending class parameters
Class parameters make it easy to create different formulas with common features, but you can even take this a step further. If you load the MandelbrotTest example in Ultra Fractal, a Browse button is visible next to the name of the class in the list of parameters. This button enables the user to select a different class than the default Bailout class, as long as it descends from Bailout. (See Example 1 - Formula classes for an example.)
Let's write a descendant class that implements more bailout options:
class ExtendedBailout(Test.ufm:Bailout) {
public:
bool func hasBailedOut(const complex z)
return (@test == "mod" && |z| > @bailout) || \
(@test == "real" && sqr(real(z)) > @bailout) || \
(@test == "imag" && sqr(imag(z)) > @bailout)
endfunc
default:
title = "Extended Bailout"
int param test
caption = "Bailout Test"
default = 0
enum = "mod" "real" "imag"
endparam
}
If you put this code in a class library file such as Test.ulb, you can select the new class by clicking on the Browse button next to the Bailout class parameter. The MandelbrotTest formula will now use the bailout code from the ExtendedBailout class. As you can see, this system enables you to add new features to a formula even after it has been published, without having to modify the formula itself!
- The descendant class must be declared in a class library file with the .ulb file extension so the browser can find it. Therefore, the file name of the ancestor needs to be specified as well. The example code above assumes that both MandelbrotTest and the Bailout class are declared in a file called Test.ufm. See also Importing classes.
- The ExtendedBailout class overrides the hasBailedOut method to change its behavior. Note that it introduces a new parameter called test. The bailout parameter is inherited from the Bailout base class.
- If you want the class to appear in the browser when selecting a value for the class parameter, make sure it has a title. Classes without a title are not visible in the browser. (This is useful for abstract base classes, as explained in the next paragraph.)
- In this example, the Bailout base class already has a default behavior and a parameter. In practice, it might be better to have an abstract base class that only declares the methods that form the public interface for the class. Do not include a title for the abstract base class (the browser will only show classes that have a title). In the class parameter declaration for the formula that uses an abstract class, you can then use the default setting to select a descendant of the base class that is declared in a corresponding class library file. In this way, the abstract base class is never used on its own, just as a base class.
- A descendant can also override parameters from its ancestor by including a parameter block for an inherited parameter. This parameter block completely replaces all previous settings for the parameter. With this, you can for example change the default value of a parameter, or hide it by setting visible to false. The type of the parameter must remain the same.
- In case you do not want to provide the option to the user to select a descendant class for a class parameter, set the selectable setting of the class parameter to false.
- The base class must have a constructor (possibly empty), otherwise the constructor of the descendant class will never be called. The compiler will generate a warning if you use a base class without a constructor for a class parameter, and the selectable setting is not false.
- You can determine the currently selected class of a class parameter by comparing the class parameter to the class directly with the == operator or != operator. This allows you to show or enable other parameters depending on the currently selected class. (You can also determine the selected class with a cast, but that requires the creation of an object of the class, something you cannot do in visible or enabled settings.)
Next: Parameter forwards
See Also
Classes
Class parameters
Inheritance
Overriding