//============================================================================ // Author : Sven Gothel, Qun Gothel, Svenson Gothel // Copyright : 2022 Gothel Software e.K. // License : MIT // Description : C++ Lesson 0.3 Geometry 1 (Complex Version w/ x-axis scale factor) //============================================================================ #include #include #include /** * Lesson 0.3 * * Implementing rendering of simple geometric Objects using ASCII-Art (w/ x-axis scale factor) */ void print_space(const int n) { for(int i = 0; i < n; ++i) { printf(" "); // space } } void print_mark(const int n, const char c='X') { for(int i = 0; i < n; ++i) { printf("%c", c); } } void print_newline() { printf("\n"); // newline or line-feed (lf) } /** * Round given float and return its integer representation. * * This function utilizes `constexpr`, i.e. might be evaluated at compile time * if the used parameters are of constexpr nature. * * @param v the float * @return the rounded integer representation */ constexpr int round_to_int(const float v) noexcept { return static_cast( std::round( v ) ); } /** * Paint a simple square, not-filled * @param len length of each side of the square * @param dx distance on x-axis, aka the x-position of object's left edge * @param sx scale factor on x-axis, only for object w/o dx */ void square(const int len, const int dx=0, const float sx=1.0) { const int len_sx = round_to_int( (float)len * sx ); print_newline(); for(int y=0; y 0; i-=step_sx) { print_space( dx + ( base_len_sx - i ) / 2 ); print_mark( i ); print_newline(); } } /** * Pyramid w/ base_len == 4 ``` * XX * XXXX ``` * * Pyramid w/ base_len == 5 ``` * X * XXX * XXXXX ``` * * @param base_len length of pyramid's base * @param dx distance on x-axis, aka the x-position of object's left edge * @param sx scale factor on x-axis, only for object w/o dx * @param show_title */ void pyramid_up(const int base_len, const int dx=0, const float sx=1.0, const bool show_title=true) { if( show_title ) { printf("\nPyramid(up, l %d, dx %d, sx %.2f)\n", base_len, dx, sx); } const int base_len_sx = round_to_int( (float)base_len * sx ); const int step_sx = round_to_int( 2 * sx ); int i = round_to_int( (float)( 2 - ( base_len % 2 ) ) * sx ); for(; i <= base_len_sx; i+=step_sx) { print_space( dx + ( base_len_sx - i ) / 2 ); print_mark( i ); print_newline(); } } /** * Salino w/ base_len == 4 ``` * XX * XXXX * XX ``` * * Salino w/ base_len == 5 ``` * X * XXX * XXXXX * XXX * X ``` * * @param base_len length of pyramid's base * @param dx distance on x-axis, aka the x-position of object's left edge * @param sx scale factor on x-axis, only for object w/o dx * @param show_title */ void salino(const int base_len, const int dx=0, const float sx=1.0, const bool show_title=true) { if( show_title ) { printf("\nSalino(l %d, dx %d, sx %.2f)\n", base_len, dx, sx); } pyramid_up(base_len, dx, sx, false); pyramid_down(base_len-2, dx + round_to_int( 1 * sx ), sx, false); } /** * Disk rendering. * * Implementation uses a floating point centroid, or barycenter. * * Disk1(r 1.00, dx 4, sx 1.00) ``` XX XX ``` * Disk1(r 2.00, dx 3, sx 1.00) ``` XX XXXX XXXX XX ``` * Disk1(r 3.00, dx 2, sx 1.00) ``` XXXX XXXXXX XXXXXX XXXXXX XXXXXX XXXX ``` * Disk1(r 3.00, dx 2, sx 2.00) ``` XXXXXXX XXXXXXXXXXX XXXXXXXXXXX XXXXXXXXXXX XXXXXXXXXXX XXXXXXX ``` * * @param radius radius of disk * @param dx distance on x-axis, aka the x-position of object's left edge * @param sx scale factor on x-axis, only for object w/o dx * @param show_title */ void disk_1(const int radius, const int dx=0, const float sx=1.0, const bool show_title=true) { if( show_title ) { printf("\nDisk1(r %d, dx %d, sx %.2f)\n", radius, dx, sx); } const float r_sq = (float)(radius*radius); // square of disk radius const float disk_p0_x = (float)radius - 0.5f; // disk center point p0, x-component, a centroid const float disk_p0_y = (float)radius - 0.5f; // disk center point p0, y-component, a centroid const int aabbox_h = 2 * radius; // disk AABBox height const int aabbox_w = round_to_int( 2.0f * (float)radius * sx ); // disk AABBox width const float sx_r = (float)aabbox_w / ( 2.0f * (float)radius ); for(int y=0; y( 0.0, std::min(1.0 , b) ); // normalize [0..1] return brightness[ std::min(max_idx, (size_t)std::round( b_n * (float)max_idx ) ) ]; } /** * Antialiased (AA) disk rendering using ASCII-Art alike brightness characters. * * Implementation uses a floating point centroid, or barycenter. * * Disk2(r 1.00, dx 4, sx 1.00, aa_seam 1.00) ``` -- -- ``` * Disk2(r 2.00, dx 3, sx 1.00, aa_seam 1.00) ``` ++ +##+ +##+ ++ ``` * Disk2(r 3.00, dx 2, sx 1.00, aa_seam 1.00) ``` .++. .####. +####+ +####+ .####. .++. ``` * Disk2(r 3.00, dx 2, sx 2.00, aa_seam 1.00) ``` .-+++-. .+#######+. +#########+ +#########+ .+#######+. .-+++-. ``` * @param radius radius of disk * @param dx distance on x-axis, aka the x-position of object's left edge * @param sx scale factor on x-axis, only for object w/o dx * @param aa_seam AA seam of pixels considerd for AA brightness adjusted rendering * @param show_title */ void disk_2(const int radius, const int dx=0, const float sx=1.0, const float aa_seam_=1.0, const bool show_title=true) { const float aa_seam = std::max(1.0, aa_seam_); if( show_title ) { printf("\nDisk2(r %d, dx %d, sx %.2f, aa_seam %.2f)\n", radius, dx, sx, aa_seam); } const float disk_p0_x = (float)radius - 0.5f; // disk center point p0, x-component, a centroid const float disk_p0_y = (float)radius - 0.5f; // disk center point p0, y-component, a centroid const int aabbox_h = 2 * radius; // disk AABBox height const int aabbox_w = round_to_int( 2.0f * (float)radius * sx ); // disk AABBox width const float sx_r = (float)aabbox_w / ( 2.0f * (float)radius ); for(int y=0; y * These `paint_func` instances paint the different objects, * e.g. `pyramid_up`, `pyramid_down` etc. */ const float sx = 2.0; const int min_size = 3; const int max_size = 10; printf("\nAlternate object with size [%d..%d]\n", min_size, max_size); /* * Pyramid_up non-capturing lambda function pointer. * * Assignment to a variable of type `paint_func` is only possible here, * since the lambda type is non-capturing. */ paint_func p1 = [](const int sz_, const int dx_, const float sx_) { pyramid_up(sz_, dx_, sx_); }; /* Array of paint_func function pointer */ paint_func paint_funcs[] = { /* Assigning `square` function-pointer for index 0.*/ square, /* Assigning pyramid_up non-capturing lambda function-pointer for index 1.*/ p1, /* Create and assigning pyramid_down non-capturing lambda function-pointer for index 2. */ [](const int sz_, const int dx_, const float sx_) { pyramid_down(sz_, dx_, sx_); }, /* Assigning `disk_2b` function-pointer for index 3 */ disk_2b }; // for all sizes for(int i=min_size; i<=max_size; i++) { // for all object types using range-loop for(const paint_func& f : paint_funcs) { paint(i, max_size-i, sx, f); } // A traditional loop would look like: // // for(size_t t=0; t