Веб-сайт самохостера Lotigara

summaryrefslogtreecommitdiff
path: root/source/base/StarWorldGeometry.hpp
blob: 2c93afd5dd09ff1603b2699dffed2babf275ea0e (plain)
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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
#pragma once

#include "StarPoly.hpp"

namespace Star {

STAR_CLASS(WorldGeometry);

// Utility class for dealing with the non-euclidean nature of the World.
// Handles the surprisingly complex job of deciding intersections and splitting
// geometry across the world wrap boundary.
class WorldGeometry {
public:
  // A null WorldGeometry will have diff / wrap methods etc be the normal
  // euclidean variety.
  WorldGeometry();
  WorldGeometry(unsigned width, unsigned height);
  WorldGeometry(Vec2U const& size);

  bool isNull();

  bool operator==(WorldGeometry const& other) const;
  bool operator!=(WorldGeometry const& other) const;

  unsigned width() const;
  unsigned height() const;
  Vec2U size() const;

  // Wrap given point back into world space by wrapping x
  int xwrap(int x) const;
  float xwrap(float x) const;
  // Only wraps x component.
  Vec2F xwrap(Vec2F const& pos) const;
  Vec2I xwrap(Vec2I const& pos) const;

  // y value is clamped to be in the range [0, height)
  float yclamp(float y) const;

  // Wraps and clamps position
  Vec2F limit(Vec2F const& pos) const;

  bool crossesWrap(float xMin, float xMax) const;

  // Do these two inexes point to the same location
  bool equal(Vec2I const& p1, Vec2I const& p2) const;

  // Same as wrap, returns unsigned type.
  unsigned index(int x) const;
  Vec2U index(Vec2I const& i) const;

  // returns right only distance from x2 to x1 (or x1 - x2).  Always positive.
  int pdiff(int x1, int x2) const;

  // Shortest difference between two given points.  Always returns diff on the
  // "side" that x1 is on.
  float diff(float x1, float x2) const;
  int diff(int x1, int x2) const;

  // Same but for 2d vectors
  Vec2F diff(Vec2F const& p1, Vec2F const& p2) const;
  Vec2I diff(Vec2I const& p1, Vec2I const& p2) const;

  // Midpoint of the shortest line connecting two points.
  Vec2F midpoint(Vec2F const& p1, Vec2F const& p2) const;

  function<float(float, float)> xDiffFunction() const;
  function<Vec2F(Vec2F, Vec2F)> diffFunction() const;
  function<float(float, float, float)> xLerpFunction(Maybe<float> discontinuityThreshold = {}) const;
  function<Vec2F(float, Vec2F, Vec2F)> lerpFunction(Maybe<float> discontinuityThreshold = {}) const;

  // Wrapping functions are not guaranteed to work for objects larger than
  // worldWidth / 2.  Bad things can happen.

  // Split the given Rect across world boundaries.
  StaticList<RectF, 2> splitRect(RectF const& bbox) const;
  // Split the given Rect after translating it by position.
  StaticList<RectF, 2> splitRect(RectF bbox, Vec2F const& position) const;

  StaticList<RectI, 2> splitRect(RectI bbox) const;

  // Same but for Line
  StaticList<Line2F, 2> splitLine(Line2F line, bool preserveDirection = false) const;
  StaticList<Line2F, 2> splitLine(Line2F line, Vec2F const& position, bool preserveDirection = false) const;

  // Same but for Poly
  StaticList<PolyF, 2> splitPoly(PolyF const& poly) const;
  StaticList<PolyF, 2> splitPoly(PolyF poly, Vec2F const& position) const;

  // Split a horizontal region of the world across the world wrap point.
  StaticList<Vec2I, 2> splitXRegion(Vec2I const& xRegion) const;
  StaticList<Vec2F, 2> splitXRegion(Vec2F const& xRegion) const;

  bool rectContains(RectF const& rect1, Vec2F const& pos) const;
  bool rectIntersectsRect(RectF const& rect1, RectF const& rect2) const;
  RectF rectOverlap(RectF const& rect1, RectF const& rect2) const;
  bool polyContains(PolyF const& poly, Vec2F const& pos) const;
  float polyOverlapArea(PolyF const& poly1, PolyF const& poly2) const;

  bool lineIntersectsRect(Line2F const& line, RectF const& rect) const;
  bool lineIntersectsPoly(Line2F const& line, PolyF const& poly) const;
  bool polyIntersectsPoly(PolyF const& poly1, PolyF const& poly2) const;

  bool rectIntersectsCircle(RectF const& rect, Vec2F const& center, float radius) const;
  bool lineIntersectsCircle(Line2F const& line, Vec2F const& center, float radius) const;

  Maybe<Vec2F> lineIntersectsPolyAt(Line2F const& line, PolyF const& poly) const;

