Friday, December 27, 2019

Fractal Friday 2019.12.27

It's the last Fractal Friday of the year, time for some recursive magic!

 This week we're exploring the Mandelbrot set, using two features of the Mathaesthetics prototype software: deep color mapping, and a new helpful zoom feature.

Here's where we start:
In this image, there's a color mapping with 10 different colors at various levels of iteration-result, from 1 to 300. This is both a more diverse color mapping and deeper-iteration delve than I've done before, although you can't tell yet from the image above. Here's a screenshot of the prototype app, showing the higher-end of the color mapping assignments and other settings:



The colors at 1 and 300 are the same (a deep dark blue), and you mostly just see gradations to the orange color set for iteration level 20 above. We have to zoom in to start seeing the other iteration levels, especially those that take over 100 or 200 iterations to hit the breakout threshold of 10.0.

Fortunately I hooked up a new UI feature this week, a click-and-drag zoom feature that lets me zoom to a tiny (but still aspect-ratio-preserving) part of the image.

Series A zooms toward (-1.075885, 0.2433465)




Zoom series B ends up around (-0.766362, 0.1001006)



Zoom series C ends up in the region of (-1.94310614143, 0.001727529425)






More images PLUS a software release to come in 2020! Happy new year. You can follow Mathaesthetics on Twitter, Facebook, and Instagram.

Monday, December 23, 2019

Dev notebook: an enum alternative to GKState / GKStateMachine

I worked on my first GameplayKit project earlier this year and there were things I liked about the lightweight, general state-machine model provided by the GKState and GKStateMachine classes. However I also found a few odd patterns in these classes: their implementation without using enumeration values for states, the inelegant (StateClass).class.self syntax model required by the state machine enterState() method, and the update() interface which I found extra and somewhat orthogonal to the pure function of the state machine (I keep time-based concerns in entities and components).

In my current project, which is not a game, I wanted to use a pattern similar to GKStateMachine for various UI and model states, but decided to implement a simple framework that addressed these issues rather than use GKStateMachine.

The basic pattern is provided by a protocol that *could* be implemented by any class, but the interface strongly suggests that a state will be an enum : Int type:


protocol MPState {
    var rawEnumValue : Int { get }
    var validNextStates : [Int] { get }
    
    func didEnter(from prevState: MPState?, with machine: MPStateMachine)
    func willExit(to nextState: MPState?, with machine: MPStateMachine)
}


Obviously the rawEnumValue will be very easy to implement as a wrapper on rawValue if the state is implemented as an enum : Int rather than some other type of struct or class. Also, the required validNextStates computed property is expected to return values that can be compared with rawEnumValue for inclusion/exclusion, and this logic is made explicit in an extension to the protocol (which also implements didEnter and willExit as no-ops to make them optional):


extension MPState {
    func isValidNextState(_ nextState : MPState
                          with machine: MPStateMachine) -> Bool {

        return self.validNextStates.contains(nextState.rawEnumValue)
    }
    
    func didEnter(from prevState: MPState?, with machine: MPStateMachine) {
        // no-op, override to handle
    }
    
    func willExit(to nextState: MPState?, with machine: MPStateMachine) {
        // no-op, override to handle
    }
}


One thing this pattern precludes: the state object itself can't retain any persistent reference to either the state machine or any model object, since enumerations in swift can't have stored properties. This is handled by simply passing the machine object into the three relevant methods; the machine is a full-fledged class which also has an optional model property that can bring in more object context.


The state-machine class provides an interface like that of GKStateMachine, but instead of passing a class to the enter() method you can just pass your enumerated state value. It has a similar currentState read-only computed property (returning a private machineState value which can only be updated by successful enter). Here's the entire implementation:

class MPStateMachine : NSObject {
    var model: Any? = nil
    
    var currentState : MPState? {
        get {
            return self.machineState
        }
    }
    
    func canEnterState(_ nextState : MPState) -> Bool {
        if nil != self.machineState {
            return self.machineState!.isValidNextState(nextState, with: self)
        }
        return true // assume we can enter any state from nil
    }
    
