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 }