libjmmcg  release_579_6_g8cffd
A C++ library containing an eclectic mix of useful, advanced components.
mandelbrot.cpp
Go to the documentation of this file.
1 /******************************************************************************
2 ** Copyright © 2002 by J.M.McGuiness, coder@hussar.me.uk
3 **
4 ** This library is free software; you can redistribute it and/or
5 ** modify it under the terms of the GNU Lesser General Public
6 ** License as published by the Free Software Foundation; either
7 ** version 2.1 of the License, or (at your option) any later version.
8 **
9 ** This library is distributed in the hope that it will be useful,
10 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
11 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 ** Lesser General Public License for more details.
13 **
14 ** You should have received a copy of the GNU Lesser General Public
15 ** License along with this library; if not, write to the Free Software
16 ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18 
19 #include "stdafx.h"
20 
21 #define BOOST_TEST_MODULE libjmmcg_tests
22 #include <boost/test/included/unit_test.hpp>
23 
24 #include "core/thread_pool_workers.hpp"
25 
26 #include <cassert>
27 #include <stdexcept>
28 
29 #include <boost/mpl/assert.hpp>
30 #include <boost/static_assert.hpp>
31 #include <boost/type_traits.hpp>
32 #include <boost/scoped_ptr.hpp>
33 #include <boost/shared_ptr.hpp>
34 #include <boost/tr1/array.hpp>
35 
36 #include <algorithm>
37 #include <complex>
38 #include <iostream>
39 #include <iterator>
40 #include <limits>
41 #include <queue>
42 #include <vector>
43 
44 using namespace libjmmcg;
45 
46 struct point_type {
48 
50 
51  constexpr point_type() noexcept(true)
52  : x(), y() {
53  }
54  constexpr point_type(element_type xi, element_type yi) noexcept(true)
55  : x(xi), y(yi) {
56  }
57  bool operator==(point_type const &pt) const noexcept(true) {
58  return x==pt.x && y==pt.y;
59  }
60 };
61 
62 template<class V, unsigned int W, unsigned int H, V Init>
63 struct bitmap {
64  enum {
65  width=W,
66  height=H,
67  initial_value=Init
68  };
69  struct element_type {
70  V value;
71  bool computed;
72 
73  constexpr element_type() noexcept(true)
74  : value(Init), computed(false) {
75  }
76 
77  friend std::ostream &__fastcall operator<<(std::ostream &os, element_type const &e) noexcept(false) {
78  os<<std::hex<<e.value<<std::dec;
79  return os;
80  }
81  };
82  typedef typename std::tr1::array<element_type, height> column_type;
83  /// The bitmaps is in the usual (x,y) cartesian co-ordinate format.
84  typedef typename std::tr1::array<column_type, width> container_type;
86  typedef typename column_type::iterator iterator;
90 
91  constexpr neighbourhood_item() noexcept(true)
92  : pt(), element() {
93  }
94  explicit constexpr neighbourhood_item(iterator const &i) noexcept(true)
95  : pt(), element(i) {
96  }
97  constexpr neighbourhood_item(point_type const &p, iterator const &i) noexcept(true)
98  : pt(p), element(i) {
99  }
100  };
103 
104  struct lower_iterations : std::binary_function<neighbourhood_item, neighbourhood_item, bool> {
105  typedef std::binary_function<neighbourhood_item, neighbourhood_item, bool> operation_type;
109 
110  result_type __fastcall operator()(first_argument_type const &f, second_argument_type const &s) const noexcept(true) {
111  return f.element->value>s.element->value;
112  }
113  };
114 
116 
117  const_iterator __fastcall find(point_type const &pt) const noexcept(true) {
118  return ((screen.begin()+pt.x)->begin()+pt.y);
119  }
120  iterator __fastcall find(point_type const &pt) noexcept(true) {
121  return ((screen.begin()+pt.x)->begin()+pt.y);
122  }
123 
124  const_iterator __fastcall bottom_left() const noexcept(true) {
125  return screen.begin()->begin();
126  }
127 
128  const_iterator __fastcall top_right() const noexcept(true) {
129  return ((screen.end()-1)->end()-1);
130  }
131 
132  bool __fastcall inside(point_type const &pt) const noexcept(true) {
133  return pt.x<width && pt.y<height;
134  }
135 
136  const_neighbourhood_type neighbours(point_type const &p) const noexcept(true) {
137  const const_neighbourhood_type ret={
138  (inside(point_type(p.x, p.y+1)) ? typename const_neighbourhood_type::value_type(point_type(p.x, p.y+1), find(point_type(p.x, p.y+1))) : typename const_neighbourhood_type::value_type(screen.begin()->end())), // North.
139  (inside(point_type(p.x+1, p.y+1)) ? typename const_neighbourhood_type::value_type(point_type(p.x+1, p.y+1), find(point_type(p.x+1, p.y+1))) : typename const_neighbourhood_type::value_type(screen.begin()->end())), // NE.
140  (inside(point_type(p.x+1, p.y)) ? typename const_neighbourhood_type::value_type(point_type(p.x+1, p.y), find(point_type(p.x+1, p.y))) : typename const_neighbourhood_type::value_type(screen.begin()->end())), // East.
141  (inside(point_type(p.x+1, p.y-1)) ? typename const_neighbourhood_type::value_type(point_type(p.x+1, p.y-1), find(point_type(p.x+1, p.y-1))) : typename const_neighbourhood_type::value_type(screen.begin()->end())), // SE.
142  (inside(point_type(p.x, p.y-1)) ? typename const_neighbourhood_type::value_type(point_type(p.x, p.y-1), find(point_type(p.x, p.y-1))) : typename const_neighbourhood_type::value_type(screen.begin()->end())), // South.
143  (inside(point_type(p.x-1, p.y-1)) ? typename const_neighbourhood_type::value_type(point_type(p.x-1, p.y-1), find(point_type(p.x-1, p.y-1))) : typename const_neighbourhood_type::value_type(screen.begin()->end())), // SW.
144  (inside(point_type(p.x-1, p.y)) ? typename const_neighbourhood_type::value_type(point_type(p.x-1, p.y), find(point_type(p.x-1, p.y))) : typename const_neighbourhood_type::value_type(screen.begin()->end())), // West.
145  (inside(point_type(p.x-1, p.y+1)) ? typename const_neighbourhood_type::value_type(point_type(p.x-1, p.y+1), find(point_type(p.x-1, p.y+1))) : typename const_neighbourhood_type::value_type(screen.begin()->end())) // NW.
146  };
147  return ret;
148  }
149  neighbourhood_type neighbours(point_type const &p) noexcept(true) {
150  const neighbourhood_type ret={{
151  (inside(point_type(p.x, p.y+1)) ? typename neighbourhood_type::value_type(point_type(p.x, p.y+1), find(point_type(p.x, p.y+1))) : typename neighbourhood_type::value_type(screen.begin()->end())), // North.
152  (inside(point_type(p.x+1, p.y+1)) ? typename neighbourhood_type::value_type(point_type(p.x+1, p.y+1), find(point_type(p.x+1, p.y+1))) : typename neighbourhood_type::value_type(screen.begin()->end())), // NE.
153  (inside(point_type(p.x+1, p.y)) ? typename neighbourhood_type::value_type(point_type(p.x+1, p.y), find(point_type(p.x+1, p.y))) : typename neighbourhood_type::value_type(screen.begin()->end())), // East.
154  (inside(point_type(p.x+1, p.y-1)) ? typename neighbourhood_type::value_type(point_type(p.x+1, p.y-1), find(point_type(p.x+1, p.y-1))) : typename neighbourhood_type::value_type(screen.begin()->end())), // SE.
155  (inside(point_type(p.x, p.y-1)) ? typename neighbourhood_type::value_type(point_type(p.x, p.y-1), find(point_type(p.x, p.y-1))) : typename neighbourhood_type::value_type(screen.begin()->end())), // South.
156  (inside(point_type(p.x-1, p.y-1)) ? typename neighbourhood_type::value_type(point_type(p.x-1, p.y-1), find(point_type(p.x-1, p.y-1))) : typename neighbourhood_type::value_type(screen.begin()->end())), // SW.
157  (inside(point_type(p.x-1, p.y)) ? typename neighbourhood_type::value_type(point_type(p.x-1, p.y), find(point_type(p.x-1, p.y))) : typename neighbourhood_type::value_type(screen.begin()->end())), // West.
158  (inside(point_type(p.x-1, p.y+1)) ? typename neighbourhood_type::value_type(point_type(p.x-1, p.y+1), find(point_type(p.x-1, p.y+1))) : typename neighbourhood_type::value_type(screen.begin()->end())) // NW.
159  }};
160  return ret;
161  }
162 
163  friend std::ostream &__fastcall operator<<(std::ostream &os, bitmap const &b) noexcept(false) {
164  os<<"Width="<<b.width
165  <<", height="<<b.height
166  <<", initial value="<<static_cast<V>(b.initial_value)
167  <<std::endl;
168  for (typename container_type::size_type y=0; y<b.height; ++y) {
169  for (typename container_type::size_type x=0; x<b.width; ++x) {
170  os<<b.screen[x][y];
171  }
172  os<<"\n";
173  }
174  return os;
175  }
176 };
177 
178 template<class T>
180  typedef std::complex<T> element_type;
181 
183 
184  __stdcall complex_plane_t(element_type const &bl, element_type const &tr) noexcept(true)
185  : bottom_left(bl), top_right(tr) {
186  assert(width()>0);
187  assert(height()>0);
188  }
189 
190  typename element_type::value_type __fastcall width() const noexcept(true) {
191  return top_right.real()-bottom_left.real();
192  }
193 
194  typename element_type::value_type __fastcall height() const noexcept(true) {
195  return top_right.imag()-bottom_left.imag();
196  }
197 
198  friend std::ostream &__fastcall operator<<(std::ostream &os, complex_plane_t const &cp) noexcept(false) {
199  os<<cp.bottom_left<<cp.top_right;
200  return os;
201  }
202 };
203 
204 template<class Op, class WkQ>
206 public:
207  typedef Op operation_type;
210  typedef WkQ work_queue_type;
211 
212 private:
213  screen_type &screen;
214  work_queue_type &work_queue;
215  operation_type const fn;
216  result_type pt;
217 
218 public:
219  explicit __stdcall greedy_render_t(screen_type &scr, work_queue_type &wk, Op const &op, result_type const &p) noexcept(true)
220  : screen(scr), work_queue(wk), fn(op), pt(p) {
221  assert(pt.element);
222  }
223 
224  void __fastcall start() noexcept(true) {
225  process(pt);
226  }
227 
228  void __fastcall process(result_type &pt_to_compute) noexcept(noexcept(fn.operator()(pt_to_compute.pt))) {
229  pt_to_compute=pt;
230  assert(pt_to_compute.element);
231  if (!pt_to_compute.element->computed) {
232  pt_to_compute.element->value=fn.operator()(pt_to_compute.pt);
233  pt_to_compute.element->computed=true;
234  add_neighbours(pt_to_compute);
235  }
236  }
237 
238  bool __fastcall operator<(greedy_render_t const &gr) const noexcept(true) {
239  return typename screen_type::lower_iterations().operator()(pt, gr.pt);
240  }
241 
242  friend std::ostream &__fastcall operator<<(std::ostream &os, greedy_render_t const &cp) noexcept(false) {
243  os<<cp.fn;
244  return os;
245  }
246 
247 private:
248  /**
249  TODO JMG: this doesn't work as it recurses, adding new items to the queue, yet waiting for each item to be computed. The big clue this is balmy is that it's using a for_each (which has side-effects), with a wait in the functor. The side effect is the setting of all iteration values into the bitmap, when finished, the render is completed. This is not reflected in this function.
250  */
251  void __fastcall add_neighbours(result_type const &cur_pt) noexcept(true) {
252  const typename screen_type::neighbourhood_type neighbours(screen.neighbours(cur_pt.pt));
253  std::for_each(
254  neighbours.begin(),
255  neighbours.end(),
256  [&screen, &work_queue, &fn, &cur_pt](typename screen_type::neighbourhood_type::const_iterator i) {
257  assert(i->element);
258  if (i->element!=screen.screen.begin()->end() && !i->element->computed && i->element->value==screen.initial_value) {
259  auto const &computed_pt=work_queue<<typename work_queue_type::joinable()<<greedy_render_t<Op, WkQ>(screen, work_queue, fn, *i);
260  *computed_pt;
261  }
262  }
263  }
264  }
265 };
266 
267 template<class CP, class Scr>
268 struct Mandelbrot : public std::unary_function<point_type, unsigned long> {
269  typedef CP complex_plane_type;
270  typedef Scr screen_type;
275 
278 
279  __stdcall Mandelbrot(complex_plane_type &cp, screen_type &sc, iterations_type mi, bailout_type b=2) noexcept(true)
280  : max_iters(mi), bailout_sqrd(b*b), plane(cp), d_x(plane.width()/sc.width), d_y(plane.height()/sc.height) {
281  }
282 
283  iterations_type __fastcall operator()(argument_type const &p) const noexcept(true) {
284  iterations_type iter=0;
285  const typename complex_plane_type::element_type c=pt_to_plane(p);
286  typename complex_plane_type::element_type z(0);
287  while (++iter<max_iters && std::norm(z)<bailout_sqrd) {
288  z=sqr(z)+c;
289  };
290  return iter;
291  }
292 
293  friend std::ostream &__fastcall operator<<(std::ostream &os, Mandelbrot const &m) noexcept(false) {
294  os<<"Max. iterations="<<m.max_iters
295  <<", bailout="<<std::sqrt(m.bailout_sqrd)
296  <<", step=("<<m.d_x
297  <<", "<<m.d_y<<")";
298  return os;
299  }
300 
301 private:
302  complex_plane_type &plane;
303  const typename complex_plane_type::element_type::value_type d_x;
304  const typename complex_plane_type::element_type::value_type d_y;
305 
306  typename complex_plane_type::element_type __fastcall pt_to_plane(argument_type const &p) const noexcept(true) {
307  return typename complex_plane_type::element_type(
308  plane.bottom_left.real()+d_x*p.x,
309  plane.bottom_left.imag()+d_y*p.y
310  );
311  };
312 
313  static typename complex_plane_type::element_type __fastcall sqr(typename complex_plane_type::element_type const &c) noexcept(true) {
314  return typename complex_plane_type::element_type(c.real()*c.real()-c.imag()*c.imag(), 2*c.real()*c.imag());
315  }
316 };
317 
318 BOOST_AUTO_TEST_SUITE(fractals)
319 
320 BOOST_AUTO_TEST_CASE(mandelbrot)
321 {
322  typedef bitmap<unsigned long, 170, 170, -1> screen_t;
323  typedef complex_plane_t<long double> plane_t;
324  typedef Mandelbrot<plane_t, screen_t> Mandelbrot_t;
325  typedef std::priority_queue<screen_t::lower_iterations::first_argument_type, std::vector<screen_t::lower_iterations::first_argument_type>, screen_t::lower_iterations> work_queue_type;
326 
327  typedef ppd::thread_pool<
328  ppd::pool_traits::work_distribution_mode_t::worker_threads_get_work<pool_traits::work_distribution_mode_t::queue_model_t::pool_owns_queue>,
329  ppd::pool_traits::size_mode_t::fixed_size,
330  ppd::pool_aspects<
331  ppd::generic_traits::return_data::joinable,
332  ppd::platform_api,
333  ppd::heavyweight_threading,
334  ppd::pool_traits::prioritised_queue,
335  std::less,
336  1,
337  ppd::basic_statistics/*,
338  ppd::control_flow_graph*/
339  >
340  > pool_type;
341  typedef greedy_render_t<Mandelbrot_t, pool_type> renderer_t;
342 
343  pool_type pool(2);
344  plane_t complex_plane(plane_t::element_type(-2.0, -1.5), plane_t::element_type(0.9, 1.5));
345  std::cout<<complex_plane<<std::endl;
346  screen_t screen;
347  const point_type start(0, 0);
348  const screen_t::lower_iterations::first_argument_type pt(start, screen.find(start));
349  renderer_t renderer(screen, pool, Mandelbrot_t(complex_plane, screen, 15), pt);
350  std::cout<<renderer<<std::endl;
351  renderer.start();
352  std::cout<<screen<<std::endl;
353 }
354 
355 BOOST_AUTO_TEST_SUITE_END()