alfps

Rule 90 Sierpinski triangles

Apr 7th, 2021 (edited)
401
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #include <kickstart/all.hpp>
  2. using namespace kickstart::all;     // Abstract_c_file, fsx::*, Array_span_, out, etc.
  3. #include <stdio.h>      // fwrite
  4. #include <stddef.h>     // offset_of
  5.  
  6. #include <algorithm>    // std::copy
  7. #include <bitset>
  8.  
  9. class Binary_writer:
  10.     public Abstract_c_file
  11. {
  12. public:
  13.     Binary_writer( const fsx::Path& path )
  14.         : Abstract_c_file( fsx::open_c_file_or_x( path, "wb" ) )    // UTF-8-ish.
  15.     {}
  16.    
  17.     void output( const Array_span_<const Byte> bytes )
  18.     {
  19.         const Size n = ::fwrite( bytes.data(), 1, bytes.size(), c_file() );
  20.         hopefully( n == bytes.size() )
  21.             or KS_FAIL( ""s << "Only " << n << " of " << bytes.size() << "  bytes written." );
  22.     }
  23. };
  24.  
  25. template< class T >
  26. void to_little_endian( const T, Type_<Byte*>& ) = delete;
  27.  
  28. void to_little_endian( const uint16_t value, Type_<Byte*>& p_bytes )
  29. {
  30.     static_assert( CHAR_BIT == 8 );
  31.     *p_bytes++ = Byte( value & 0xFF );
  32.     *p_bytes++ = Byte( value >> 8 );
  33. }
  34.    
  35. namespace tga {
  36.     // <url: https://en.wikipedia.org/wiki/Truevision_TGA#Header>
  37.  
  38.     namespace rgb {
  39.         using std::copy;
  40.  
  41.         class Pixel
  42.         {
  43.             friend class Image;
  44.             using Data = Byte[3];   // BGR order as in an image file.
  45.             Data data;
  46.  
  47.         public:
  48.             auto r() const -> Byte { return data[2]; }
  49.             auto g() const -> Byte { return data[1]; }
  50.             auto b() const -> Byte { return data[0]; }
  51.            
  52.             Pixel( const Byte r, const Byte g, const Byte b ):
  53.                 data{ b, g, r }
  54.             {}
  55.         };
  56.  
  57.         class Image:
  58.             private Matrix_<Pixel::Data>
  59.         {
  60.         public:
  61.             using Matrix_::Item;
  62.  
  63.             using Matrix_::Matrix_;
  64.             using Matrix_::width;
  65.             using Matrix_::height;
  66.             using Matrix_::p_items;
  67.             using Matrix_::n_items;
  68.  
  69.             void set( const int x, const int y, const Pixel& value )
  70.             {
  71.                 Pixel::Data& image_data = (*this)( x, y );
  72.                 copy( value.data, value.data + sizeof( Pixel::Data ), image_data );
  73.             }
  74.            
  75.             auto get( const int x, const int y ) const
  76.                 -> Pixel
  77.             {
  78.                 const Pixel::Data& image_data = (*this)( x, y );
  79.                 return Pixel( image_data[2], image_data[1], image_data[0] );
  80.             }
  81.         };
  82.     }  // namespace rgb
  83.  
  84.     namespace file {
  85.         struct Spec
  86.         {
  87.             static constexpr int n_bytes = 10;
  88.  
  89.             uint16_t    lower_left_x;
  90.             uint16_t    lower_left_y;
  91.             uint16_t    width;
  92.             uint16_t    height;
  93.             Byte        bits_per_pixel;
  94.             Byte        alpha_channel_depth;        // 4 bits
  95.             Byte        direction;                  // 2 bits
  96.  
  97.             auto descriptor() const -> Byte { return (direction << 4) | alpha_channel_depth; }
  98.            
  99.             void to_bytes_in( Byte*& p_bytes ) const
  100.             {
  101.                 const auto p_start_of_bytes = p_bytes;
  102.                 to_little_endian( lower_left_x,     p_bytes );
  103.                 to_little_endian( lower_left_y,     p_bytes );
  104.                 to_little_endian( width,            p_bytes );
  105.                 to_little_endian( height,           p_bytes );
  106.                 *p_bytes++ = bits_per_pixel;
  107.                 *p_bytes++ = descriptor();
  108.                 assert( p_bytes - p_start_of_bytes == n_bytes );
  109.             }
  110.         };
  111.  
  112.         class Image
  113.         {
  114.             Byte                        m_id_length;
  115.             Byte                        m_color_map_kind;
  116.             Byte                        m_image_kind;
  117.             array<Byte, 5>              m_color_map_spec;
  118.             Spec                        m_spec;             // 10 bytes
  119.             vector<Byte>                m_id;               // id_length bytes
  120.             vector<Byte>                m_color_map;        // length implied by color_map_kind
  121.             Array_span_<const Byte>     m_data;
  122.             // Extension fields can follow, 495 bytes.
  123.            
  124.         public:
  125.             Image( const rgb::Image& image ):
  126.                 m_id_length( 0 ),           // No id.
  127.                 m_color_map_kind( 0 ),      // No color map.
  128.                 m_image_kind( 2 ),          // Uncompressed true-color image.
  129.                 m_color_map_spec{},         // No color map.
  130.                 m_spec{ 0, 0, uint16_t( image.width() ), uint16_t( image.height() ), 3*8, 0, 2 },
  131.                 m_id(),                     // No id.
  132.                 m_color_map(),              // No color map.
  133.                 m_data{ byte_span_of(
  134.                     image.p_items(), image.n_items()*sizeof( rgb::Image::Item )
  135.                     ) }
  136.             {}
  137.  
  138.             void write_to( const fsx::Path& path ) const
  139.             {
  140.                 auto writer = Binary_writer( path );
  141.                 auto header = array<Byte, sizeof( Image )>();   // More than sufficient.
  142.                 Byte* p_current = header.data();
  143.                
  144.                 *p_current++ = m_id_length;
  145.                 *p_current++ = m_color_map_kind;
  146.                 *p_current++ = m_image_kind;
  147.                 for( const Byte byte: m_color_map_spec ) { *p_current++ = byte; }
  148.                 m_spec.to_bytes_in( p_current );
  149.                 assert( m_id.size() == 0 );
  150.                 assert( m_color_map.size() == 0 );
  151.  
  152.                 const auto n_header_bytes = int( p_current - header.data() );
  153.                 writer.output( byte_span_of( header.data(), n_header_bytes ) );
  154.                 writer.output( m_data );
  155.             }
  156.         };
  157.     }  // namespace file
  158.  
  159.     void write_to( const fsx::Path& path, const rgb::Image& rgb_image )
  160.     {
  161.         file::Image( rgb_image ).write_to( path );
  162.     }
  163. }  // namespace tga
  164.  
  165. void cppmain()
  166. {
  167.     namespace rgb = tga::rgb;
  168.     using std::bitset;
  169.  
  170.     const fsx::Path image_path = fsx::exe_directory_path()/"rule-90-sierpinskies.tga";
  171.     const int size = math::intpow( 2, 11 ) - 1;
  172.     auto image = rgb::Image( size );
  173.     const auto bg = rgb::Pixel( 0xFF, 0xFF, 0xF8 );     // Light yellow.
  174.     const auto fg = rgb::Pixel( 0xA0, 0x20, 0xFF );     // Purplish.
  175.  
  176.     auto current = vector<bool>( size + 2 );
  177.     current[size/2] = true;
  178.     for( int y = 0; y < size; ++y ) {
  179.         if( y == 31 ) { current[7] = true; }            // Just some “random”
  180.         if( y == 127 ) { current[size - 228] = true; }  // dots, for better image.
  181.         for( int x = 0; x < size; ++x ) {
  182.             image.set( x, y, (current[x + 1]? fg : bg) );
  183.         }
  184.  
  185.         auto next = vector<bool>( current.size() );
  186.         for( int x = 0; x < size; ++x ) {
  187.             const unsigned pattern_above =
  188.                 (+current[x] << 2) | (+current[x + 1] << 1) | (+current[x + 2] << 0);
  189.             const auto nks_rule_90 = bitset<8>( 0b01011010 );
  190.             next[x + 1] = nks_rule_90[pattern_above];
  191.         }
  192.         next.swap( current );
  193.     }
  194.     tga::write_to( image_path, image );
  195.     out << "Created “" << image_path.to_string() << "”." << endl;
  196. }
  197.  
  198. auto main() -> int { return with_exceptions_displayed( cppmain ); }
  199.  
RAW Paste Data