1 module caLib.renderers.TwoDimBasicRenderer;
2 
3 import caLib_abstract.lattice : isBoundedLattice, isAnyBoundedLattice;
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(isBoundedLattice!(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(isAnyBoundedLattice!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) && isBoundedLattice!(Lt, Ct, 2))
44 {
45 
46 private:
47 
48     Lt* lattice;
49 
50     Pt* palette;
51 
52     Window window;
53     Texture texture;
54     immutable SDL_Rect sRect;
55     immutable SDL_Rect dRect;
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         texture = new Texture(window, lattice.getLatticeBounds()[0], lattice.getLatticeBounds()[1]);
71         sRect = SDL_Rect(0, 0, texture.getWidth(), texture.getHeight());
72         dRect = setupDRect();
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=0; row<sRect.h; row++)
86             {
87                 for(int col=0; col<sRect.w; col++)
88                 {
89                     pixels[row * texture.getWidth() + col] =
90                         palette.getDisplayValue(lattice.get(sRect.x + col, sRect.y + row));
91                 }
92             }
93 
94             if(recording)
95                 video.addFrame(Image.fromColorValues(pixels, sRect.w, sRect.h));
96         }
97 
98         SDL_RenderCopy(window.getRenderer(), texture.getTexture(), &sRect, &dRect);
99         SDL_RenderPresent(window.getRenderer());
100     }
101 
102     void screenshot(string path)
103     in
104     { assert(path !is(null)); }
105     body
106     {
107         Image image = Image.fromColorValues(texture.lock(), sRect.w, sRect.h);
108         scope(exit) texture.unlock();
109         
110         image.saveToFile(path);
111     }
112 
113     void startRecording(string path, uint framerate)
114     {
115         if(recording)
116             stopRecording();
117 
118         recording = true;
119         video = new Video(path, framerate);
120         videoPath = path;
121     }
122     
123     void stopRecording()
124     {
125         video.saveToFile();
126         recording = false;
127         video = null;
128         videoPath = null;
129     }
130 
131 
132 private:
133 
134     SDL_Rect setupDRect()
135     {
136         SDL_Rect dRect;
137 
138         float ratio = cast(float) (sRect.y + sRect.h) / (sRect.x + sRect.w);
139         float windowRatio = cast(float) (window.getHeight()) / window.getWidth();
140 
141         if(ratio <= windowRatio)
142         {
143             dRect.x = 0;
144             dRect.w = window.getWidth();
145             dRect.y = cast(int) ((window.getHeight() - window.getWidth() * ratio) / 2);
146             dRect.h = cast(int) (window.getWidth() * ratio);
147         }
148         else
149         {
150             dRect.y = 0;
151             dRect.h = window.getHeight();
152             dRect.x = cast(int) ((window.getWidth() - window.getHeight() * 1/ratio) / 2);
153             dRect.w = cast(int) (window.getHeight() * 1/ratio);
154         }
155 
156         return dRect;
157     }
158 }
159 
160 
161 
162 version(unittest)
163 {
164     import caLib_abstract.renderer : isRenderer;
165     import caLib_abstract.lattice : BoundedLattice;
166     import caLib_abstract.palette : Palette;
167 }
168 
169 
170 
171 unittest
172 {
173     alias Ct = string;
174     assert(isRenderer!(TwoDimBasicRenderer!(Ct, BoundedLattice!(Ct, 2), Palette!(Ct, Color))));
175 }