    func enter(_ state: MPState) -> Bool {
        var setState : Bool = false
        if self.machineState == nil {
            self.machineState = state
            state.didEnter(from: nil, with: self)
            setState = true
        } else if self.machineState!.isValidNextState(state, with: self) {
            let oldState = self.machineState!
            self.machineState?.willExit(to: state, with: self)
            self.machineState = state
            state.didEnter(from: oldState, with: self)
            setState = true
        }
        return setState
    }
    
    fileprivate var machineState : MPState? = nil

}

The model property in the state machine is up to the client code to define; it will typically be the model object whose state is being managed, i.e. the domain-specific owner of the state machine instance.

As a sample implementation I created an MPApplicationState which tracks the app states of starting, running, terminating, owned by my AppDelegate class. This demonstrates the pattern of how valid next-states can be handled by a simple switch statement for most state collections, but you could always add helper methods for various state-transition contexts for more complex cases that need to inspect the model:

enum MPApplicationState : Int, MPState {

    case starting
    case running
    case terminating
    
    var rawEnumValue: Int {
        get {
            return self.rawValue
        }
    }
    
    var validNextStates: [Int] {
        get {
            var nextStates : [Int] = []
            switch self {
                case .starting:
                    nextStates.append(MPApplicationState.running.rawValue)
                    nextStates.append(MPApplicationState.terminating.rawValue)
                case .running:
                    nextStates.append(MPApplicationState.terminating.rawValue)
                case .terminating:
                    nextStates.removeAll()
            }
            return nextStates
        }
    }
    
    func didEnter(from prevState: MPState?, with machine: MPStateMachine) {
        print ("*** MPApplicationState *** did enter state: \(String(describing:self)) from \(String(describing:prevState))")
    }
    
    func willExit(to nextState: MPState, with machine: MPStateMachine) {
        print ("*** MPApplicationState *** will exit state: \(String(describing:self)) to state: \(String(describing:nextState))")
    }

}

