(back to exercise)
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
// ANCHOR: Point
pub struct Point {
// ANCHOR_END: Point
x: i32,
y: i32,
}
// ANCHOR: Point-impl
impl Point {
// ANCHOR_END: Point-impl
pub fn new(x: i32, y: i32) -> Point {
Point { x, y }
}
pub fn magnitude(self) -> f64 {
f64::from(self.x.pow(2) + self.y.pow(2)).sqrt()
}
pub fn dist(self, other: Point) -> f64 {
(self - other).magnitude()
}
}
impl std::ops::Add for Point {
type Output = Self;
fn add(self, other: Self) -> Self::Output {
Self {
x: self.x + other.x,
y: self.y + other.y,
}
}
}
impl std::ops::Sub for Point {
type Output = Self;
fn sub(self, other: Self) -> Self::Output {
Self {
x: self.x - other.x,
y: self.y - other.y,
}
}
}
// ANCHOR: Polygon
pub struct Polygon {
// ANCHOR_END: Polygon
points: Vec<Point>,
}
// ANCHOR: Polygon-impl
impl Polygon {
// ANCHOR_END: Polygon-impl
pub fn new() -> Polygon {
Polygon { points: Vec::new() }
}
pub fn add_point(&mut self, point: Point) {
self.points.push(point);
}
pub fn left_most_point(&self) -> Option<Point> {
self.points.iter().min_by_key(|p| p.x).copied()
}
pub fn iter(&self) -> impl Iterator<Item = &Point> {
self.points.iter()
}
pub fn length(&self) -> f64 {
if self.points.is_empty() {
return 0.0;
}
let mut result = 0.0;
let mut last_point = self.points[0];
for point in &self.points[1..] {
result += last_point.dist(*point);
last_point = *point;
}
result += last_point.dist(self.points[0]);
result
}
}
// ANCHOR: Circle
pub struct Circle {
// ANCHOR_END: Circle
center: Point,
radius: i32,
}
// ANCHOR: Circle-impl
impl Circle {
// ANCHOR_END: Circle-impl
pub fn new(center: Point, radius: i32) -> Circle {
Circle { center, radius }
}
pub fn circumference(&self) -> f64 {
2.0 * std::f64::consts::PI * f64::from(self.radius)
}
pub fn dist(&self, other: &Self) -> f64 {
self.center.dist(other.center)
}
}
// ANCHOR: Shape
pub enum Shape {
Polygon(Polygon),
Circle(Circle),
}
// ANCHOR_END: Shape
impl From<Polygon> for Shape {
fn from(poly: Polygon) -> Self {
Shape::Polygon(poly)
}
}
impl From<Circle> for Shape {
fn from(circle: Circle) -> Self {
Shape::Circle(circle)
}
}
impl Shape {
pub fn perimeter(&self) -> f64 {
match self {
Shape::Polygon(poly) => poly.length(),
Shape::Circle(circle) => circle.circumference(),
}
}
}
// ANCHOR: unit-tests
#[cfg(test)]
mod tests {
use super::*;
fn round_two_digits(x: f64) -> f64 {
(x * 100.0).round() / 100.0
}
#[test]
fn test_point_magnitude() {
let p1 = Point::new(12, 13);
assert_eq!(round_two_digits(p1.magnitude()), 17.69);
}
#[test]
fn test_point_dist() {
let p1 = Point::new(10, 10);
let p2 = Point::new(14, 13);
assert_eq!(round_two_digits(p1.dist(p2)), 5.00);
}
#[test]
fn test_point_add() {
let p1 = Point::new(16, 16);
let p2 = p1 + Point::new(-4, 3);
assert_eq!(p2, Point::new(12, 19));
}
#[test]
fn test_polygon_left_most_point() {
let p1 = Point::new(12, 13);
let p2 = Point::new(16, 16);
let mut poly = Polygon::new();
poly.add_point(p1);
poly.add_point(p2);
assert_eq!(poly.left_most_point(), Some(p1));
}
#[test]
fn test_polygon_iter() {
let p1 = Point::new(12, 13);
let p2 = Point::new(16, 16);
let mut poly = Polygon::new();
poly.add_point(p1);
poly.add_point(p2);
let points = poly.iter().cloned().collect::<Vec<_>>();
assert_eq!(points, vec![Point::new(12, 13), Point::new(16, 16)]);
}
#[test]
fn test_shape_perimeters() {
let mut poly = Polygon::new();
poly.add_point(Point::new(12, 13));
poly.add_point(Point::new(17, 11));
poly.add_point(Point::new(16, 16));
let shapes = vec![
Shape::from(poly),
Shape::from(Circle::new(Point::new(10, 20), 5)),
];
let perimeters = shapes
.iter()
.map(Shape::perimeter)
.map(round_two_digits)
.collect::<Vec<_>>();
assert_eq!(perimeters, vec![15.48, 31.42]);
}
}
// ANCHOR_END: unit-tests
#[allow(dead_code)]
fn main() {}