1 /*
2  * Copyright (C) 2008 Emweb bv, Herent, Belgium.
3  *
4  * See the LICENSE file for terms of use.
5  */
6 
7 #include <algorithm>
8 #include <cmath>
9 #include <stdio.h>
10 #include <fstream>
11 #include <iostream>
12 
13 #include <Wt/WResource.h>
14 #include <Wt/WImage.h>
15 #include <Wt/WPainter.h>
16 #include <Wt/WPen.h>
17 #include <Wt/WRasterImage.h>
18 #include <Wt/Http/Response.h>
19 
20 #include "MandelbrotImage.h"
21 
22 namespace {
23   class MandelbrotResource : public WResource
24   {
25   public:
MandelbrotResource(MandelbrotImage * img,int64_t x,int64_t y,int w,int h)26     MandelbrotResource(MandelbrotImage *img,
27 		       int64_t x, int64_t y, int w, int h)
28       : img_(img),
29 	x_(x), y_(y), w_(w), h_(h)
30     { }
31 
~MandelbrotResource()32     virtual ~MandelbrotResource() {
33       beingDeleted();
34     }
35 
handleRequest(const Http::Request & request,Http::Response & response)36     void handleRequest(const Http::Request& request,
37 		       Http::Response& response) {
38       WRasterImage image("png", w_, h_);
39       img_->generate(x_, y_, &image);
40       image.handleRequest(request, response);
41     }
42 
43   private:
44     MandelbrotImage *img_;
45     int64_t x_, y_;
46     int w_, h_;
47   };
48 }
49 
MandelbrotImage(int width,int height,int64_t virtualWidth,int64_t virtualHeight,double bx1,double by1,double bx2,double by2)50 MandelbrotImage::MandelbrotImage(int width, int height,
51 				 int64_t virtualWidth,
52 				 int64_t virtualHeight,
53 				 double bx1, double by1,
54 				 double bx2, double by2)
55   : WVirtualImage(width, height, virtualWidth, virtualHeight, 256),
56     bx1_(bx1), by1_(by1),
57     bwidth_(bx2 - bx1), bheight_(by2 - by1),
58     maxDepth_(50),
59     bailOut2_(30*30)
60 {
61   enableDragging();
62   redrawAll();
63   scroll(width*2, virtualHeight/2 - height);
64 }
65 
zoomIn()66 void MandelbrotImage::zoomIn()
67 {
68   resizeImage(imageWidth() * 2, imageHeight() * 2);
69 
70   scrollTo(currentTopLeftX() * 2 + viewPortWidth()/2,
71 	   currentTopLeftY() * 2 + viewPortHeight()/2);
72 }
73 
zoomOut()74 void MandelbrotImage::zoomOut()
75 {
76   scrollTo(currentTopLeftX() / 2 - viewPortWidth()/4,
77 	   currentTopLeftY() / 2 - viewPortHeight()/4);
78 
79   resizeImage(std::max((int64_t)viewPortWidth(), imageWidth() / 2),
80 	      std::max((int64_t)viewPortHeight(), imageHeight() / 2));
81 }
82 
render(int64_t x,int64_t y,int w,int h)83 std::unique_ptr<WResource> MandelbrotImage::render(int64_t x, int64_t y, int w, int h)
84 {
85   return std::make_unique<MandelbrotResource>(this, x, y, w, h);
86 }
87 
generate(int64_t x,int64_t y,WRasterImage * img)88 void MandelbrotImage::generate(int64_t x, int64_t y, WRasterImage *img)
89 {
90   int w = img->width().toPixels();
91   int h = img->height().toPixels();
92 
93   std::cerr << "rendering: (" << x << "," << y << ") ("
94 	    << x+w << "," << y+h << ")" << std::endl;
95 
96   for (int i = 0; i < w; ++i)
97     for (int j = 0; j < h; ++j) {
98       double bx = convertPixelX(x + i);
99       double by = convertPixelY(y + j);
100       double d = calcPixel(bx, by);
101 
102       int lowr = 100;
103 
104       int r, g, b;
105       if (d == maxDepth_)
106 	r = g = b = 0;
107       else {
108 	r = lowr + (int)((d * (255-lowr))/maxDepth_);
109 	g = 0 + (int)((d * 255)/maxDepth_);
110 	b = 0;
111       }
112 
113       img->setPixel(i, j, WColor(r, g, b));
114     }
115 
116   img->done();
117 }
118 
convertPixelX(int64_t x)119 double MandelbrotImage::convertPixelX(int64_t x) const
120 {
121   return bx1_ + ((double) (x) / imageWidth() * bwidth_);
122 }
123 
convertPixelY(int64_t y)124 double MandelbrotImage::convertPixelY(int64_t y) const
125 {
126   return by1_ + ((double) (y) / imageHeight() * bheight_);
127 }
128 
currentX1()129 double MandelbrotImage::currentX1() const
130 {
131   return convertPixelX(currentTopLeftX());
132 }
133 
currentY1()134 double MandelbrotImage::currentY1() const
135 {
136   return convertPixelY(currentTopLeftY());
137 }
138 
currentX2()139 double MandelbrotImage::currentX2() const
140 {
141   return convertPixelX(currentBottomRightX());
142 }
143 
currentY2()144 double MandelbrotImage::currentY2() const
145 {
146   return convertPixelY(currentBottomRightY());
147 }
148 
calcPixel(double x,double y)149 double MandelbrotImage::calcPixel(double x, double y)
150 {
151   double x1 = x;
152   double y1 = y;
153 
154   for (int i = 0; i < maxDepth_; ++i) {
155     double xs = x1 * x1;
156     double ys = y1 * y1;
157     double x2 = xs - ys + x;
158     double y2 = x1 * y1 * 2 + y;
159     x1 = x2;
160     y1 = y2;
161 
162     double z = xs + ys;
163 
164     if (xs + ys > bailOut2_)
165       return (double)i + 1 - log(log(sqrt(z)))/log(2.0);
166   }
167 
168   return maxDepth_;
169 }
170