Just to show the functionality (not really because it's useful) I created an MPStateMachine property on my AppDelegate (allocated at the declaration), and transition states in the following method overrides:

func applicationWillFinishLaunching(_ notification: Notification) {
    _ = self.stateMachine.enter(MPApplicationState.starting)

}


func applicationDidFinishLaunching(_ aNotification: Notification) {
    _ = self.stateMachine.enter(MPApplicationState.running)
    // etc...
}

func applicationWillTerminate(_ aNotification: Notification) {

    _ = self.stateMachine.enter(MPApplicationState.terminating)
}

I'm not bothering to check the output values for these simple cases but the idea is that more involved state transitions could do so, and/or pre-check the ability of the state machine to transition with a call a like:


   self.stateMachine.canEnterState(MPApplicationState.running)

And now checking the current application state looks a bit nicer than it would with GKStateMachine:

    if appDelegate.stateMachine.currentState != MPApplicationState.terminating {

        // do something...

That's it!

Friday, December 20, 2019

Fractal Friday 2019.12.20

The fractal renderer used to draw this week's graphics has a new feature - rendering in 'edge' mode. Rather than filling the value regions with different colors, edge-mode detects the change in output values and draws a line at the boundary between them. Here's the classic Mandelbrot set rendered this way:

Here's a simple black-and-white rendering of a Julia set region:

Edge rendering of fractals emphasizes different structures within the shapes, and also produces different aesthetic effects, especially when different pen and background colors are used:




Coming soon: images created with the new geometry renderer! The Mathaesthetics flagship app that produces all these images will be released in 2020. You can follow Mathaesthetics on Twitter, Facebook, and Instagram.

Tuesday, December 17, 2019

Scalar fields for gradients and other graphic patterns

The Mathaesthetics flagship app will support a number of modes of mathematical image creation. I spent most of yesterday working on and improving the scalar field renderer.

In a scalar field, a function f(x, y) produces a value for each point. This scalar value is then mapped to a color gradient. I'm exploring both different modes of color assignment and different function definitions and the results they yield.

Here's a simple example, of the function f(x, y) = x • y. The color scale is white-to-black, and here I'm only allowing 16 different distinct shades:

Here's the same function with the color mapping changed to orange-to-blue:

And again, but with the discrete shading limit removed so we get a smooth gradient:

Producing gradients that aren't simply linear or radial is one potential graphic application of scalar fields with algebraic functions. For example, a logarithmic gradient...

Friday, December 13, 2019

Fractal Friday 2019.12.13

It's Friday already! This week I've been jamming on this application UI development, including a brand-new feature to support fractals: the color mapping control:
For fractal rendering the integers below each color correspond to the output value (escape iterations) to which the color is assigned. The linear-gradient mapping mode means that colors between these values will be interpolated evenly between them. Here's the image that is using these control settings, with the dark-red background color of the canvas showing through for values above 30:


There are non-gradient coloration modes supported by the control. A color cycle is easy to set up with this control - all colors are used, and they just repeat for each successive band of output. This one's a little different in that some of the colors are assigned to be transparent, letting the deep blue background color show through at those levels:




Thursday, December 12, 2019

Dev notebook: Converting scale & position of circular NSSlider for degrees

The standard Cocoa control NSSlider comes in a circular variety that resembles a rotating knob with an indicator point. Among the ideal applications for such a control is representing a circular angle.



In the application I'm developing I wanted to use this to control the angle of the brush image used for drawing. I began by setting the range of the control from 100 and using the value as a percentage (a pretty common approach for sliders), converting to the angle (which I'm using in radians in my drawing code)

Functionally it was okay, but there were some user-feedback issues. For one, the circular slider by default increases in the clockwise direction, but in my mathematics-centered application I felt it made much more sense for angle to be treated as it is on the unit circle, increasing in the counter-clockwise direction. (Since my drawing code was written this way, it also meant that the position of the knob did not visually follow the rotation of the brush image). A related issue: the unit circle typically puts the angle zero at "3 o'clock", whereas the NSSlider defaults to position 0 at noon.

One other usability issue involved precise setting, which is somewhat a limitation of the size of the control. A text field displaying the control setting could help the user a bit; however displaying values in radians would require a lot of decimal spaces and perhaps not be as intuitive, so I decided at this point to convert the control to work with degrees, and display this value in a text field.

To sum up, the needs are: 1) make the control increase counter-clockwise instead of clockwise, 2) make the control set 0 to the 3 o'clock knob position instead of noon; 3) map the range from 0 to 360 degrees and display that value in a text field.

Tuesday, December 10, 2019

Painting with Trigonometry

I'm excited to share today's image generated from the application under development:


It's a pretty organic background-pattern sort of texture, but it highlights a few cool things about the app, which is focused on creating beautiful images with mathematical techniques.

Friday, December 6, 2019

Fractal Friday 2019.12.06

The Mathaesthetics software has made some cool advances this week, and we're celebrating with our first Fractal Friday video!


These images depict a Julia set of degree 3 (function z^3 + C), with the C value 0.4+0.7i, 50 iterations maximum, and the escape value slowly changing with each image. The first image has escape value 0.8:

Thursday, December 5, 2019

Flavors of static: random distributions and noise

It's day one of noise generation in our flagship product! There are many ways to generate visual noise; our first algorithm is a simple static pixel generator. For these examples I'm generating a random integer from 0 to 255 and using that value to calculate a color on the gradient between two colors (here, blue and orange). Here's the initial output:
It's easiest to compare other output zoomed in a bit - here's a section zoomed at 1200%:
I'm interested in tweaking the actual noise generation, which here means changing the random value generation (not just applying visual adjustments to the output). Filtering the random unit through a log() function creates a curve that produces more values closer to the maximum and few values closer to the minimum - which interpreted as color, means we see more orange:
The complementary approach uses an exponential function to produce a distribution that increases more slowly at first - mapping more values to the lower end of the scale, here meaning more blue: