-
Notifications
You must be signed in to change notification settings - Fork 4
/
area.hpp
142 lines (124 loc) · 5.24 KB
/
area.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/*
* Copyright (c) 2017, 2018 Florian Jung
*
* This file is part of factorio-bot.
*
* factorio-bot is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License,
* version 3, as published by the Free Software Foundation.
*
* factorio-bot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with factorio-bot. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <type_traits>
#include <string>
#include <cmath>
#include <algorithm>
#include "pos.hpp"
#include "defines.h"
#include <assert.h>
#include <vector>
template<typename T> struct Area_
{
Pos_<T> left_top;
Pos_<T> right_bottom;
Area_() {}
Area_(T x1, T y1, T x2, T y2) : left_top(x1,y1), right_bottom(x2,y2) {}
Area_(const Pos_<T>& lt, const Pos_<T>& rb) : left_top(lt), right_bottom(rb) {}
Area_(const std::vector<Pos_<T>>& positions)
{
assert(!positions.empty());
// calculate the bounding box
left_top = positions[0];
right_bottom = positions[0];
for (const auto& p : positions)
{
if (p.x < left_top.x)
left_top.x = p.x;
if (p.x >= right_bottom.x)
right_bottom.x = p.x+1; // FIXME what to do with floats here :/?
if (p.y < left_top.y)
left_top.y = p.y;
if (p.y >= right_bottom.y)
right_bottom.y = p.y+1;
}
}
Area_(std::string str);
template <typename U> Area_(const Area_<U>& other) : left_top(other.left_top), right_bottom(other.right_bottom) {}
std::string str() const;
bool operator==(const Area_<T>& other) const { return this->left_top==other.left_top && this->right_bottom==other.right_bottom; }
bool empty() const { return !(left_top <= right_bottom); }
void normalize()
{
if (left_top.x > right_bottom.x)
std::swap(left_top.x, right_bottom.x);
if (left_top.y > right_bottom.y)
std::swap(left_top.y, right_bottom.y);
}
Pos_<T> center() const { return (left_top + right_bottom) / 2; }
bool contains(const Pos_<T>& p) const { return left_top <= p && p < right_bottom; }
bool contains_x(T x) const { return left_top.x <= x && x < right_bottom.x; }
bool contains_y(T y) const { return left_top.y <= y && y < right_bottom.y; }
T diameter() const { return std::max(right_bottom.x - left_top.x, right_bottom.y - left_top.y); }
T radius_around(Pos_<T> p) const
{
Pos_<T> rel_left_top = left_top - p;
Pos_<T> rel_right_bottom = right_bottom - p;
return std::max( std::max(rel_left_top.x, rel_left_top.y), std::max(rel_right_bottom.x, rel_right_bottom.y) );
}
Pos_<T> size() const { return right_bottom - left_top; }
template <typename U=T> typename std::enable_if< std::is_floating_point<U>::value, Area_<int> >::type
/*Area_<int>*/ outer() const
{
return Area_<int>( Pos_<int>(int(std::floor(left_top.x)), int(std::floor(left_top.y))),
Pos_<int>(int(std::ceil(right_bottom.x)), int(std::ceil(right_bottom.y))) );
}
[[nodiscard]] Area_<T> rotate(dir4_t rot) const // assumes that the box is originally in NORTH orientation. i.e., passing rot=NORTH will do nothing, passing EAST will rotate 90deg clockwise etc
{
assert(rot == NORTH || rot == EAST || rot == SOUTH || rot == WEST);
switch (rot)
{
case NORTH: return *this;
case EAST: return Area_<T>( Pos_<T>(-right_bottom.y, left_top.x), Pos_<T>(-left_top.y, right_bottom.x) );
case SOUTH: return Area_<T>( Pos_<T>(-right_bottom.x, -right_bottom.y), Pos_<T>(-left_top.x, -left_top.y) );
case WEST: return Area_<T>( Pos_<T>(left_top.y, -right_bottom.x), Pos_<T>(right_bottom.y, -left_top.x) );
}
// can't be reached
assert(false);
}
[[nodiscard]] Area_<T> shift(Pos_<T> offset) const { return Area_<T>(left_top+offset, right_bottom+offset); }
[[nodiscard]] Area_<T> expand(T radius) const { return Area_<T>(left_top-Pos_<T>(radius,radius), right_bottom+Pos_<T>(radius,radius)); }
template <typename U=T> [[nodiscard]] typename std::enable_if< std::is_integral<U>::value, Area_<T> >::type expand(Pos_<T> point) const
{
return expand(Area_<T>(point, point+Pos_<T>(1,1)));
}
[[nodiscard]] Area_<T> expand(Area_<T> other) const { return Area_<T>( std::min(left_top.x, other.left_top.x), std::min(left_top.y, other.left_top.y), std::max(right_bottom.x, other.right_bottom.x), std::max(right_bottom.y, other.right_bottom.y) ); }
[[nodiscard]] T radius() const { return std::max( std::max( left_top.x, left_top.y), std::max( right_bottom.x, right_bottom.y) ); }
[[nodiscard]] Area_<T> intersect(Area_<T> other) const
{
return Area_<T>(
Pos_<T>(
std::max(this->left_top.x, other.left_top.x),
std::max(this->left_top.y, other.left_top.y)
),
Pos_<T>(
std::min(this->right_bottom.x, other.right_bottom.x),
std::min(this->right_bottom.y, other.right_bottom.y)
)
);
}
[[nodiscard]] bool intersects(Area_<T> other) const { return !intersect(other).empty(); }
};
typedef Area_<int> Area;
typedef Area_<double> Area_f;
template <> Area_<int>::Area_(std::string str);
template <> Area_<double>::Area_(std::string str);
extern template struct Area_<int>;
extern template struct Area_<double>;
double distance(Pos_f a, Area_f b);