GCC Code Coverage Report


Directory: ./
File: Shape.cpp
Date: 2026-03-21 20:51:59
Exec Total Coverage
Lines: 79 92 85.9%
Functions: 29 33 87.9%
Branches: 28 58 48.3%

Line Branch Exec Source
1 #include "Shape.hpp"
2 #include "compat/gsl14.hpp"
3 #include "compat/numbers20.hpp"
4 #include <cmath>
5
6 ////////////////////////////////////////////////////////////////////////////////
7
8 // public static
9
10 75 Point Point::rectangular(double x, double y) { return Point{x, y}; }
11
12 16 Point Point::polar(double radius, double angle) {
13 16 return Point{radius * std::cos(angle), radius * std::sin(angle)};
14 }
15
16 // public const
17
18 32 double Point::x() const { return x_; }
19
20 32 double Point::y() const { return y_; }
21
22 16 double Point::radius() const { return std::hypot(x_, y_); }
23
24 16 double Point::angle() const { return std::atan2(y_, x_); }
25
26 // private
27
28 91 Point::Point(double x, double y) : x_{x}, y_{y} {}
29
30 // non-member
31
32 double distance(const Point& a, const Point& b) {
33 return std::hypot(a.x() - b.x(), a.y() - b.y());
34 }
35
36 ////////////////////////////////////////////////////////////////////////////////
37
38 // public const
39
40 16 std::unique_ptr<Shape> Shape::clone() const {
41 16 auto* result = cloneImpl();
42 // If a derived class fails to override cloneImpl,
43 // then the returned object will be of the wrong type.
44
2/4
✓ Branch 4 taken 16 times.
✗ Branch 5 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 16 times.
16 Ensures(typeid(*result) == typeid(*this));
45 16 return std::unique_ptr<Shape>{result};
46 }
47
48 48 double Shape::area() const { return areaImpl(scale_); }
49
50 8 Circle Shape::boundingCircle() const {
51 8 auto result = Circle{boundingRadiusImpl(scale_)};
52
1/2
✓ Branch 17 taken 8 times.
✗ Branch 18 not taken.
8 result.moveTo(position_.x(), position_.y());
53 8 return result;
54 }
55
56 8 Rectangle Shape::boundingBox() const {
57 #if __cplusplus >= 201703L // since C++17
58
1/2
✓ Branch 13 taken 8 times.
✗ Branch 14 not taken.
8 auto [width, height] = boundingBoxImpl(scale_, rotation_);
59 #else
60 auto tuple = boundingBoxImpl(scale_, rotation_);
61 auto& width = std::get<0>(tuple);
62 auto& height = std::get<1>(tuple);
63 #endif // C++17
64 8 auto result = Rectangle{width, height};
65
1/2
✓ Branch 17 taken 8 times.
✗ Branch 18 not taken.
8 result.moveTo(position_.x(), position_.y());
66 8 return result;
67 }
68
69 // public
70
71 12 Shape& Shape::scaleTo(double s) {
72 12 scale_ = s;
73 12 return *this;
74 }
75
76 4 Shape& Shape::scaleBy(double ds) {
77 4 scale_ *= ds;
78 4 return *this;
79 }
80
81 Shape& Shape::rotateTo(double radians) {
82 rotation_ = std::fmod(radians, 2 * std::numbers::pi);
83 return *this;
84 }
85
86 Shape& Shape::rotateBy(double dradians) {
87 rotation_ = std::fmod(rotation_ + dradians, 2 * std::numbers::pi);
88 return *this;
89 }
90
91 16 Shape& Shape::moveTo(double x, double y) {
92
1/2
✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
16 position_ = Point::rectangular(x, y);
93 16 return *this;
94 }
95
96 Shape& Shape::moveBy(double dx, double dy) {
97 position_ = Point::rectangular(position_.x() + dx, position_.y() + dy);
98 return *this;
99 }
100
101 ////////////////////////////////////////////////////////////////////////////////
102
103 // public
104
105 15 Rectangle::Rectangle(double width, double height) :
106 15 width_{width}, height_{height} {
107
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 Expects(width > 0.0);
108
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 Expects(height > 0.0);
109 15 }
110
111 // private const override
112
113 4 Rectangle* Rectangle::cloneImpl() const {
114
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
4 return new Rectangle{*this}; // NOLINT(*-owning-memory)
115 }
116
117 20 double Rectangle::areaImpl(double scale) const {
118 20 return (scale * width_) * (scale * height_);
119 }
120
121 4 double Rectangle::boundingRadiusImpl(double scale) const {
122 4 return std::hypot(scale * width_, scale * height_) / 2;
123 }
124
125 4 std::tuple<double, double> Rectangle::boundingBoxImpl(
126 double scale, double rotation) const {
127 // Corners of scaled rectangle.
128 4 auto halfWidth = scale * width_ / 2;
129 4 auto halfHeight = scale * height_ / 2;
130
1/2
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 auto s1 = Point::rectangular(-halfWidth, -halfHeight);
131
1/2
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 auto s2 = Point::rectangular(-halfWidth, +halfHeight);
132
1/2
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 auto s3 = Point::rectangular(+halfWidth, -halfHeight);
133
1/2
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 auto s4 = Point::rectangular(+halfWidth, +halfHeight);
134
135 // Corners of scaled and rotated rectangle.
136
1/2
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 auto r1 = Point::polar(s1.radius(), s1.angle() + rotation);
137
1/2
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 auto r2 = Point::polar(s2.radius(), s2.angle() + rotation);
138
1/2
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 auto r3 = Point::polar(s3.radius(), s3.angle() + rotation);
139
1/2
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 auto r4 = Point::polar(s4.radius(), s4.angle() + rotation);
140
141 // Find bounds.
142 4 auto x = {r1.x(), r2.x(), r3.x(), r4.x()};
143 4 auto y = {r1.y(), r2.y(), r3.y(), r4.y()};
144
4/8
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
✓ Branch 10 taken 4 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 4 times.
✗ Branch 14 not taken.
8 return {std::max(x) - std::min(x), std::max(y) - std::min(y)};
145 }
146
147 ////////////////////////////////////////////////////////////////////////////////
148
149 // public
150
151 5 Square::Square(double side) : Rectangle{side, side} {}
152
153 // private const override
154
155 4 Square* Square::cloneImpl() const {
156
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
4 return new Square{*this}; // NOLINT(*-owning-memory)
157 }
158
159 ////////////////////////////////////////////////////////////////////////////////
160
161 // public
162
163 28 Ellipse::Ellipse(double width, double height) :
164 28 a_{std::max(width, height) / 2},
165 28 b_{std::min(width, height) / 2},
166 56 e_{std::sqrt(1.0 - std::pow(b_ / a_, 2))} {
167
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
28 Expects(width > 0.0);
168
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
28 Expects(height > 0.0);
169 28 }
170
171 // private const override
172
173 4 Ellipse* Ellipse::cloneImpl() const {
174
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
4 return new Ellipse{*this}; // NOLINT(*-owning-memory)
175 }
176
177 28 double Ellipse::areaImpl(double scale) const {
178 28 return std::numbers::pi * (scale * a_) * (scale * b_);
179 }
180
181 4 double Ellipse::boundingRadiusImpl(double scale) const { return scale * a_; }
182
183 4 std::tuple<double, double> Ellipse::boundingBoxImpl(
184 double scale, double rotation) const {
185
1/2
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
8 return {2 * scale * radius(rotation),
186
1/2
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
8 2 * scale * radius(rotation + std::numbers::pi / 2)};
187 }
188
189 // private const
190
191 8 double Ellipse::radius(double angle) const {
192 8 return b_ / std::sqrt(1.0 - std::pow(e_ * std::cos(angle), 2));
193 }
194
195 ////////////////////////////////////////////////////////////////////////////////
196
197 // public
198
199 26 Circle::Circle(double radius) : Ellipse{2 * radius, 2 * radius} {}
200
201 // private const override
202
203 4 Circle* Circle::cloneImpl() const {
204
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
4 return new Circle{*this}; // NOLINT(*-owning-memory)
205 }
206