  // Returns the distance from a point to any part of the given poly
  float polyDistance(PolyF const& poly, Vec2F const& point) const;

  // Produces a point that is on the same "side" of the world as the source point.
  int nearestTo(int source, int target) const;
  float nearestTo(float source, float target) const;
  Vec2I nearestTo(Vec2I const& source, Vec2I const& target) const;
  Vec2F nearestTo(Vec2F const& source, Vec2F const& target) const;

  Vec2F nearestCoordInBox(RectF const& box, Vec2F const& pos) const;
  Vec2F diffToNearestCoordInBox(RectF const& box, Vec2F const& pos) const;

private:
  Vec2U m_size;
};

inline WorldGeometry::WorldGeometry()
  : m_size(Vec2U()) {}

inline WorldGeometry::WorldGeometry(unsigned width, unsigned height)
  : m_size(width, height) {}

inline WorldGeometry::WorldGeometry(Vec2U const& size)
  : m_size(size) {}

inline bool WorldGeometry::isNull() {
  return m_size == Vec2U();
}

inline bool WorldGeometry::operator==(WorldGeometry const& other) const {
  return m_size == other.m_size;
}

inline bool WorldGeometry::operator!=(WorldGeometry const& other) const {
  return m_size != other.m_size;
}

inline unsigned WorldGeometry::width() const {
  return m_size[0];
}

inline unsigned WorldGeometry::height() const {
  return m_size[1];
}

inline Vec2U WorldGeometry::size() const {
  return m_size;
}

inline int WorldGeometry::xwrap(int x) const {
  if (m_size[0] == 0)
    return x;
  else
    return pmod<int>(x, m_size[0]);
}

inline float WorldGeometry::xwrap(float x) const {
  if (m_size[0] == 0)
    return x;
  else
    return pfmod<float>(x, m_size[0]);
}

inline Vec2F WorldGeometry::xwrap(Vec2F const& pos) const {
  return {xwrap(pos[0]), pos[1]};
}

inline Vec2I WorldGeometry::xwrap(Vec2I const& pos) const {
  return {xwrap(pos[0]), pos[1]};
}

inline float WorldGeometry::yclamp(float y) const {
  return clamp<float>(y, 0, std::nextafter(m_size[1], 0.0f));
}

inline Vec2F WorldGeometry::limit(Vec2F const& pos) const {
  return {xwrap(pos[0]), yclamp(pos[1])};
}

inline bool WorldGeometry::crossesWrap(float xMin, float xMax) const {
  return xwrap(xMax) < xwrap(xMin);
}

inline bool WorldGeometry::equal(Vec2I const& p1, Vec2I const& p2) const {
  return index(p1) == index(p2);
}

inline unsigned WorldGeometry::index(int x) const {
  return (unsigned)xwrap(x);
}

inline Vec2U WorldGeometry::index(Vec2I const& i) const {
  return Vec2U(xwrap(i[0]), i[1]);
}

inline int WorldGeometry::pdiff(int x1, int x2) const {
  if (m_size[0] == 0)
    return x1 - x2;
  else
    return pmod<int>(x1 - x2, m_size[0]);
}

inline float WorldGeometry::diff(float x1, float x2) const {
  if (m_size[0] == 0)
    return x1 - x2;
  else
    return wrapDiffF<float>(x1, x2, m_size[0]);
}

inline int WorldGeometry::diff(int x1, int x2) const {
  if (m_size[0] == 0)
    return x1 - x2;
  else
    return wrapDiff<int>(x1, x2, m_size[0]);
}

inline Vec2F WorldGeometry::diff(Vec2F const& p1, Vec2F const& p2) const {
  float xdiff = diff(p1[0], p2[0]);
  return {xdiff, p1[1] - p2[1]};
}

inline Vec2I WorldGeometry::diff(Vec2I const& p1, Vec2I const& p2) const {
  int xdiff = diff(p1[0], p2[0]);
  return {xdiff, p1[1] - p2[1]};
}

inline Vec2F WorldGeometry::midpoint(Vec2F const& p1, Vec2F const& p2) const {
  return xwrap(diff(p1, p2) / 2 + p2);
}

inline int WorldGeometry::nearestTo(int source, int target) const {
  if (abs(target - source) < (int)(m_size[0] / 2))
    return target;
  else
    return diff(target, source) + source;
}

inline float WorldGeometry::nearestTo(float source, float target) const {
  if (abs(target - source) < (float)(m_size[0] / 2))
    return target;
  else
    return diff(target, source) + source;
}

inline Vec2I WorldGeometry::nearestTo(Vec2I const& source, Vec2I const& target) const {
  return Vec2I(nearestTo(source[0], target[0]), target[1]);
}

inline Vec2F WorldGeometry::nearestTo(Vec2F const& source, Vec2F const& target) const {
  return Vec2F(nearestTo(source[0], target[0]), target[1]);
}

}