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 <ae@cy.md> 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 countSort!`a.z`(pixels); 98 99 canvas.clear(BGRX.init); 100 foreach (ref pixel; pixels) 101 canvas.safePut( 102 canvas.w/2 + pixel.x, 103 canvas.h/2 + pixel.y, 104 pixel.color); 105 } 106 107 override int run(string[] args) 108 { 109 if (args.length < 2) 110 throw new Exception("No file specified - please specify a 24-bit .BMP file"); 111 112 auto image = args[1].read().parseBMP!BGR(); 113 114 //static bool havePixel[256][256][256]; 115 auto havePixel = new bool[256][256][256]; 116 117 static bool extreme(uint b) { return b==0 || b==255; } 118 119 // Uncomment for bounding axes 120 /*foreach (r; 0..256) 121 foreach (g; 0..256) 122 foreach (b; 0..256) 123 havePixel[r][g][b] = 124 //(r+g+b)%101 == 0 || 125 (extreme(r) && extreme(g)) || 126 (extreme(r) && extreme(b)) || 127 (extreme(g) && extreme(b));*/ 128 129 foreach (y; 0..image.h) 130 foreach (x; 0..image.w) 131 { 132 auto c = image[x, y]; 133 havePixel[c.r][c.g][c.b] = true; 134 } 135 136 foreach (r; 0..256) 137 foreach (g; 0..256) 138 foreach (b; 0..256) 139 if (havePixel[r][g][b]) 140 pixels ~= Pixel(cast(ubyte)r, cast(ubyte)g, cast(ubyte)b); 141 142 shell = new SDL2Shell(this); 143 shell.video = new SDL2SoftwareVideo(); 144 shell.run(); 145 shell.video.shutdown(); 146 return 0; 147 } 148 149 override void handleKeyDown(Key key, dchar character) 150 { 151 switch (key) 152 { 153 case Key.up : dy = -1; break; 154 case Key.down : dy = +1; break; 155 case Key.left : dx = -1; break; 156 case Key.right: dx = +1; break; 157 case Key.esc : shell.quit(); break; 158 default : break; 159 } 160 } 161 162 override void handleKeyUp(Key key) 163 { 164 switch (key) 165 { 166 case Key.up : 167 case Key.down : dy = 0; break; 168 case Key.left : 169 case Key.right: dx = 0; break; 170 default : break; 171 } 172 } 173 174 override void handleQuit() 175 { 176 shell.quit(); 177 } 178 } 179 180 shared static this() 181 { 182 createApplication!MyApplication(); 183 }