1 /******************************************************************************
2  * Copyright (c) 2014, Hobu Inc. (howard@hobu.co)
3  *
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following
8  * conditions are met:
9  *
10  *     * Redistributions of source code must retain the above copyright
11  *       notice, this list of conditions and the following disclaimer.
12  *     * Redistributions in binary form must reproduce the above copyright
13  *       notice, this list of conditions and the following disclaimer in
14  *       the documentation and/or other materials provided
15  *       with the distribution.
16  *     * Neither the name of the Howard Butler or Hobu, Inc.
17  *       the names of its contributors may be
18  *       used to endorse or promote products derived from this software
19  *       without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
28  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
32  * OF SUCH DAMAGE.
33  ****************************************************************************/
34 
35 
36 #include <cmath>
37 #include <algorithm>
38 
39 #include "HexGrid.hpp"
40 #include "HexIter.hpp"
41 #include "Mathpair.hpp"
42 #include "Processor.hpp"
43 #include "Segment.hpp"
44 
45 using namespace std;
46 
47 namespace hexer
48 {
49 
HexGrid(int dense_limit)50 HexGrid::HexGrid(int dense_limit) : m_height(-1.0), m_width(-1.0),
51     m_pos_roots(HexCompare()), m_dense_limit(dense_limit), m_miny(1)
52 {}
53 
initialize(double height)54 void HexGrid::initialize(double height)
55 {
56     m_maxSample = 10000;
57     m_height = height;
58     m_miny = 1;
59     m_width = (3 / (2 * SQRT_3)) * m_height;
60     m_offsets[0] = Point(0, 0);
61     m_offsets[1] = Point(-m_width / 3, m_height / 2);
62     m_offsets[2] = Point(0, m_height);
63     m_offsets[3] = Point(2 * m_width / 3, m_height);
64     m_offsets[4] = Point(m_width, m_height / 2);
65     m_offsets[5] = Point(2 * m_width / 3, 0);
66     m_center_offset = Point(m_width / 3, m_height / 2);
67 }
68 
dense(Hexagon * h)69 bool HexGrid::dense(Hexagon *h)
70 {
71     return h->count() >= m_dense_limit;
72 }
73 
addPoint(Point p)74 void HexGrid::addPoint(Point p)
75 {
76     if (m_width < 0)
77     {
78         m_sample.push_back(p);
79         if (m_sample.size() >= m_maxSample)
80             processSample();
81         return;
82     }
83 
84     Hexagon *h = findHexagon(p);
85     h->increment();
86     if (!h->dense())
87     {
88         if (dense(h))
89         {
90             h->setDense();
91             m_miny = std::min(m_miny, h->y() - 1);
92             if (h->possibleRoot())
93                 m_pos_roots.insert(h);
94             markNeighborBelow(h);
95         }
96     }
97 }
98 
processSample()99 void HexGrid::processSample()
100 {
101     if (m_width > 0 || m_sample.empty())
102         return;
103 
104     double height = computeHexSize(m_sample, m_dense_limit);
105     initialize(height);
106     for (auto pi = m_sample.begin(); pi != m_sample.end(); ++pi)
107         addPoint(*pi);
108     m_sample.clear();
109 }
110 
111 // A debugging function that can be used to make a particular hexagon
112 // dense.
addDenseHexagon(int x,int y)113 void HexGrid::addDenseHexagon(int x, int y)
114 {
115     Hexagon *h = getHexagon(x, y);
116     if (!h->dense())
117     {
118         h->setCount(m_dense_limit);
119         h->setDense();
120         m_miny = std::min(m_miny, h->y() - 1);
121         if (h->possibleRoot())
122             m_pos_roots.insert(h);
123         markNeighborBelow(h);
124     }
125 }
126 
hexBegin()127 HexIter HexGrid::hexBegin()
128 {
129     return HexIter(m_hexes.begin(), this);
130 }
131 
hexEnd()132 HexIter HexGrid::hexEnd()
133 {
134     return HexIter(m_hexes.end(), this);
135 }
136 
markNeighborBelow(Hexagon * h)137 void HexGrid::markNeighborBelow(Hexagon *h)
138 {
139     Coord c = h->neighborCoord(3);
140     Hexagon *neighbor = getHexagon(c);
141     neighbor->setDenseNeighbor(0);
142     if (neighbor->dense() && !neighbor->possibleRoot())
143         m_pos_roots.erase(neighbor);
144 }
145 
146 //  first point (origin) and start of column 0
147 //   |
148 //   |
149 //   |  ------- column 0
150 //   |  |
151 //   |  |   end of column 0 - start of column 1
152 //   |  |   |
153 //   |  v   |                             _____
154 //   v____  v                            |\    |
155 //   /    \                              | \   |  Here's an expansion of what
156 //  / 0,0  \____                         |  \  |  I'm calling a mini-column,
157 //  \      /    \                        |   \ |  showing two half rows.
158 //   \____/ 1,0  \                       |____\|
159 //   /    \      /                       |    /|  The top rectangle is the
160 //  / 0,1  \____/                        |   / |  negative slope case and
161 //  \      /    \                        |  /  |  the lower rectangle is the
162 //   \____/      \                       | /   |  positive slope case.
163 //                                       |/____|
164 //        ** <--  The area above these
165 //                asterisks are the "mini-column"
166 //                The mini-column is 1/3 the total width of the column.
167 //
168 // We are creating a tesselated plane of hexagons where one side of each
169 // hexagon is parallel with the X axis.  We think of the columns of
170 // hexagons as all having the same X value.  Hexagons lower than their
171 // neighbor have successive Y values.
172 //
173 // The hexagon in the second column but just below the hexagon in the first
174 // column as the same Y value as the hexagon above and to the left.  The third
175 // column's Y values are the one less than the hexagon below and to the left
176 // as the second column.
177 //
178 // The first point, whatever it's X/Y location, is made the origin, and is
179 // placed at the top-left edge of hexagon 0,0.
180 //
findHexagon(Point p)181 Hexagon *HexGrid::findHexagon(Point p)
182 {
183     int x, y;
184 
185     if (m_hexes.empty())
186     {
187         m_origin = p;
188         // Make a hex at the origin and insert it.  Return a pointer
189         // to the hexagon in the map.
190         HexMap::value_type hexpair(Hexagon::key(0, 0), Hexagon(0, 0));
191         HexMap::iterator it = m_hexes.insert(hexpair).first;
192         return &it->second;
193     }
194 
195     // Offset by the origin.
196     p -= m_origin;
197 
198     double col = p.m_x / m_width;
199 
200     // First calculate X and Y as if we had a bunch of offset rectangles.
201     // This works for 2/3 of the width of the hexagons.
202     x = (int)floor(col);
203     if (x % 2 == 0)
204         y = static_cast<int>(floor(p.m_y / m_height));
205     else
206         y = static_cast<int>(floor((p.m_y - (m_height / 2)) / m_height));
207 
208     // Compute the column remainder to determine if we are in a strip where
209     // the hexagons overlap (the mini-column).
210     double xcolOffset = col - floor(col);
211     if (xcolOffset > 2.0/3.0)
212     {
213         // Calculate the xvalue as a fraction of the width of the column-piece
214         // containing multiple hex columns.  These overlap columns are 1/3
215         // the total width of any column.
216 
217         // Subtract the 2/3 of the value not relevant to the mini-column.
218         xcolOffset -= 2.0/3.0;
219         // Scale the value to the width of the mini-column.
220         xcolOffset *= 3.0;
221 
222         // Each halfrow contains a single sloping edge of a hexagon.
223         // The slope of the edge is either sqrt(3) or -sqrt(3).  The edge
224         // extends from top left to lower right or from bottom left to top
225         // right.  What we do here is compute the horizontal fraction of
226         // the box (xcolOffset) and the vertical fraction of the box
227         // (yrowOffset) and then compare them.
228         double halfrow = p.m_y / (m_height / 2);
229         int halfy = (int)halfrow;
230         double yrowOffset = halfrow - floor(halfrow);
231 
232         // Negative slope case.
233         if ((halfy % 2 == 0 && x % 2 == 0) || (x % 2 && halfy % 2))
234         {
235             if (xcolOffset > yrowOffset)
236             {
237                 if (x % 2 == 0)
238                     y--;
239                 x++;
240             }
241         }
242         // Positive slope case.
243         else
244         {
245             if (yrowOffset > xcolOffset)
246             {
247                 if (x % 2)
248                     y++;
249                 x++;
250             }
251         }
252     }
253     return getHexagon(x, y);
254 }
255 
256 // Get the hexagon at position x, y.  If it doesn't exist, create it.
257 // Never returns NULL.
getHexagon(int x,int y)258 Hexagon *HexGrid::getHexagon(int x, int y)
259 {
260     // Stick a hexagon into the map if necessary.
261     HexMap::value_type hexpair(Hexagon::key(x, y), Hexagon(x, y));
262     std::pair<HexMap::iterator,bool> retval;
263     retval = m_hexes.insert(hexpair);
264     HexMap::iterator it = retval.first;
265 
266     Hexagon *hex_p = &(it->second);
267 
268     // Return a pointer to the located hexagon.
269     return hex_p;
270 }
271 
272 /**
273 // Walk the outside of the hexagons to make a path.  Hexagon sides are labeled:
274 //
275 //     __0_
276 //  1 /    \ 5
277 //   /      \
278 //   \      /
279 //  2 \____/ 4
280 //      3
281 **/
findShapes()282 void HexGrid::findShapes()
283 {
284     if (m_pos_roots.empty())
285     {
286         throw hexer_error("No areas of sufficient density - no shapes. "
287             "Decrease density or area size.");
288     }
289 
290     while (m_pos_roots.size())
291     {
292         Hexagon *h = *m_pos_roots.begin();
293         findShape(h);
294     }
295 }
296 
findParentPaths()297 void HexGrid::findParentPaths()
298 {
299     std::vector<Path *> roots;
300     for (size_t i = 0; i < m_paths.size(); ++i)
301     {
302         Path *p = m_paths[i];
303         findParentPath(p);
304         // Either add the path to the root list or the parent's list of
305         // children.
306         !p->parent() ?  roots.push_back(p) : p->parent()->addChild(p);
307     }
308     for (size_t i = 0; i < roots.size(); ++i)
309        roots[i]->finalize(CLOCKWISE);
310 
311     // In the end, the list of paths is just the root paths.  Children can
312     // be retrieved from their parents.
313     m_paths = roots;
314 }
315 
findParentPath(Path * p)316 void HexGrid::findParentPath(Path *p)
317 {
318     Segment s = p->rootSegment();
319     Hexagon *h = s.hex();
320     int y = h->y();
321     while (y >= m_miny)
322     {
323         HexPathMap::iterator it = m_hex_paths.find(h);
324         if (it != m_hex_paths.end())
325         {
326             Path *parentPath = it->second;
327             if (parentPath == p->parent())
328             {
329                p->setParent(NULL);
330             }
331             else if (!p->parent() && parentPath != p)
332             {
333                p->setParent(parentPath);
334             }
335         }
336         h = getHexagon(h->x(), --y);
337     }
338 }
339 
findShape(Hexagon * hex)340 void HexGrid::findShape(Hexagon *hex)
341 {
342     if (!hex)
343         throw hexer_error("hexagon was null!");
344 
345     Path *p = new Path(this, CLOCKWISE);
346     Segment first(hex, 0);
347     Segment cur(first);
348     do {
349         cleanPossibleRoot(cur, p);
350         p->push_back(cur);
351         Segment next = cur.leftClockwise(this);
352         if (!next.hex()->dense())
353             next = cur.rightClockwise(this);
354         cur = next;
355     } while (cur != first);
356     m_paths.push_back(p);
357 }
358 
cleanPossibleRoot(Segment s,Path * p)359 void HexGrid::cleanPossibleRoot(Segment s, Path *p)
360 {
361     if (s.possibleRoot(this))
362         m_pos_roots.erase(s.hex());
363     if (s.horizontal())
364     {
365         s.normalize(this);
366         HexPathMap::value_type hexpath(s.hex(), p);
367         m_hex_paths.insert(hexpath);
368     }
369 }
370 
toWKT(std::ostream & out) const371 void HexGrid::toWKT(std::ostream& out) const
372 {
373     auto writePath = [this, &out](size_t pathNum)
374     {
375        rootPaths()[pathNum]->toWKT(out);
376     };
377 
378     out << "MULTIPOLYGON (";
379 
380     if (rootPaths().size())
381         writePath(0);
382     for (size_t pi = 1; pi < rootPaths().size(); ++pi)
383     {
384         out << ",";
385         writePath(pi);
386     }
387     out << ")";
388 }
389 
densePointCount() const390 size_t HexGrid::densePointCount() const
391 {
392     size_t count = 0;
393     for (auto it = m_hexes.begin(); it != m_hexes.end(); ++it)
394     {
395         const Hexagon& h = it->second;
396         if (h.dense())
397             count += h.count();
398     }
399     return count;
400 }
401 
402 } //namespace hexer
403