1 /** 2 * 3D visualizer of used colors in an image 3 * 4 * License: 5 * This Source Code Form is subject to the terms of 6 * the Mozilla Public License, v. 2.0. If a copy of 7 * the MPL was not distributed with this file, You 8 * can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * Authors: 11 * Vladimir Panteleev <vladimir@thecybershadow.net> 12 */ 13 14 module ae.demo.colorcube.colorcube; 15 16 import std.datetime; 17 import std.file; 18 import std.math; 19 import std.parallelism; 20 21 import ae.ui.app.application; 22 import ae.ui.app.main; 23 import ae.ui.shell.shell; 24 import ae.ui.shell.sdl2.shell; 25 import ae.ui.video.video; 26 import ae.ui.video.sdl2.video; 27 import ae.ui.video.renderer; 28 29 import ae.utils.array; 30 import ae.utils.fps; 31 32 import ae.utils.graphics.image; 33 import ae.utils.graphics.draw; 34 35 final class MyApplication : Application 36 { 37 override string getName() { return "Demo/ColorCube"; } 38 override string getCompanyName() { return "CyberShadow"; } 39 40 Shell shell; 41 FPSCounter fps; 42 43 int dx, dy; 44 real ax=0, ay=0; 45 46 struct Pixel 47 { 48 BGRX color; 49 int x0, y0, z0; 50 int x, y, z; 51 52 this(ubyte r, ubyte g, ubyte b) 53 { 54 color = BGRX(b, g, r); 55 x0 = r-128; 56 y0 = g-128; 57 z0 = b-128; 58 } 59 60 void rotate(real sinx, real cosx, real siny, real cosy) 61 { 62 auto z1 = x0 *-siny + z0 * cosy; 63 x = cast(int)(10000 + x0 * cosy + z0 * siny) - 10000; // hack: this is faster than lrint 64 y = cast(int)(10000 + y0 * cosx - z1 * sinx) - 10000; 65 z = cast(int)(10000 + y0 * sinx + z1 * cosx) - 10000; 66 } 67 } 68 69 Pixel[] pixels; 70 71 SysTime lastFrame; 72 73 /// Angular rotation speed (radians per second) 74 enum RV = PI; // per second 75 76 override void render(Renderer s) 77 { 78 fps.tick(&shell.setCaption); 79 80 auto canvas = s.lock(); 81 scope(exit) s.unlock(); 82 83 auto now = Clock.currTime(); 84 auto frameDuration = (now - lastFrame).total!"usecs" / 1_000_000f; // fractionary seconds 85 lastFrame = now; 86 87 ay += dx*RV*frameDuration; 88 ax += dy*RV*frameDuration; 89 90 auto sinx = sin(ax); 91 auto cosx = cos(ax); 92 auto siny = sin(ay); 93 auto cosy = cos(ay); 94 95 foreach (ref pixel; parallel(pixels)) 96 pixel.rotate(sinx, cosx, siny, cosy); 97 auto newPixels = countSort!`a.z`(pixels); 98 delete pixels; pixels = newPixels; // avoid memory leak 99 100 canvas.clear(BGRX.init); 101 foreach (ref pixel; pixels) 102 canvas.safePut( 103 canvas.w/2 + pixel.x, 104 canvas.h/2 + pixel.y, 105 pixel.color); 106 } 107 108 override int run(string[] args) 109 { 110 if (args.length < 2) 111 throw new Exception("No file specified - please specify a 24-bit .BMP file"); 112 113 auto image = args[1].read().parseBMP!BGR(); 114 115 //static bool havePixel[256][256][256]; 116 auto havePixel = new bool[256][256][256]; 117 118 static bool extreme(uint b) { return b==0 || b==255; } 119 120 // Uncomment for bounding axes 121 /*foreach (r; 0..256) 122 foreach (g; 0..256) 123 foreach (b; 0..256) 124 havePixel[r][g][b] = 125 //(r+g+b)%101 == 0 || 126 (extreme(r) && extreme(g)) || 127 (extreme(r) && extreme(b)) || 128 (extreme(g) && extreme(b));*/ 129 130 foreach (y; 0..image.h) 131 foreach (x; 0..image.w) 132 { 133 auto c = image[x, y]; 134 havePixel[c.r][c.g][c.b] = true; 135 } 136 137 foreach (r; 0..256) 138 foreach (g; 0..256) 139 foreach (b; 0..256) 140 if (havePixel[r][g][b]) 141 pixels ~= Pixel(cast(ubyte)r, cast(ubyte)g, cast(ubyte)b); 142 143 shell = new SDL2Shell(this); 144 shell.video = new SDL2SoftwareVideo(); 145 shell.run(); 146 shell.video.shutdown(); 147 return 0; 148 } 149 150 override void handleKeyDown(Key key, dchar character) 151 { 152 switch (key) 153 { 154 case Key.up : dy = -1; break; 155 case Key.down : dy = +1; break; 156 case Key.left : dx = -1; break; 157 case Key.right: dx = +1; break; 158 case Key.esc : shell.quit(); break; 159 default : break; 160 } 161 } 162 163 override void handleKeyUp(Key key) 164 { 165 switch (key) 166 { 167 case Key.up : 168 case Key.down : dy = 0; break; 169 case Key.left : 170 case Key.right: dx = 0; break; 171 default : break; 172 } 173 } 174 175 override void handleQuit() 176 { 177 shell.quit(); 178 } 179 } 180 181 shared static this() 182 { 183 createApplication!MyApplication(); 184 }