bf_con.erl 4.65 KB
Newer Older
1
2
3
4
5
6
7
%%% @doc
%%% Barnsley's Fern Controller
%%%
%%% This process is a in charge of maintaining the program's state.
%%% @end

-module(bf_con).
8
-vsn("0.1.3").
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
-author("Craig Everett <zxq9@zxq9.com>").
-copyright("Craig Everett <zxq9@zxq9.com>").
-license("MIT").

-behavior(gen_server).

%% GUI Interface
-export([iterate/1, reset/0, stop/0]).
%% gen_server
-export([start_link/0]).
-export([init/1, terminate/2, code_change/3,
         handle_call/3, handle_cast/2, handle_info/2]).
-include("$zx_include/zx_logger.hrl").


%%% Type and Record Definitions


-record(s,
        {xy         = {0, 0} :: {number(), number()},
         iterations = 1      :: pos_integer(),
         window     = none   :: none | wx:wx_object()}).

-type state() :: #s{}.



%% GUI Interface

-spec iterate(Count) -> ok
    when Count :: pos_integer().

iterate(Count) ->
    gen_server:cast(?MODULE, {iterate, Count}).


-spec reset() -> ok.

reset() ->
    gen_server:cast(?MODULE, reset).


-spec stop() -> ok.

stop() ->
    gen_server:cast(?MODULE, stop).



%%% Startup Functions


-spec start_link() -> Result
    when Result :: {ok, pid()}
                 | {error, Reason},
         Reason :: {already_started, pid()}
                 | {shutdown, term()}
                 | term().
%% @private
%% Called by bf_sup.

start_link() ->
    gen_server:start_link({local, ?MODULE}, ?MODULE, none, []).


-spec init(none) -> no_return().

init(none) ->
    ok = log(info, "Starting"),
Craig Everett's avatar
Craig Everett committed
78
    Window = bf_gui:start_link("ZOMG! FRACTALS!"),
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
    ok = log(info, "Window: ~p", [Window]),
    State = #s{window = Window},
    {ok, do_reset(State)}.



%%% gen_server Message Handling Callbacks


-spec handle_call(Message, From, State) -> Result
    when Message  :: term(),
         From     :: {pid(), reference()},
         State    :: state(),
         Result   :: {reply, Response, NewState}
                   | {noreply, State},
         Response :: ok
                   | {error, {listening, inet:port_number()}},
         NewState :: state().
%% @private
%% The gen_server:handle_call/3 callback.
%% See: http://erlang.org/doc/man/gen_server.html#Module:handle_call-3

handle_call(Unexpected, From, State) ->
    ok = log(warning, "Unexpected call from ~tp: ~tp~n", [From, Unexpected]),
    {noreply, State}.


-spec handle_cast(Message, State) -> {noreply, NewState}
    when Message  :: term(),
         State    :: state(),
         NewState :: state().
%% @private
%% The gen_server:handle_cast/2 callback.
%% See: http://erlang.org/doc/man/gen_server.html#Module:handle_cast-2

handle_cast({iterate, Count}, State) ->
Craig Everett's avatar
Craig Everett committed
115
    NewState = do_iterate(Count, [], State),
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
    {noreply, NewState};
handle_cast(reset, State) ->
    NewState = do_reset(State),
    {noreply, NewState};
handle_cast(stop, State) ->
    ok = log(info, "Received a 'stop' message."),
    {stop, normal, State};
handle_cast(Unexpected, State) ->
    ok = log(warning, "Unexpected cast: ~tp~n", [Unexpected]),
    {noreply, State}.


-spec handle_info(Message, State) -> {noreply, NewState}
    when Message  :: term(),
         State    :: state(),
         NewState :: state().
%% @private
%% The gen_server:handle_info/2 callback.
%% See: http://erlang.org/doc/man/gen_server.html#Module:handle_info-2

handle_info(Unexpected, State) ->
    ok = log(warning, "Unexpected info: ~tp~n", [Unexpected]),
    {noreply, State}.


%% @private
%% gen_server callback to handle state transformations necessary for hot
%% code updates. This template performs no transformation.

code_change(_, State, _) ->
    {ok, State}.


terminate(Reason, State) ->
    ok = log(info, "Reason: ~tp, State: ~tp", [Reason, State]),
    zx:stop().


%%% Doer functions

Craig Everett's avatar
Craig Everett committed
156
do_iterate(Count, Points, State = #s{xy = XY, iterations = I}) when Count > 0 ->
157
158
159
160
161
162
163
164
165
    R = rand:uniform(),
    NewXY =
        if
            R =< 0.01 -> fern1(XY);
            R =< 0.86 -> fern2(XY);
            R =< 0.93 -> fern3(XY);
            R >  0.93 -> fern4(XY)
        end,
    NewI = I + 1,
Craig Everett's avatar
Craig Everett committed
166
167
168
    do_iterate(Count - 1, [NewXY | Points], State#s{xy = NewXY, iterations = NewI});
do_iterate(0, Points, State = #s{iterations = Iterations}) ->
    ok = bf_gui:show(Points, Iterations),
169
170
171
172
173
174
    State.


do_reset(State) ->
    I = 1,
    XY = fern1({0, 0}),
Craig Everett's avatar
Craig Everett committed
175
    ok = bf_gui:show([XY], I),
176
177
178
    State#s{xy = XY, iterations = I}.


Craig Everett's avatar
Craig Everett committed
179
fern1(XY) ->
180
    fern(XY,  0,     0,     0,     0.16,  0,  0).
181

Craig Everett's avatar
Craig Everett committed
182
fern2(XY) ->
183
    fern(XY,  0.85,  0.04, -0.04,  0.85,  0,  1.6).
184

Craig Everett's avatar
Craig Everett committed
185
fern3(XY) ->
186
    fern(XY,  0.20, -0.26,  0.23,  0.22,  0,  0.16).
Craig Everett's avatar
Craig Everett committed
187
188

fern4(XY) ->
189
    fern(XY, -0.15,  0.28,  0.26,  0.24,  0,  0.44).
190

Craig Everett's avatar
Craig Everett committed
191
192
193
fern({X, Y}, A, B, C, D, E, F) ->
    Next = bf_matrix:multiply([[A, B], [C, D]], [[X], [Y]]),
    [[NewX], [NewY]] = bf_matrix:add(Next, [[E],[F]]),
194
    {NewX, NewY}.