shushus

Спринт 11. Runtime-полиморфизм с std::variant. Задание 1.

Apr 6th, 2023 (edited)
693
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 16.52 KB | None | 0 0
  1. -----------------------------------------------------------------------
  2. svg.h
  3. -----------------------------------------------------------------------
  4. #pragma once
  5. #define _USE_MATH_DEFINES
  6.  
  7. #include <cstdint>
  8. #include <iostream>
  9. #include <memory>
  10. #include <string>
  11. #include <vector>
  12. #include <optional>
  13. #include <cmath>
  14. #include <variant>
  15.  
  16. namespace svg {
  17.  
  18. class Rgb {
  19. public:
  20.     Rgb() = default;
  21.     Rgb(uint8_t red, uint8_t green, uint8_t blue) : red(red)
  22.                                                   , green(green)
  23.                                                   , blue(blue){};
  24.     uint8_t red = 0;
  25.     uint8_t green = 0;
  26.     uint8_t blue = 0;
  27. };
  28.  
  29. inline void PrintColor(std::ostream& out, Rgb& rgb);
  30.  
  31. class Rgba {
  32. public:
  33.     Rgba() = default;
  34.     Rgba(uint8_t red, uint8_t green, uint8_t blue, double opacity) : red(red)
  35.                                                                    , green(green)
  36.                                                                    , blue(blue)
  37.                                                                    , opacity(opacity) {};
  38.     uint8_t red = 0;
  39.     uint8_t green = 0;
  40.     uint8_t blue = 0;
  41.     double opacity = 1.0;
  42. };  
  43.    
  44. inline void PrintColor(std::ostream& out, Rgba& rgba);
  45.  
  46. using Color = std::variant<std::monostate, std::string, Rgb, Rgba>;
  47. inline const Color NoneColor{"none"};  
  48.    
  49. inline void PrintColor(std::ostream& out, std::monostate);
  50. inline void PrintColor(std::ostream& out, std::string& color);
  51. std::ostream& operator<<(std::ostream& out, const Color& color);
  52.    
  53. enum class StrokeLineCap {
  54.     BUTT,
  55.     ROUND,
  56.     SQUARE,
  57. };
  58.    
  59. inline std::ostream &operator<<(std::ostream &out, StrokeLineCap stroke_line_cap) {
  60.     using namespace std::literals;
  61.     if (stroke_line_cap == StrokeLineCap::BUTT) {
  62.         out << "butt"sv;
  63.     } else if (stroke_line_cap == StrokeLineCap::ROUND) {
  64.         out << "round"sv;
  65.     } else if (stroke_line_cap == StrokeLineCap::SQUARE) {
  66.         out << "square"sv;
  67.     }
  68.     return out;
  69. }
  70.  
  71. enum class StrokeLineJoin {
  72.     ARCS,
  73.     BEVEL,
  74.     MITER,
  75.     MITER_CLIP,
  76.     ROUND,
  77. };
  78.    
  79. inline std::ostream &operator<<(std::ostream &out, StrokeLineJoin stroke_line_join) {
  80.     using namespace std::literals;
  81.     if (stroke_line_join == StrokeLineJoin::ARCS) {
  82.         out << "arcs"sv;
  83.     } else if (stroke_line_join == StrokeLineJoin::BEVEL) {
  84.         out << "bevel"sv;
  85.     } else if (stroke_line_join == StrokeLineJoin::MITER) {
  86.         out << "miter"sv;
  87.     } else if (stroke_line_join == StrokeLineJoin::MITER_CLIP) {
  88.         out << "miter-clip"sv;
  89.     } else if (stroke_line_join == StrokeLineJoin::ROUND) {
  90.         out << "round"sv;
  91.     }
  92.     return out;
  93. }
  94.    
  95. template<typename Owner>
  96. class PathProps {
  97. public:
  98.     Owner& SetFillColor(const Color& color) {
  99.         fill_color_ = std::move(color);
  100.         return AsOwner();
  101.     }
  102.  
  103.     Owner& SetStrokeColor(const Color& color) {
  104.         stroke_color_ = std::move(color);
  105.         return AsOwner();
  106.     }
  107.  
  108.     Owner& SetStrokeWidth(double width) {
  109.         stroke_width_ = width;
  110.         return AsOwner();
  111.     }
  112.  
  113.     Owner& SetStrokeLineCap(StrokeLineCap line_cap) {
  114.         stroke_line_cap_ = line_cap;
  115.         return AsOwner();
  116.     }
  117.  
  118.     Owner& SetStrokeLineJoin(StrokeLineJoin line_join) {
  119.         stroke_line_join_ = line_join;
  120.         return AsOwner();
  121.     }
  122.  
  123. protected:
  124.     ~PathProps() = default;
  125.  
  126.     void RenderAttrs(std::ostream &out) const {
  127.         using namespace std::literals;
  128.  
  129.         if (fill_color_ != std::nullopt) {
  130.             out << "fill=\""sv << *fill_color_ << "\" "sv;
  131.         }
  132.         if (stroke_color_ != std::nullopt) {
  133.             out << "stroke=\""sv << *stroke_color_ << "\" "sv;
  134.         }
  135.         if (stroke_width_ != std::nullopt) {
  136.             out << "stroke-width=\""sv << *stroke_width_ << "\" "sv;
  137.         }
  138.         if (stroke_line_cap_ != std::nullopt) {
  139.             out << "stroke-linecap=\""sv << *stroke_line_cap_ << "\" "sv;
  140.         }
  141.         if (stroke_line_join_ != std::nullopt) {
  142.             out << "stroke-linejoin=\""sv << *stroke_line_join_ << "\" "sv;
  143.         }
  144.     }
  145.    
  146. private:
  147.     std::optional<Color> fill_color_;
  148.     std::optional<Color> stroke_color_;
  149.     std::optional<double> stroke_width_;
  150.     std::optional<StrokeLineCap> stroke_line_cap_;
  151.     std::optional<StrokeLineJoin> stroke_line_join_;
  152.    
  153.     Owner& AsOwner() {
  154.         return static_cast<Owner&>(*this);
  155.     }
  156. };
  157.    
  158. struct Point {
  159.     Point() = default;
  160.     Point(double x, double y) : x(x)
  161.                               , y(y) {}
  162.     double x = 0;
  163.     double y = 0;
  164. };
  165.  
  166. struct RenderContext {
  167.     RenderContext(std::ostream& out) : out(out) {}
  168.  
  169.     RenderContext(std::ostream& out, int indent_step, int indent = 0) : out(out)
  170.                                                                       , indent_step(indent_step)
  171.                                                                       , indent(indent) {}
  172.  
  173.     RenderContext Indented() const {
  174.         return {out,
  175.                 indent_step,
  176.                 indent + indent_step};
  177.     }
  178.  
  179.     void RenderIndent() const {
  180.         for (int i = 0; i < indent; ++i) {
  181.             out.put(' ');
  182.         }
  183.     }
  184.  
  185.     std::ostream& out;
  186.     int indent_step = 0;
  187.     int indent = 0;
  188. };
  189.  
  190. class Object {
  191. public:
  192.     void Render(const RenderContext& context) const;
  193.     virtual ~Object() = default;
  194.  
  195. private:
  196.     virtual void RenderObject(const RenderContext& context) const = 0;
  197. };
  198.  
  199. class Circle final : public Object, public PathProps<Circle> {
  200. public:
  201.     Circle& SetCenter(Point center);
  202.     Circle& SetRadius(double radius);
  203.  
  204. private:
  205.     Point center_;
  206.     double radius_ = 1.0;
  207.    
  208.     void RenderObject(const RenderContext& context) const override;
  209. };
  210.  
  211. class Polyline final : public Object, public PathProps<Polyline> {
  212. public:
  213.     Polyline& AddPoint(Point point);
  214. private:
  215.     std::vector<Point> points_;
  216.     void RenderObject(const RenderContext& context) const override;
  217. };
  218.  
  219. class Text final : public Object, public PathProps<Text> {
  220. public:
  221.     Text& SetPosition(Point pos);
  222.     Text& SetOffset(Point offset);
  223.     Text& SetFontSize(uint32_t size);
  224.     Text& SetFontFamily(std::string font_family);
  225.     Text& SetFontWeight(std::string font_weight);
  226.     Text& SetData(std::string data);
  227.  
  228. private:
  229.     Point position_;
  230.     Point offset_;
  231.     std::string font_family_;
  232.     std::string font_weight_;
  233.     uint32_t font_size_ = 1;
  234.     std::string data_;
  235.      
  236.     static std::string delete_spaces(const std::string& str);
  237.     static std::string uniq_symbols(const std::string& str);
  238.    
  239.     void RenderObject(const RenderContext& context) const override;
  240. };
  241.    
  242. class ObjectContainer {
  243. public:
  244.     virtual ~ObjectContainer() = default;
  245.  
  246.     template<typename Obj>
  247.     void Add(Obj obj);
  248.  
  249.     virtual void AddPtr(std::unique_ptr<Object> &&) = 0;
  250.  
  251. protected:
  252.     std::vector<std::unique_ptr<Object>> objects_;
  253. };
  254.    
  255. template<typename Obj>
  256. void ObjectContainer::Add(Obj obj) {
  257.     objects_.emplace_back(std::make_unique<Obj>(std::move(obj)));
  258. }
  259.    
  260. class Drawable {
  261. public:
  262.     virtual void Draw(ObjectContainer& container) const = 0;
  263.     virtual ~Drawable() = default;
  264. };
  265.  
  266. namespace shapes {  
  267. class Triangle : public Drawable {
  268. public:
  269.     Triangle(Point p1, Point p2, Point p3)
  270.         : p1_(p1)
  271.         , p2_(p2)
  272.         , p3_(p3) {}
  273.  
  274.     void Draw(ObjectContainer& container) const override {
  275.         container.Add(Polyline().AddPoint(p1_).AddPoint(p2_).AddPoint(p3_).AddPoint(p1_));
  276.     }
  277.  
  278. private:
  279.     Point p1_, p2_, p3_;
  280. };
  281.  
  282. class Star : public Drawable {
  283. public:
  284.     Star(Point center, double outer_rad, double inner_rad, int num_rays) {
  285.         polyline_ = CreateStar(center,
  286.                                outer_rad,
  287.                                inner_rad,
  288.                                num_rays);
  289.     }
  290.  
  291.     void Draw(ObjectContainer& container) const override {
  292.         container.Add(polyline_);
  293.     }
  294.  
  295. private:
  296.    
  297.     Polyline polyline_;
  298.    
  299.     Polyline CreateStar(Point center, double outer_rad, double inner_rad, int num_rays) {
  300.         using namespace svg;
  301.         Polyline polyline;
  302.         polyline.SetFillColor("red");
  303.         polyline.SetStrokeColor("black");
  304.         for (int i = 0; i <= num_rays; ++i) {
  305.             double angle = 2 * M_PI * (i % num_rays) / num_rays;
  306.             polyline.AddPoint({center.x + outer_rad * sin(angle), center.y - outer_rad * cos(angle)});
  307.             if (i == num_rays) {
  308.                 break;
  309.             }
  310.             angle += M_PI / num_rays;
  311.             polyline.AddPoint({center.x + inner_rad * sin(angle), center.y - inner_rad * cos(angle)});
  312.             }
  313.         return polyline;
  314.     }
  315. };
  316.  
  317. class Snowman : public Drawable {
  318. public:
  319.     Snowman(Point head_point, double radius) : head_(head_point)
  320.                                              , radius_(radius){}
  321.  
  322.     void Draw(ObjectContainer& container) const override {
  323.        
  324.         using namespace svg;
  325.         Circle head;
  326.         head.SetFillColor("rgb(240,240,240)");
  327.         head.SetStrokeColor("black");
  328.         head.SetCenter(head_).SetRadius(radius_);
  329.        
  330.         Circle middle;
  331.         middle.SetFillColor("rgb(240,240,240)");
  332.         middle.SetStrokeColor("black");
  333.         middle.SetCenter({ head_.x, head_.y + radius_ * 2 }).SetRadius(radius_ * 1.5);
  334.        
  335.         Circle down;
  336.         down.SetFillColor("rgb(240,240,240)");
  337.         down.SetStrokeColor("black");
  338.         down.SetCenter({ head_.x, head_.y + radius_ * 5 }).SetRadius(radius_ * 2);
  339.        
  340.         container.Add(down);
  341.         container.Add(middle);
  342.         container.Add(head);
  343.     }
  344.    
  345. private:
  346.     Point head_;
  347.     double radius_;
  348. };
  349.  
  350. }//end namespace shapes
  351.    
  352. class Document : public ObjectContainer {
  353. public:
  354.    
  355.     void AddPtr(std::unique_ptr<Object>&& obj) override {
  356.         objects_.emplace_back(std::move(obj));
  357.     }
  358.     void Render(std::ostream& out) const;
  359. };
  360.  
  361. }//end namespace svg
  362. -----------------------------------------------------------------------
  363. svg.cpp
  364. -----------------------------------------------------------------------
  365. #include "svg.h"
  366.  
  367. namespace svg {
  368.  
  369. using namespace std::literals;
  370.    
  371. // ---------- Color ------------------
  372.    
  373. inline void PrintColor(std::ostream& out, Rgb& rgb) {
  374.     out << "rgb("sv << static_cast<short>(rgb.red) << ","sv
  375.                     << static_cast<short>(rgb.green) << ","sv
  376.                     << static_cast<short>(rgb.blue) << ")"sv;
  377. }
  378.    
  379. inline void PrintColor(std::ostream& out, Rgba& rgba) {
  380.     out << "rgba("sv << static_cast<short>(rgba.red) << ","sv
  381.                      << static_cast<short>(rgba.green) << ","sv
  382.                      << static_cast<short>(rgba.blue) << ","sv
  383.                      << (rgba.opacity) << ")"sv;
  384. }
  385.    
  386. inline void PrintColor(std::ostream& out, std::monostate) {
  387.     out << "none"sv;
  388. }
  389.  
  390. inline void PrintColor(std::ostream& out, std::string& color) {
  391.     out << color;
  392. }
  393.    
  394. std::ostream& operator<<(std::ostream& out, const Color& color) {
  395.     std::visit([&out](auto value) {
  396.             PrintColor(out, value);
  397.     }, color);
  398.    
  399.     return out;
  400. }
  401.    
  402. // ---------- Render ------------------
  403.  
  404. void Object::Render(const RenderContext& context) const {
  405.     context.RenderIndent();
  406.     RenderObject(context);
  407.     context.out << std::endl;
  408. }
  409.  
  410. // ---------- Circle ------------------
  411.  
  412. Circle& Circle::SetCenter(Point center)  {
  413.     center_ = center;
  414.     return *this;
  415. }
  416.  
  417. Circle& Circle::SetRadius(double radius)  {
  418.     radius_ = radius;
  419.     return *this;
  420. }
  421.  
  422. void Circle::RenderObject(const RenderContext& context) const {
  423.     std::ostream& out = context.out;
  424.  
  425.     out << "<circle cx=\""sv << center_.x
  426.         << "\" cy=\""sv << center_.y << "\" "sv;
  427.     out << "r=\""sv << radius_ << "\" "sv;
  428.    
  429.     RenderAttrs(context.out);
  430.     out << "/>"sv;
  431. }
  432.  
  433. // ---------- Polyline ------------------
  434.    
  435. Polyline& Polyline::AddPoint(Point point) {
  436.     points_.emplace_back(point);
  437.     return *this;
  438. }
  439.  
  440. void Polyline::RenderObject(const RenderContext& context) const {
  441.    
  442.     std::ostream& out = context.out;
  443.     out << "<polyline points=\"";
  444.    
  445.     for (size_t i = 0; i < points_.size(); ++i) {
  446.         out << points_[i].x << ","sv << points_[i].y;
  447.  
  448.         if (i+1 != points_.size()) {
  449.             out << " "sv;
  450.         }
  451.     }
  452.     out << "\" ";
  453.     RenderAttrs(context.out);
  454.     out << "/>";
  455. }
  456.  
  457. // ---------- Text ------------------
  458.    
  459. Text& Text::SetPosition(Point pos) {
  460.     position_ = pos;
  461.     return *this;
  462. }
  463.  
  464. Text& Text::SetOffset(Point offset) {
  465.     offset_ = offset;
  466.     return *this;
  467. }
  468.    
  469. Text& Text::SetFontSize(uint32_t size) {
  470.     font_size_ = size;
  471.     return *this;
  472. }
  473.  
  474. Text& Text::SetFontFamily(std::string font_family) {
  475.     font_family_ = std::move(font_family);
  476.     return *this;
  477. }
  478.  
  479. Text& Text::SetFontWeight(std::string font_weight) {
  480.     font_weight_ = std::move(font_weight);
  481.     return *this;
  482. }
  483.  
  484. Text& Text::SetData(std::string data) {
  485.     data_ = std::move(data);
  486.     return *this;
  487. }
  488.  
  489. std::string Text::delete_spaces(const std::string& str) {
  490.     if (str.empty()) {
  491.         return {};
  492.     } else {
  493.        
  494.         auto left = str.find_first_not_of(' ');
  495.         auto right = str.find_last_not_of(' ');
  496.         return str.substr(left, right - left + 1);  
  497.     }
  498. }
  499.  
  500. std::string Text::uniq_symbols(const std::string& str) {
  501.    
  502.     std::string out;
  503.  
  504.     for (char ch : str) {
  505.        
  506.         if (ch == '"') {
  507.             out += "&quot;"sv;
  508.             continue;
  509.            
  510.         } else if (ch == '`' || ch == '\'') {
  511.             out += "&apos;"sv;
  512.             continue;
  513.            
  514.         } else if (ch == '<') {
  515.             out += "&lt;"sv;
  516.             continue;
  517.            
  518.         } else if (ch == '>') {
  519.             out += "&gt;"sv;
  520.             continue;
  521.            
  522.         } else if (ch == '&') {
  523.             out += "&amp;"sv;
  524.             continue;
  525.            
  526.         } else {
  527.            
  528.         }
  529.        
  530.         out += ch;
  531.     }
  532.  
  533.     return out;
  534. }
  535.  
  536. void Text::RenderObject(const RenderContext& context) const {
  537.    
  538.     std::ostream& out = context.out;
  539.     out << "<text ";
  540.     RenderAttrs(context.out);
  541.     out << "x=\""
  542.         << position_.x << "\" y=\""
  543.         << position_.y << "\" "
  544.         << "dx=\""
  545.         << offset_.x << "\" dy=\""
  546.         << offset_.y << "\" "
  547.         << "font-size=\""
  548.         << font_size_ << "\" ";
  549.  
  550.     if (!font_family_.empty()) {
  551.         out << "font-family=\"" << font_family_ << "\" ";
  552.     }
  553.  
  554.     if (!font_weight_.empty()) {
  555.         out << "font-weight=\"" << font_weight_ << "\"";
  556.     }
  557.  
  558.     out << ">"sv << delete_spaces(uniq_symbols(data_)) << "</text>"sv;
  559.  
  560. }
  561.  
  562. // ---------- Document ------------------
  563.  
  564. void Document::Render(std::ostream& out) const {
  565.     int indent = 2;
  566.     int indent_step = 2;
  567.  
  568.     RenderContext context(out, indent_step, indent);
  569.  
  570.     const std::string_view xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>"sv;
  571.     const std::string_view svg = "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"sv;
  572.  
  573.     out << xml << "\n"sv << svg << "\n"sv;
  574.  
  575.     for (const auto& object : objects_) {
  576.         object->Render(context);
  577.     }
  578.    
  579.     out << "</svg>"sv;
  580. }
  581.  
  582. }//end namespace svg
  583. -----------------------------------------------------------------------
  584. main.cpp
  585. -----------------------------------------------------------------------
  586. #include "svg.h"
  587. #include <cassert>
  588.  
  589. using namespace std::literals;
  590. using namespace svg;
  591.  
  592. template <typename DrawableIterator>
  593. void DrawPicture(DrawableIterator begin, DrawableIterator end, svg::ObjectContainer& target) {
  594.     for (auto it = begin; it != end; ++it) {
  595.         (*it)->Draw(target);
  596.     }
  597. }
  598.  
  599. template <typename Container>
  600. void DrawPicture(const Container& container, svg::ObjectContainer& target) {
  601.     using namespace std;
  602.     DrawPicture(begin(container), end(container), target);
  603. }
  604.  
  605. int main() {
  606.     using namespace svg;
  607.     using namespace std;
  608.  
  609.     Color none_color;
  610.     cout << none_color << endl;
  611.    
  612.     Color purple{"purple"s};
  613.     cout << purple << endl;
  614.    
  615.     Color rgb = Rgb{100, 200, 255};
  616.     cout << rgb << endl;
  617.  
  618.     Color rgba = Rgba{100, 200, 255, 0.5};
  619.     cout << rgba << endl;
  620.  
  621.     Circle c;
  622.     c.SetRadius(3.5).SetCenter({1.0, 2.0});
  623.     c.SetFillColor(rgba);
  624.     c.SetStrokeColor(purple);
  625.    
  626.     Document doc;
  627.     doc.Add(std::move(c));
  628.     doc.Render(cout);
  629. }
Advertisement
Add Comment
Please, Sign In to add comment