1# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""This is a simple geometry module containing basic elements."""
6
7
8# To allow roundoff error
9TOLERANCE = 0.00000001
10
11
12def about_eq(f1, f2):
13    """Determine if two numbers are about equal within the TOLERANCE.
14
15    @param f1: float number 1
16    @param f2: float number 2
17    """
18    return abs(f1 - f2) < TOLERANCE
19
20
21class Point:
22    """A point class."""
23    def __init__(self, x=None, y=None):
24        """Initialize a point.
25
26        @param x: x coordinate
27        @param y: y coordinate
28        """
29        self.x = x if x is None else float(x)
30        self.y = y if y is None else float(y)
31
32    def __bool__(self):
33        """A boolean indicating if this point is defined."""
34        return self.x is not None and self.y is not None
35
36    def __eq__(self, p):
37        """Determine if this point is equal to the specified point, p.
38
39        @param p: a point
40        """
41        return about_eq(self.x, p.x) and about_eq(self.y, p.y)
42
43    def __hash__(self):
44        """Redefine the hash function to meet the consistency requirement.
45
46        In order to put an item into a set, it needs to be hashable.
47        To make an object hashable, it must meet the consistency requirement:
48            a == b must imply hash(a) == hash(b)
49        """
50        return hash((self.x, self.y))
51
52    def __str__(self):
53        """The string representation of the point value."""
54        return 'Point: (%.4f, %.4f)' % (self.x, self.y)
55
56    def distance(self, p):
57        """Calculate the distance between p and this point.
58
59        @param p: a point
60        """
61        dist_x = p.x - self.x
62        dist_y = p.y - self.y
63        return (dist_x ** 2 + dist_y ** 2 ) ** 0.5
64
65    def value(self):
66        """Return the point coordinates."""
67        return (self.x, self.y)
68
69    # __bool__ is used in Python 3.x and __nonzero__ in Python 2.x
70    __nonzero__ = __bool__
71
72
73class Circle:
74    """A circle class."""
75    def __init__(self, center, radius):
76        """Initialize a circle.
77
78        @param center: the center point of the circle
79        @param radius: the radius of the circle
80        """
81        self.center = center
82        self.radius = radius
83
84    def __bool__(self):
85        """A boolean indicating if this circle is defined."""
86        return self.center is not None and self.radius is not None
87
88    def __contains__(self, p):
89        """Determine if p is enclosed in the circle.
90
91        @param p: a point
92        """
93        return self.center.distance(p) <= self.radius + TOLERANCE
94
95    def __eq__(self, c):
96        """Determine if this circle is equal to the specified circle, c.
97
98        @param c: a circle
99        """
100        return self.center == c.center and about_eq(self.radius, c.radius)
101
102    def __hash__(self):
103        """Redefine the hash function to meet the consistency requirement.
104
105        In order to put an item into a sets.Set, it needs to be hashable.
106        To make an object hashable, it must meet the consistency requirement:
107            a == b must imply hash(a) == hash(b)
108        """
109        return hash((self.center, self.radius))
110
111    def __str__(self):
112        """The string representation of the circle value."""
113        return ('Center: %s, %s' % (str(self.center),
114                                    'Radius: %.4f' % self.radius)
115                if self else 'Circle is None')
116
117    # __bool__ is used in Python 3.x and __nonzero__ in Python 2.x
118    __nonzero__ = __bool__
119