(C++) TextCanvas
February 24, 2017 · View on GitHub
(C++) TextCanvas




TextCanvas is a Canvas to put text on.
Technical facts
./CppTextCanvas/CppTextCanvas.pri
INCLUDEPATH += \ ../../Classes/CppTextCanvas SOURCES += \ ../../Classes/CppTextCanvas/textcanvas.cpp HEADERS += \ ../../Classes/CppTextCanvas/textcanvas.h OTHER_FILES += \ ../../Classes/CppTextCanvas/Licence.txt
./CppTextCanvas/textcanvas.h
#ifndef TEXTCANVAS_H #define TEXTCANVAS_H #include <iosfwd> #include <string> #include <vector> #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Weffc++" #pragma GCC diagnostic ignored "-Wunused-local-typedefs" #include <boost/signals2.hpp> #include "canvas.h" #pragma GCC diagnostic pop namespace ribi { ///The TextCanvas is an ASCII art tool to write text ///The Canvas has a coordinat system of (0,0)-(width,height) ///similar to the possible character position on a screen. ///All texts written beyond the range of Canvas is not stored. ///Yet, if for example a text is written at an off-screen coordinat, ///the part that makes it to the Canvas is stored struct TextCanvas : public Canvas { ///The number of characters the Canvas is heigh and wide ///but also the maximum x and y coordinat. The minimum ///x and y coordinats are 0 and 0 TextCanvas( const int width = 1, const int height = 1, const CanvasCoordinatSystem coordinatSystem = CanvasCoordinatSystem::screen ); TextCanvas(const TextCanvas& rhs); TextCanvas& operator=(const TextCanvas& rhs); TextCanvas( const std::vector<std::string>& canvas, const CanvasCoordinatSystem coordinatSystem ); ~TextCanvas() noexcept {} ///Does the (x,y) coordinat exist? bool CanGetChar(const int x, const int y) const noexcept { return IsInRange(x,y); } bool CanPutChar(const int x, const int y) const noexcept { return IsInRange(x,y); } ///Clears the canvas void Clear() noexcept; const std::vector<std::string>& GetCanvas() const noexcept { return m_canvas; } CanvasCoordinatSystem GetCoordinatSystem() const noexcept { return m_coordinat_system; } char GetChar(const int x, const int y) const noexcept; ///Obtain the height of the canvas is characters int GetHeight() const noexcept { return m_canvas.size(); } static std::string GetVersion() noexcept; static std::vector<std::string> GetVersionHistory() noexcept; ///Obtain the width of the canvas is characters int GetWidth() const noexcept { return (GetHeight()==0 ? 0 : m_canvas[0].size() ); } ///Check if a coordinat is in the range of the Canvas bool IsInRange(const int x, const int y) const noexcept; void Load(const std::vector<std::string>& v) noexcept { m_canvas = v; } ///Put a canvas on the canvas void PutCanvas( const int left, const int top, const boost::shared_ptr<const TextCanvas>& canvas ) noexcept; ///Put a character on the Canvas ///If the character is not in range, nothing happens void PutChar(const int x, const int y, const char c) noexcept; ///Put text to the Canvas // // Canvas::DrawText(1,1,"Hello world") results in (I added dots for spaces, for clarity): // // ............. // .Hello world. // ............. // void PutText(const int x, const int y, const std::string& text) noexcept; ///Set the coordinat system used void SetCoordinatSystem(const CanvasCoordinatSystem coordinatSystem) noexcept; ///Convert to a single string, lines seperated with '\n' std::string ToString() const noexcept; ///Convert to a collection of strings std::vector<std::string> ToStrings() const noexcept; private: ///The Canvas its internal data std::vector<std::string> m_canvas; ///The coordinat system used in displayal: ///- screen: origin is at top-left of the screen ///- graph: origin is at bottom-left of the screen CanvasCoordinatSystem m_coordinat_system; //From http://www.richelbilderbeek.nl/CppMinElement.htm template <class Container> static const typename Container::value_type::value_type MinElement(const Container& v); //From http://www.richelbilderbeek.nl/CppMaxElement.htm template <class Container> static const typename Container::value_type::value_type MaxElement(const Container& v); ///Plot a surface on screen ///if as_screen_coordinat_system is true, the origin is in the top left ///corner of the screen, else it is in the bottom left of the screen, ///as is usual in graphs //From http://www.richelbilderbeek.nl/CppPlotSurface.htm static void PlotSurface( std::ostream& os, const std::vector<std::vector<double> >& v, const bool use_normal_color_system, const bool as_screen_coordinat_system); #ifndef NDEBUG static void Test() noexcept; #endif friend std::ostream& operator<<(std::ostream& os, const TextCanvas& canvas); }; std::ostream& operator<<(std::ostream& os, const TextCanvas& canvas); bool operator==(const TextCanvas& lhs, const TextCanvas& rhs) noexcept; bool operator!=(const TextCanvas& lhs, const TextCanvas& rhs) noexcept; } //~namespace ribi #endif
./CppTextCanvas/textcanvas.cpp
#pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Weffc++" #pragma GCC diagnostic ignored "-Wunused-local-typedefs" #include "textcanvas.h" #include <iostream> #include <cassert> #include <cmath> #include <algorithm> #include <functional> #include <iterator> #include <boost/math/constants/constants.hpp> #include "dotmatrixstring.h" #include "testtimer.h" #include "trace.h" #pragma GCC diagnostic pop ribi::TextCanvas::TextCanvas( const int width, const int height, const CanvasCoordinatSystem coordinatSystem) : m_canvas(std::vector<std::string>(height,std::string(width,' '))), m_coordinat_system(coordinatSystem) { #ifndef NDEBUG Test(); #endif assert(width > 0); assert(height > 0); } ribi::TextCanvas::TextCanvas( const std::vector<std::string>& canvas, const CanvasCoordinatSystem coordinatSystem ) : m_canvas(canvas), m_coordinat_system(coordinatSystem) { #ifndef NDEBUG Test(); #endif } ribi::TextCanvas::TextCanvas(const TextCanvas& rhs) : m_canvas{rhs.m_canvas}, m_coordinat_system{rhs.m_coordinat_system} { } ribi::TextCanvas& ribi::TextCanvas::operator=(const TextCanvas& rhs) { this->m_canvas = rhs.m_canvas; this->m_coordinat_system = rhs.m_coordinat_system; return *this; } void ribi::TextCanvas::Clear() noexcept { for (auto& row: m_canvas) { for (auto& cell:row) { cell = ' '; } } #ifndef NDEBUG for (const auto& row: m_canvas) { assert(std::count(row.begin(),row.end(),' ') == static_cast<int>(row.size())); } #endif m_signal_changed(this); } char ribi::TextCanvas::GetChar(const int x, const int y) const noexcept { assert(IsInRange(x,y)); return m_canvas[y][x]; } bool ribi::TextCanvas::IsInRange(const int x, const int y) const noexcept { if ( x < 0 || y < 0 || y >= static_cast<int>(m_canvas.size()) || x >= static_cast<int>(m_canvas[y].size()) ) return false; return true; } std::string ribi::TextCanvas::GetVersion() noexcept { return "1.0"; } std::vector<std::string> ribi::TextCanvas::GetVersionHistory() noexcept { return { "2014-01-09: version 1.0: initial version" }; } void ribi::TextCanvas::PutCanvas( const int left, const int top, const boost::shared_ptr<const TextCanvas>& canvas ) noexcept { const int height { canvas->GetHeight() }; const int width { canvas->GetWidth() }; for (int y=0; y!=height; ++y) { for (int x=0; x!=width; ++x) { PutChar(left + x, top + y,canvas->GetChar(x,y)); } } } void ribi::TextCanvas::PutChar(const int x, const int y, const char c) noexcept { if(!IsInRange(x,y)) return; if(m_canvas[y][x] != c) { m_canvas[y][x] = c; m_signal_changed(this); } } void ribi::TextCanvas::PutText(const int x, const int y, const std::string& text) noexcept { int i=0; for (const auto& c: text) { const int x_here = x + i; const int y_here = y; if (IsInRange(x_here,y_here)) { PutChar(x_here,y_here,c); } ++i; } } void ribi::TextCanvas::SetCoordinatSystem(const CanvasCoordinatSystem coordinatSystem) noexcept { if (this->m_coordinat_system != coordinatSystem) { this->m_coordinat_system = coordinatSystem; this->m_signal_changed(this); } } #ifndef NDEBUG void ribi::TextCanvas::Test() noexcept { { static bool is_tested{false}; if (is_tested) return; is_tested = true; } const TestTimer test_timer(__func__,__FILE__,1.0); //Drawing text { const int maxx = 90; const int maxy = 18; const boost::shared_ptr<TextCanvas> canvas(new TextCanvas(maxx,maxy)); std::stringstream s_before; s_before << (*canvas); const std::string str_before {s_before.str() }; assert(static_cast<int>(str_before.size()) - maxy == maxx * maxy); //-maxy, as newlines are added assert(std::count(str_before.begin(),str_before.end(),' ') == maxx * maxy); //Only spaces canvas->PutText(1,1,"Hello world"); std::stringstream s_after; s_after << (*canvas); const std::string str_after {s_after.str() }; assert(std::count(str_after.begin(),str_after.end(),' ') != maxx * maxy); //Line trly drawn } //Is a text that starts before the canvas partially accepted? { const int maxx = 3; const int maxy = 4; const boost::shared_ptr<TextCanvas> canvas(new TextCanvas(maxx,maxy)); std::stringstream s_before; s_before << (*canvas); const std::string str_before {s_before.str() }; assert(static_cast<int>(str_before.size()) - maxy == maxx * maxy); //-maxy, as newlines are added assert(std::count(str_before.begin(),str_before.end(),' ') == maxx * maxy); //Only spaces canvas->PutText(-5,1,"Hello world"); std::stringstream s_after; s_after << (*canvas); const std::string str_after {s_after.str() }; assert(std::count(str_after.begin(),str_after.end(),' ') != maxx * maxy); //Line truely drawn } //Copy constructor { const TextCanvas a(3,4); const TextCanvas b(a); assert(a==b); } //Assignment operator { const TextCanvas a(3,4); TextCanvas b(4,5); assert(a!=b); b = a; assert(a==b); } } #endif std::string ribi::TextCanvas::ToString() const noexcept { const std::vector<std::string> v { ToStrings() }; std::string s; for (const auto& t: v) { s += t; s += '\n'; } if (!s.empty()) { //Remove the trailing '\n' s.pop_back(); } return s; } std::vector<std::string> ribi::TextCanvas::ToStrings() const noexcept { if (m_coordinat_system == CanvasCoordinatSystem::screen) { return m_canvas; } else { std::vector<std::string> v(m_canvas); std::reverse(std::begin(v),std::end(v)); return v; } } std::ostream& ribi::operator<<(std::ostream& os, const TextCanvas& canvas) { const auto v = canvas.ToStrings(); std::copy(v.begin(),v.end(), std::ostream_iterator<std::string>(os,"\n") ); return os; } bool ribi::operator==(const TextCanvas& lhs, const TextCanvas& rhs) noexcept { return lhs.ToString() == rhs.ToString(); } bool ribi::operator!=(const TextCanvas& lhs, const TextCanvas& rhs) noexcept { return !(lhs == rhs); }