rect.go (8252B)
1 // Copyright 2014 Google Inc. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package r2 16 17 import ( 18 "fmt" 19 "math" 20 21 "github.com/golang/geo/r1" 22 ) 23 24 // Point represents a point in ℝ². 25 type Point struct { 26 X, Y float64 27 } 28 29 // Add returns the sum of p and op. 30 func (p Point) Add(op Point) Point { return Point{p.X + op.X, p.Y + op.Y} } 31 32 // Sub returns the difference of p and op. 33 func (p Point) Sub(op Point) Point { return Point{p.X - op.X, p.Y - op.Y} } 34 35 // Mul returns the scalar product of p and m. 36 func (p Point) Mul(m float64) Point { return Point{m * p.X, m * p.Y} } 37 38 // Ortho returns a counterclockwise orthogonal point with the same norm. 39 func (p Point) Ortho() Point { return Point{-p.Y, p.X} } 40 41 // Dot returns the dot product between p and op. 42 func (p Point) Dot(op Point) float64 { return p.X*op.X + p.Y*op.Y } 43 44 // Cross returns the cross product of p and op. 45 func (p Point) Cross(op Point) float64 { return p.X*op.Y - p.Y*op.X } 46 47 // Norm returns the vector's norm. 48 func (p Point) Norm() float64 { return math.Hypot(p.X, p.Y) } 49 50 // Normalize returns a unit point in the same direction as p. 51 func (p Point) Normalize() Point { 52 if p.X == 0 && p.Y == 0 { 53 return p 54 } 55 return p.Mul(1 / p.Norm()) 56 } 57 58 func (p Point) String() string { return fmt.Sprintf("(%.12f, %.12f)", p.X, p.Y) } 59 60 // Rect represents a closed axis-aligned rectangle in the (x,y) plane. 61 type Rect struct { 62 X, Y r1.Interval 63 } 64 65 // RectFromPoints constructs a rect that contains the given points. 66 func RectFromPoints(pts ...Point) Rect { 67 // Because the default value on interval is 0,0, we need to manually 68 // define the interval from the first point passed in as our starting 69 // interval, otherwise we end up with the case of passing in 70 // Point{0.2, 0.3} and getting the starting Rect of {0, 0.2}, {0, 0.3} 71 // instead of the Rect {0.2, 0.2}, {0.3, 0.3} which is not correct. 72 if len(pts) == 0 { 73 return Rect{} 74 } 75 76 r := Rect{ 77 X: r1.Interval{Lo: pts[0].X, Hi: pts[0].X}, 78 Y: r1.Interval{Lo: pts[0].Y, Hi: pts[0].Y}, 79 } 80 81 for _, p := range pts[1:] { 82 r = r.AddPoint(p) 83 } 84 return r 85 } 86 87 // RectFromCenterSize constructs a rectangle with the given center and size. 88 // Both dimensions of size must be non-negative. 89 func RectFromCenterSize(center, size Point) Rect { 90 return Rect{ 91 r1.Interval{Lo: center.X - size.X/2, Hi: center.X + size.X/2}, 92 r1.Interval{Lo: center.Y - size.Y/2, Hi: center.Y + size.Y/2}, 93 } 94 } 95 96 // EmptyRect constructs the canonical empty rectangle. Use IsEmpty() to test 97 // for empty rectangles, since they have more than one representation. A Rect{} 98 // is not the same as the EmptyRect. 99 func EmptyRect() Rect { 100 return Rect{r1.EmptyInterval(), r1.EmptyInterval()} 101 } 102 103 // IsValid reports whether the rectangle is valid. 104 // This requires the width to be empty iff the height is empty. 105 func (r Rect) IsValid() bool { 106 return r.X.IsEmpty() == r.Y.IsEmpty() 107 } 108 109 // IsEmpty reports whether the rectangle is empty. 110 func (r Rect) IsEmpty() bool { 111 return r.X.IsEmpty() 112 } 113 114 // Vertices returns all four vertices of the rectangle. Vertices are returned in 115 // CCW direction starting with the lower left corner. 116 func (r Rect) Vertices() [4]Point { 117 return [4]Point{ 118 {r.X.Lo, r.Y.Lo}, 119 {r.X.Hi, r.Y.Lo}, 120 {r.X.Hi, r.Y.Hi}, 121 {r.X.Lo, r.Y.Hi}, 122 } 123 } 124 125 // VertexIJ returns the vertex in direction i along the X-axis (0=left, 1=right) and 126 // direction j along the Y-axis (0=down, 1=up). 127 func (r Rect) VertexIJ(i, j int) Point { 128 x := r.X.Lo 129 if i == 1 { 130 x = r.X.Hi 131 } 132 y := r.Y.Lo 133 if j == 1 { 134 y = r.Y.Hi 135 } 136 return Point{x, y} 137 } 138 139 // Lo returns the low corner of the rect. 140 func (r Rect) Lo() Point { 141 return Point{r.X.Lo, r.Y.Lo} 142 } 143 144 // Hi returns the high corner of the rect. 145 func (r Rect) Hi() Point { 146 return Point{r.X.Hi, r.Y.Hi} 147 } 148 149 // Center returns the center of the rectangle in (x,y)-space 150 func (r Rect) Center() Point { 151 return Point{r.X.Center(), r.Y.Center()} 152 } 153 154 // Size returns the width and height of this rectangle in (x,y)-space. Empty 155 // rectangles have a negative width and height. 156 func (r Rect) Size() Point { 157 return Point{r.X.Length(), r.Y.Length()} 158 } 159 160 // ContainsPoint reports whether the rectangle contains the given point. 161 // Rectangles are closed regions, i.e. they contain their boundary. 162 func (r Rect) ContainsPoint(p Point) bool { 163 return r.X.Contains(p.X) && r.Y.Contains(p.Y) 164 } 165 166 // InteriorContainsPoint returns true iff the given point is contained in the interior 167 // of the region (i.e. the region excluding its boundary). 168 func (r Rect) InteriorContainsPoint(p Point) bool { 169 return r.X.InteriorContains(p.X) && r.Y.InteriorContains(p.Y) 170 } 171 172 // Contains reports whether the rectangle contains the given rectangle. 173 func (r Rect) Contains(other Rect) bool { 174 return r.X.ContainsInterval(other.X) && r.Y.ContainsInterval(other.Y) 175 } 176 177 // InteriorContains reports whether the interior of this rectangle contains all of the 178 // points of the given other rectangle (including its boundary). 179 func (r Rect) InteriorContains(other Rect) bool { 180 return r.X.InteriorContainsInterval(other.X) && r.Y.InteriorContainsInterval(other.Y) 181 } 182 183 // Intersects reports whether this rectangle and the other rectangle have any points in common. 184 func (r Rect) Intersects(other Rect) bool { 185 return r.X.Intersects(other.X) && r.Y.Intersects(other.Y) 186 } 187 188 // InteriorIntersects reports whether the interior of this rectangle intersects 189 // any point (including the boundary) of the given other rectangle. 190 func (r Rect) InteriorIntersects(other Rect) bool { 191 return r.X.InteriorIntersects(other.X) && r.Y.InteriorIntersects(other.Y) 192 } 193 194 // AddPoint expands the rectangle to include the given point. The rectangle is 195 // expanded by the minimum amount possible. 196 func (r Rect) AddPoint(p Point) Rect { 197 return Rect{r.X.AddPoint(p.X), r.Y.AddPoint(p.Y)} 198 } 199 200 // AddRect expands the rectangle to include the given rectangle. This is the 201 // same as replacing the rectangle by the union of the two rectangles, but 202 // is more efficient. 203 func (r Rect) AddRect(other Rect) Rect { 204 return Rect{r.X.Union(other.X), r.Y.Union(other.Y)} 205 } 206 207 // ClampPoint returns the closest point in the rectangle to the given point. 208 // The rectangle must be non-empty. 209 func (r Rect) ClampPoint(p Point) Point { 210 return Point{r.X.ClampPoint(p.X), r.Y.ClampPoint(p.Y)} 211 } 212 213 // Expanded returns a rectangle that has been expanded in the x-direction 214 // by margin.X, and in y-direction by margin.Y. If either margin is empty, 215 // then shrink the interval on the corresponding sides instead. The resulting 216 // rectangle may be empty. Any expansion of an empty rectangle remains empty. 217 func (r Rect) Expanded(margin Point) Rect { 218 xx := r.X.Expanded(margin.X) 219 yy := r.Y.Expanded(margin.Y) 220 if xx.IsEmpty() || yy.IsEmpty() { 221 return EmptyRect() 222 } 223 return Rect{xx, yy} 224 } 225 226 // ExpandedByMargin returns a Rect that has been expanded by the amount on all sides. 227 func (r Rect) ExpandedByMargin(margin float64) Rect { 228 return r.Expanded(Point{margin, margin}) 229 } 230 231 // Union returns the smallest rectangle containing the union of this rectangle and 232 // the given rectangle. 233 func (r Rect) Union(other Rect) Rect { 234 return Rect{r.X.Union(other.X), r.Y.Union(other.Y)} 235 } 236 237 // Intersection returns the smallest rectangle containing the intersection of this 238 // rectangle and the given rectangle. 239 func (r Rect) Intersection(other Rect) Rect { 240 xx := r.X.Intersection(other.X) 241 yy := r.Y.Intersection(other.Y) 242 if xx.IsEmpty() || yy.IsEmpty() { 243 return EmptyRect() 244 } 245 246 return Rect{xx, yy} 247 } 248 249 // ApproxEqual returns true if the x- and y-intervals of the two rectangles are 250 // the same up to the given tolerance. 251 func (r Rect) ApproxEqual(r2 Rect) bool { 252 return r.X.ApproxEqual(r2.X) && r.Y.ApproxEqual(r2.Y) 253 } 254 255 func (r Rect) String() string { return fmt.Sprintf("[Lo%s, Hi%s]", r.Lo(), r.Hi()) }