{"id":111153,"date":"2017-02-07T22:15:52","date_gmt":"2017-02-07T22:15:52","guid":{"rendered":"http:\/\/www.timestored.com\/b\/?p=111153"},"modified":"2017-02-07T22:15:52","modified_gmt":"2017-02-07T22:15:52","slug":"deathstar-bmp-kdb-database","status":"publish","type":"post","link":"https:\/\/www.timestored.com\/b\/deathstar-bmp-kdb-database\/","title":{"rendered":"Drawing the DeathStar bmp with kdb+"},"content":{"rendered":"<p>This post is a walkthrough of my implementation in Q of the <a href=\"https:\/\/rosettacode.org\/wiki\/Death_Star#Q\">RosettaCode task \u2018Death Star\u2019<\/a>.<\/p>\n<p>The code is organized as general-purpose bitmap generator which can be used in other projects, and a client specific to the task of deathstar-drawing. The interface is a function which passes a map of pixel position to pixel value. The map can be a mapping function, or alternatively a 2D array of pixel values. The bitmap generator raster-scans the image, getting pixel values from either a mapping function or a mapped array.<\/p>\n<p>genheader follows directly from the <a href=\"https:\/\/en.wikipedia.org\/wiki\/BMP_file_format\">referenced BMP Wikipedia article<\/a>.<\/p>\n\r\n<textarea cols='80' rows='20' id='ccode-genheader'>\r\ngenheader:{[w;h]\r\n   0x424d, \"x\"$(f2i4[54+4*h*w],0,0,0,0,54,0,0,0,40,0,0,0,\r\n                f2i4[h],f2i4[w],1,0,24,0,0,0,0,0,\r\n                f2i4[h*((w*3)+((w*3)mod 4))],\r\n                19,11,0,0,19,11,0,0,0,0,0,0,0,0,0,0)};\r\n\r\n<\/textarea><script type='text\/javascript'>var myCodeMirror = CodeMirror.fromTextArea(document.getElementById('ccode-genheader'), {lineNumbers: true, matchBrackets: true, indentUnit: 4, tabMode: 'default', mode: 'text\/x-c', readOnly:true });<\/script>\r\n<\/textarea>\r\n\n<p>genbitmap and genrow perform a raster scan of the image to be constructed. genbitmap steps along the vertical axis, calling genrow, which steps along the pixels of the current line, in turn calling fcn, the pixel-mapping function passed in by the client.<\/p>\n\r\n<textarea cols='80' rows='20' id='ccode-genbit'>\r\ngenbitmap:{[w;h;fcn]\r\n    ary:enlist 0i;yy:0i;do[h;ary,:genrow[w;yy;fcn];yy+:1i];\"x\"$1_ary};\r\n\r\ngenrow:{[w;y;fcn]\r\n    row:enlist 0i;xx:0i;do[w;row,:fcn[xx;y];xx+:1i];row,:((w mod 4)#0i);1_row};\r\n\r\n\/\/ writebmp pulls it all together.\r\n\r\nwritebmp:{[w;h;fcn;fn] \r\n    fn 1: (genheader[h;w],genbitmap[w;h;fcn])};\r\n\r\n\r\n<\/textarea><script type='text\/javascript'>var myCodeMirror = CodeMirror.fromTextArea(document.getElementById('ccode-genbit'), {lineNumbers: true, matchBrackets: true, indentUnit: 4, tabMode: 'default', mode: 'text\/x-c', readOnly:true });<\/script>\r\n<\/textarea>\r\n\n<p>A sample client is included in comments, the simplest possible demonstration of shape and color (a circular mask selecting between two fill colors):<\/p>\n\r\n<textarea cols='80' rows='20' id='ccode-sample'>\r\nw:100;\r\nh:75;\r\nfcn:{x0:x-w%2;y0:y-h%2;r:45;$[(r*r)>((x0*x0)+(y0*y0));(0;0;255);(0;255;0)]};\r\nfn:`:demo.bmp;\r\nwritebmp[w;h;fcn;fn];\r\n<\/textarea><script type='text\/javascript'>var myCodeMirror = CodeMirror.fromTextArea(document.getElementById('ccode-sample'), {lineNumbers: true, matchBrackets: true, indentUnit: 4, tabMode: 'default', mode: 'text\/x-c', readOnly:true });<\/script>\r\n<\/textarea>\r\n\n<p>Conveniently, functions and arrays can be equivalently accessed in Q.<br \/>\nHere is an array-passing client which replicates the first example in the <a href=\"https:\/\/en.wikipedia.org\/wiki\/BMP_file_format#Example_1\">Wikipedia article on BMP format<\/a>.<\/p>\n<p>Element ordering can be confusing at first glance: Byte order for RGB pixels is B,G,R. Also, rows are indexed from bottom to top, and since bitmaps are in row-major order, the first and second array indices designated x and y correspond to the y and x image axes respectively.<\/p>\n\r\n<textarea cols='80' rows='20' id='ccode-whg1'>\r\nw:2;h:2;\r\nr:0 0 255;g:0 255 0;b:255 0 0;wt:255 255 255\r\nary:((r;b);(wt;g));\r\nfn:`:demo2.bmp;\r\nwritebmp[w;h;ary;fn];\r\n\r\n\/\/ TODO: discuss situations in which the callback approach is necessary or advantageous\r\n\/\/ The death star code defines a function fcn to be passed to writebmp:\r\n\r\nfcn:{[xpx;ypx]\r\n   x:xpx-w%2;\r\n   y:ypx-h%2;\r\n   z1:z[x;y;r];\r\n   x2:x+190;\r\n   z2:170-z[x2;y;r];\r\n   $[(r*r)<((x*x)+(y*y));\r\n      $[y>-50;\r\n          i:3#0;\r\n          i:200 100 50];\r\n      $[z2>z1;\r\n         i:3#is[x;y;r]*140;\r\n         i:3#is[(-1*x2);(-1*y);r]*120]\r\n   ];\r\n   \"i\"$i};\r\n\r\n<\/textarea><script type='text\/javascript'>var myCodeMirror = CodeMirror.fromTextArea(document.getElementById('ccode-whg1'), {lineNumbers: true, matchBrackets: true, indentUnit: 4, tabMode: 'default', mode: 'text\/x-c', readOnly:true });<\/script>\r\n<\/textarea>\r\n\n<p><a href=\"https:\/\/www.timestored.com\/b\/wp-content\/uploads\/2016\/03\/bmp-format.png\"><img loading=\"lazy\" src=\"https:\/\/www.timestored.com\/b\/wp-content\/uploads\/2016\/03\/bmp-format.png\" alt=\"bmp-format\" width=\"134\" height=\"134\" class=\"alignnone size-full wp-image-111159\" \/><\/a><\/p>\n<p>After centering the image fcn applies several masks:<\/p>\n\r\n<textarea cols='80' rows='20' id='ccode-blah2'>\r\n(r*r)<((x*x)+(y*y)) \/ outside of the radius of the deathstar?    \r\ny>-50               \/ above the horizon of the doomed Alderaan?\r\nz2>z1               \/ not in the weaponface?\r\n\r\n\/\/ The masks select a pixel value or a function which returns one:\r\n\r\n3#0                         \/ space\r\n200 100 50                  \/ the planet\r\n3#is[x;y;r]*140             \/ deathstar surface\r\n3#is[(-1*x2);(-1*y);r]*120] \/ weaponface surface\r\n\r\n<\/textarea><script type='text\/javascript'>var myCodeMirror = CodeMirror.fromTextArea(document.getElementById('ccode-blah2'), {lineNumbers: true, matchBrackets: true, indentUnit: 4, tabMode: 'default', mode: 'text\/x-c', readOnly:true });<\/script>\r\n<\/textarea>\r\n\n<p>is calculates the orientation of a point on a sphere, and then a pixel value for that point, using the dot product of l, the light source direction, and s, the surface orientation. A correction of (1+value)\/2 is applied, to achieve the \u2018soft\u2019 appearance of a space object in a movie. Alternatively we might have suppressed negative illumination values, to get the high-contrast appearance of an actual space object.<\/p>\n\r\n<textarea cols='80' rows='20' id='ccode-is'>\r\nis:{[x;y;r]\r\n   z0:z[x;y;r];\r\n   s:(x;y;z0)%r;\r\n   $[z0>0;i:0.5*1+(+\/)(s*l);i:0];\r\n   i};\r\n\r\n\/\/ z calculates the position of a point on a sphere, which corresponds to the orientation of the surface at that point.\r\n\r\nz:{[x;y;r]sqrt0((r*r)-((x*x)+(y*y)))};\r\n\r\n<\/textarea><script type='text\/javascript'>var myCodeMirror = CodeMirror.fromTextArea(document.getElementById('ccode-is'), {lineNumbers: true, matchBrackets: true, indentUnit: 4, tabMode: 'default', mode: 'text\/x-c', readOnly:true });<\/script>\r\n<\/textarea>\r\n\n<p>We might want to generate images of the death star at different rotations, however due to some simplifying assumptions we can\u2019t rotate the weapon face to the side without glitching. We calculate z1 and z2 to select between the forward surface of the death star and the rearward surface of the weapon face. We should also calculate z3, the forward surface of the weapon face sphere, and z4, the rearward surface of the death star:<\/p>\n<p>   z3:170+z[x2;y;r];<br \/>\n   z4:-z1;<\/p>\n<p>Then the masks can be modified so that when z3 > z1 > z4 > z2, an additional bit of background is visible through the carved-out chunk of the deathstar.<\/p>\n<p>TODO: discuss limitations of mask-and-fill; alternative approaches; display-list; \u2026<br \/>\nTODO: discuss animation; \u2026<br \/>\nTODO: discuss three-component architecture: orchestrator, world, bitmap generator<br \/>\nTODO: \u2026 conclusions<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This post is a walkthrough of my implementation in Q of the RosettaCode task \u2018Death Star\u2019. The code is organized as general-purpose bitmap generator which can be used in other projects, and a client specific to the task of deathstar-drawing. The interface is a function which passes a map of pixel position to pixel value. [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_bbp_topic_count":0,"_bbp_reply_count":0,"_bbp_total_topic_count":0,"_bbp_total_reply_count":0,"_bbp_voice_count":0,"_bbp_anonymous_reply_count":0,"_bbp_topic_count_hidden":0,"_bbp_reply_count_hidden":0,"_bbp_forum_subforum_count":0},"categories":[2],"tags":[72,90],"_links":{"self":[{"href":"https:\/\/www.timestored.com\/b\/wp-json\/wp\/v2\/posts\/111153"}],"collection":[{"href":"https:\/\/www.timestored.com\/b\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.timestored.com\/b\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.timestored.com\/b\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.timestored.com\/b\/wp-json\/wp\/v2\/comments?post=111153"}],"version-history":[{"count":13,"href":"https:\/\/www.timestored.com\/b\/wp-json\/wp\/v2\/posts\/111153\/revisions"}],"predecessor-version":[{"id":111317,"href":"https:\/\/www.timestored.com\/b\/wp-json\/wp\/v2\/posts\/111153\/revisions\/111317"}],"wp:attachment":[{"href":"https:\/\/www.timestored.com\/b\/wp-json\/wp\/v2\/media?parent=111153"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.timestored.com\/b\/wp-json\/wp\/v2\/categories?post=111153"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.timestored.com\/b\/wp-json\/wp\/v2\/tags?post=111153"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}