std::vector<std::string> ribi::ImageCanvas::ConvertGreynessesToAscii( const std::vector<std::vector<double> >& image, const int width //How many chars the ASCII image will be wide ) noexcept { if (width == 0) return std::vector<std::string>{}; //If the number of chars is below 5, //the calculation would be more complicated due to a too trivial value of charWidth assert(width >= 5); std::vector<std::string> v; if (image.empty()) return v; assert(!image.empty()); //Maxy is in proportion with the bitmap const int image_width = image[0].size(); const int image_height = image.size(); const int maxy = (static_cast<double>(width) / static_cast<double>(image_width)) * static_cast<double>(image_height) / 1.5; //Characters are 1.5 higher than wide assert(maxy > 0); const double dX = static_cast<double>(image_width) / static_cast<double>(width); const double dY = static_cast<double>(image_height) / static_cast<double>(maxy); assert(dX > 0.0); assert(dY > 0.0); for (int y=0; y!=maxy; ++y) { std::string s; for (int x=0; x!=width; ++x) { const int x1 = std::min( static_cast<double>(x) * dX, image_width - 1.0) + 0.5; const int y1 = std::min( static_cast<double>(y) * dY, image_height - 1.0) + 0.5; const int x2 = std::min( (static_cast<double>(x) * dX) + dX, image_width - 1.0) + 0.5; const int y2 = std::min( (static_cast<double>(y) * dY) + dY, image_height - 1.0) + 0.5; assert(x1 >= 0); assert(x2 >= 0); assert(y1 >= 0); assert(y2 >= 0); assert(x1 < image_width); assert(x2 < image_width); assert(y1 < image_height); assert(y2 < image_height); const double f = GetFractionGrey(image,x1,y1,x2,y2); assert(f >= 0.0 && f <= 1.0); const std::vector<char> m_gradient { GetAsciiArtGradient() }; const int i = boost::numeric_cast<int>( f * boost::numeric_cast<double>(m_gradient.size() - 1)); assert(i >= 0); assert(i < boost::numeric_cast<int>(m_gradient.size())); const char c = m_gradient[i]; s+=c; } v.push_back(s); } return v; }
void ribi::DrawCanvas::PlotSurface( std::ostream& os, const std::vector<std::vector<double> >& v, const bool use_normal_color_system, const bool as_screen_coordinat_system) { assert(v.empty() == false && "Surface must have a size"); assert(v[0].size() > 0 && "Surface must have a two-dimensional size"); //Obtain the ASCII art gradient and its size static const std::vector<char> asciiArtGradient = GetAsciiArtGradient(); static const int nAsciiArtGradientChars = asciiArtGradient.size(); //Minimum and maximum are not given, so these need to be calculated const double minVal = MinElement(v); double maxVal = MaxElement(v); if (minVal == maxVal) { maxVal = minVal == 0.0 ? 1.0 : minVal * 2.0; } //Draw the pixels const auto row_function( [](const std::vector<double>& row, std::ostream& os, const double minVal, const double maxVal, const bool use_normal_color_system) { //Iterate through each row's columns const std::vector<double>::const_iterator colEnd = row.end(); for (std::vector<double>::const_iterator col = row.begin(); col != colEnd; ++col) { //Scale the found grey value to an ASCII art character assert(maxVal != minVal); assert(maxVal - minVal != 0.0); assert(maxVal > minVal); const double greyValueDouble = ( (*col) - minVal) / (maxVal - minVal); assert(greyValueDouble >= 0.0 && greyValueDouble <= 1.0); const int greyValueInt = (use_normal_color_system ? greyValueDouble : 1.0 - greyValueDouble ) * nAsciiArtGradientChars; const int greyValue = ( greyValueInt < 0 ? 0 : (greyValueInt > nAsciiArtGradientChars - 1 ? nAsciiArtGradientChars - 1: greyValueInt) ); assert(greyValue >= 0 && greyValue < nAsciiArtGradientChars); os << asciiArtGradient[greyValue]; } os << std::endl; } ); //Iterator through all rows if (as_screen_coordinat_system) { for (const auto row: v) { row_function(row,os,minVal,maxVal,use_normal_color_system); } } else { const auto rowEnd = v.rend(); for (auto row = v.rbegin(); row != rowEnd; ++row) { row_function(*row,os,minVal,maxVal,use_normal_color_system); } } }