1 module caLib.renderers.TwoDimBasicRenderer;
2 
3 import caLib_abstract.lattice : isLattice, isAnyLattice, isBoundedLattice;
4 import caLib_abstract.palette : isColorPalette, isAnyColorPalette;
5 import std.exception : enforce;
6 import std.algorithm : canFind;
7 import std.algorithm.comparison : max;
8 import std.traits : CommonType;
9 import caLib_util.image : Image;
10 import caLib_util.video : Video;
11 import caLib_util.structs : Rect, Color;
12 import caLib_util.graphics;
13 
14 public import caLib_util.graphics : Window;
15 
16 
17 
18 template create_TwoDimBasicRenderer(Ct, Lt, Pt)
19 if(isLattice!(Lt, Ct, 2) && isColorPalette!(Pt, Ct))
20 {
21     TwoDimBasicRenderer!(Ct, Lt, Pt)*
22     create_TwoDimBasicRenderer(Lt* lattice, Pt* palette, Window window)
23     {
24         return new TwoDimBasicRenderer!(Ct, Lt, Pt)(lattice, palette, window);
25     }
26 }
27 
28 
29 
30 
31 auto create_TwoDimBasicRenderer(Lt, Pt)(Lt* lattice, Pt* palette, Window window)
32 if(isAnyLattice!Lt && isAnyColorPalette!Pt && Lt.Dimension == 2)
33 {
34     alias Ct = CommonType!(Lt.CellStateType, Pt.CellStateType);
35     static assert(!is(Ct == void));
36 
37     return new TwoDimBasicRenderer!(Ct, Lt, Pt)(lattice, palette, window);
38 }
39 
40 
41 
42 struct TwoDimBasicRenderer(Ct, Lt, Pt)
43 if(isColorPalette!(Pt, Ct) && isLattice!(Lt, Ct, 2))
44 {
45 
46 private:
47 
48     Lt* lattice;
49 
50     Pt* palette;
51 
52     Rect v;
53 
54     Window window;
55     Texture texture;
56 
57     bool recording;
58     string videoPath;
59     Video video;
60 
61 public:
62 
63     this(Lt* lattice, Pt* palette, Window window)
64     {
65         this.lattice = lattice;
66         this.palette = palette;
67 
68         this.window = window;
69 
70         v = setupViewPort(window, lattice);
71 
72         texture = new Texture(window, v.w, v.h);
73 
74         recording = false;
75         videoPath = null;
76         video = null;
77     }
78 
79     void render()
80     {
81         {
82             uint* pixels = texture.lock();
83             scope(exit) texture.unlock();
84 
85             for(int row=v.y; row<v.y + v.h; row++)
86             {
87                 for(int col=v.x; col<v.x + v.w; col++)
88                 {
89                     // TODO if the lattice's get method
90                     //have a "bounded-assumeInBounds" behaviour, use it
91                     pixels[(row-v.y) * texture.getWidth() + (col-v.x)] =
92                         palette.getDisplayValue(lattice.get(col, row));
93                 }
94             }
95 
96             if(recording)
97                 video.addFrame(Image.fromColorValues(pixels, v.w, v.h));
98         }
99 
100         SDL_Rect srect = {0, 0, texture.getWidth(), texture.getHeight()};
101         SDL_Rect drect = {0, 0, window.getWidth(), window.getHeight()};
102         SDL_RenderCopy(window.getRenderer(), texture.getTexture(), &srect, &drect);
103 
104         SDL_RenderPresent(window.getRenderer());
105     }
106 
107     void screenshot(string path)
108     in
109     { assert(path !is(null)); }
110     body
111     {
112         Image image = Image.fromColorValues(texture.lock(), v.w, v.h);
113         image.saveToFile(path);
114 
115         scope(exit) texture.unlock();
116     }
117 
118     void startRecording(string path, uint framerate)
119     {
120         if(recording)
121             stopRecording();
122 
123         recording = true;
124         video = new Video(path, framerate);
125         videoPath = path;
126     }
127     
128     void stopRecording()
129     {
130         video.saveToFile();
131         recording = false;
132         video = null;
133         videoPath = null;
134     }
135 
136     void moveViewport(int xDir, int yDir){}
137     void zoom(int factor){}
138 
139 private:
140 
141     static Rect setupViewPort(Window window, Lt* lattice)
142     {
143         Rect viewport;
144 
145         int cellSize = (window.getWidth() + window.getHeight()) / 2 / 10;
146 
147         viewport.x = window.getWidth()/cellSize/2 * -1;
148         viewport.y = window.getHeight()/cellSize/2 * -1;
149         viewport.w = window.getWidth()/cellSize;
150         viewport.h = window.getHeight()/cellSize;
151 
152         // if the lattice is bounded, set the viewport's size accordingly
153         static if(isBoundedLattice!(Lt, Ct, 2))
154         {
155             int xLength = lattice.getLatticeBounds[0];
156             int yLength = lattice.getLatticeBounds[1];
157             int length = max(xLength, yLength);
158 
159             viewport.x = - (length - xLength) / 2;
160             viewport.y = - (length - yLength) / 2;
161             viewport.w = length;
162             viewport.h = length;
163         }
164 
165         return viewport;
166     }
167 }
168 
169 
170 
171 version(unittest)
172 {
173     import caLib_abstract.renderer : isRenderer;
174     import caLib_abstract.lattice : Lattice;
175     import caLib_abstract.palette : Palette;
176 }
177 
178 
179 
180 unittest
181 {
182     alias Ct = string;
183     assert(isRenderer!(TwoDimBasicRenderer!(Ct, Lattice!(Ct, 2), Palette!(Ct, Color))));
184 }