| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #pragma once | ||
| 2 | #include <memory> | ||
| 3 | #include <tuple> | ||
| 4 | |||
| 5 | //////////////////////////////////////////////////////////////////////////////// | ||
| 6 | |||
| 7 | class Point { | ||
| 8 | public: | ||
| 9 | static Point rectangular(double x, double y); | ||
| 10 | static Point polar(double radius, double angle); | ||
| 11 | [[nodiscard]] double x() const; | ||
| 12 | [[nodiscard]] double y() const; | ||
| 13 | [[nodiscard]] double radius() const; | ||
| 14 | [[nodiscard]] double angle() const; | ||
| 15 | |||
| 16 | private: | ||
| 17 | Point(double x, double y); | ||
| 18 | double x_{0.0}; | ||
| 19 | double y_{0.0}; | ||
| 20 | }; | ||
| 21 | |||
| 22 | double distance(const Point& a, const Point& b); | ||
| 23 | |||
| 24 | //////////////////////////////////////////////////////////////////////////////// | ||
| 25 | |||
| 26 | // Forward declarations. | ||
| 27 | class Circle; | ||
| 28 | class Rectangle; | ||
| 29 | |||
| 30 | // See [What is a "virtual constructor"?](https://isocpp.org/wiki/faq/virtual-functions#virtual-ctors) | ||
| 31 | // See [Non-virtual interface pattern](https://en.wikipedia.org/wiki/Non-virtual_interface_pattern) | ||
| 32 | class Shape { | ||
| 33 | public: | ||
| 34 | 118 | virtual ~Shape() noexcept = default; | |
| 35 | [[nodiscard]] std::unique_ptr<Shape> clone() const; // Create a copy. | ||
| 36 | [[nodiscard]] double area() const; // Area. | ||
| 37 | [[nodiscard]] Circle boundingCircle() const; // Bounding circle. | ||
| 38 | [[nodiscard]] Rectangle boundingBox() const; // Bounding rectangle. | ||
| 39 | Shape& scaleTo(double s); // Set scale. | ||
| 40 | Shape& scaleBy(double ds); // Adjust scale. | ||
| 41 | Shape& rotateTo(double radians); // Set rotation angle. | ||
| 42 | Shape& rotateBy(double dradians); // Adjust rotation angle. | ||
| 43 | Shape& moveTo(double x, double y); // Set position. | ||
| 44 | Shape& moveBy(double dx, double dy); // Adjust position. | ||
| 45 | |||
| 46 | protected: | ||
| 47 | 43 | Shape() noexcept = default; | |
| 48 | 16 | Shape(const Shape&) = default; | |
| 49 | Shape(Shape&&) noexcept = default; | ||
| 50 | Shape& operator=(const Shape&) = default; | ||
| 51 | Shape& operator=(Shape&&) noexcept = default; | ||
| 52 | |||
| 53 | private: | ||
| 54 | // By convention, derived classes implement a virtual constructor | ||
| 55 | // with a covariant return type, which is not possible with smart pointers. | ||
| 56 | [[nodiscard]] virtual Shape* cloneImpl() const = 0; | ||
| 57 | |||
| 58 | // Not affected by rotation or position. | ||
| 59 | [[nodiscard]] virtual double areaImpl(double scale) const = 0; | ||
| 60 | |||
| 61 | // Not affected by rotation or position. | ||
| 62 | [[nodiscard]] virtual double boundingRadiusImpl(double scale) const = 0; | ||
| 63 | |||
| 64 | // Not affected by position. Returns width and height. | ||
| 65 | [[nodiscard]] virtual std::tuple<double, double> boundingBoxImpl( | ||
| 66 | double scale, double rotation) const = 0; | ||
| 67 | |||
| 68 | double scale_{1.0}; | ||
| 69 | double rotation_{0.0}; | ||
| 70 | Point position_{Point::rectangular(0.0, 0.0)}; | ||
| 71 | }; | ||
| 72 | |||
| 73 | //////////////////////////////////////////////////////////////////////////////// | ||
| 74 | |||
| 75 | class Rectangle : public Shape { | ||
| 76 | public: | ||
| 77 | Rectangle(double width, double height); | ||
| 78 | |||
| 79 | private: | ||
| 80 | [[nodiscard]] Rectangle* cloneImpl() const override; | ||
| 81 | [[nodiscard]] double areaImpl(double scale) const override; | ||
| 82 | [[nodiscard]] double boundingRadiusImpl(double scale) const override; | ||
| 83 | [[nodiscard]] std::tuple<double, double> boundingBoxImpl( | ||
| 84 | double scale, double rotation) const override; | ||
| 85 | double width_{0.0}; // Initial width. | ||
| 86 | double height_{0.0}; // Initial height. | ||
| 87 | }; | ||
| 88 | |||
| 89 | //////////////////////////////////////////////////////////////////////////////// | ||
| 90 | |||
| 91 | class Square : public Rectangle { | ||
| 92 | public: | ||
| 93 | Square(double side); | ||
| 94 | |||
| 95 | private: | ||
| 96 | [[nodiscard]] Square* cloneImpl() const override; | ||
| 97 | }; | ||
| 98 | |||
| 99 | //////////////////////////////////////////////////////////////////////////////// | ||
| 100 | |||
| 101 | class Ellipse : public Shape { | ||
| 102 | public: | ||
| 103 | Ellipse(double width, double height); | ||
| 104 | |||
| 105 | private: | ||
| 106 | [[nodiscard]] Ellipse* cloneImpl() const override; | ||
| 107 | [[nodiscard]] double areaImpl(double scale) const override; | ||
| 108 | [[nodiscard]] double boundingRadiusImpl(double scale) const override; | ||
| 109 | [[nodiscard]] std::tuple<double, double> boundingBoxImpl( | ||
| 110 | double scale, double rotation) const override; | ||
| 111 | [[nodiscard]] double radius(double angle) const; | ||
| 112 | double a_; // Initial length of semi-major axis. | ||
| 113 | double b_; // Initial length of semi-minor axis. | ||
| 114 | double e_; // Eccentricity. | ||
| 115 | }; | ||
| 116 | |||
| 117 | //////////////////////////////////////////////////////////////////////////////// | ||
| 118 | |||
| 119 | class Circle : public Ellipse { | ||
| 120 | public: | ||
| 121 | Circle(double radius); | ||
| 122 | |||
| 123 | private: | ||
| 124 | [[nodiscard]] Circle* cloneImpl() const override; | ||
| 125 | }; | ||
| 126 |