libspatialindex API Reference  (git-trunk)
LineSegment.cc
Go to the documentation of this file.
1 /******************************************************************************
2  * Project: libspatialindex - A C++ library for spatial indexing
3  * Author: Marios Hadjieleftheriou, mhadji@gmail.com
4  ******************************************************************************
5  * Copyright (c) 2004, Marios Hadjieleftheriou
6  *
7  * All rights reserved.
8  *
9  * Permission is hereby granted, free of charge, to any person obtaining a
10  * copy of this software and associated documentation files (the "Software"),
11  * to deal in the Software without restriction, including without limitation
12  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13  * and/or sell copies of the Software, and to permit persons to whom the
14  * Software is furnished to do so, subject to the following conditions:
15  *
16  * The above copyright notice and this permission notice shall be included
17  * in all copies or substantial portions of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25  * DEALINGS IN THE SOFTWARE.
26 ******************************************************************************/
27 
28 #include <cstring>
29 #include <cmath>
30 #include <limits>
31 
33 
34 using namespace SpatialIndex;
35 
37 = default;
38 
39 LineSegment::LineSegment(const double* pStartPoint, const double* pEndPoint, uint32_t dimension)
40  : m_dimension(dimension)
41 {
42  // no need to initialize arrays to 0 since if a bad_alloc is raised the destructor will not be called.
43 
44  m_pStartPoint = new double[m_dimension];
45  m_pEndPoint = new double[m_dimension];
46  memcpy(m_pStartPoint, pStartPoint, m_dimension * sizeof(double));
47  memcpy(m_pEndPoint, pEndPoint, m_dimension * sizeof(double));
48 }
49 
50 LineSegment::LineSegment(const Point& startPoint, const Point& endPoint)
51  : m_dimension(startPoint.m_dimension)
52 {
53  if (startPoint.m_dimension != endPoint.m_dimension)
55  "LineSegment::LineSegment: Points have different dimensionalities."
56  );
57 
58  // no need to initialize arrays to 0 since if a bad_alloc is raised the destructor will not be called.
59 
60  m_pStartPoint = new double[m_dimension];
61  m_pEndPoint = new double[m_dimension];
62  memcpy(m_pStartPoint, startPoint.m_pCoords, m_dimension * sizeof(double));
63  memcpy(m_pEndPoint, endPoint.m_pCoords, m_dimension * sizeof(double));
64 }
65 
68 {
69  // no need to initialize arrays to 0 since if a bad_alloc is raised the destructor will not be called.
70 
71  m_pStartPoint = new double[m_dimension];
72  m_pEndPoint = new double[m_dimension];
73  memcpy(m_pStartPoint, l.m_pStartPoint, m_dimension * sizeof(double));
74  memcpy(m_pEndPoint, l.m_pEndPoint, m_dimension * sizeof(double));
75 }
76 
78 {
79  delete[] m_pStartPoint;
80  delete[] m_pEndPoint;
81 }
82 
84 {
85  if (this != &l)
86  {
88  memcpy(m_pStartPoint, l.m_pStartPoint, m_dimension * sizeof(double));
89  memcpy(m_pEndPoint, l.m_pEndPoint, m_dimension * sizeof(double));
90  }
91 
92  return *this;
93 }
94 
96 {
97  if (m_dimension != l.m_dimension)
99  "LineSegment::operator==: LineSegments have different number of dimensions."
100  );
101 
102  for (uint32_t i = 0; i < m_dimension; ++i)
103  {
104  if (
105  m_pStartPoint[i] < l.m_pStartPoint[i] - std::numeric_limits<double>::epsilon() ||
106  m_pStartPoint[i] > l.m_pStartPoint[i] + std::numeric_limits<double>::epsilon()) return false;
107 
108  if (
109  m_pEndPoint[i] < l.m_pEndPoint[i] - std::numeric_limits<double>::epsilon() ||
110  m_pEndPoint[i] > l.m_pEndPoint[i] + std::numeric_limits<double>::epsilon()) return false;
111  }
112 
113  return true;
114 }
115 
116 //
117 // IObject interface
118 //
120 {
121  return new LineSegment(*this);
122 }
123 
124 //
125 // ISerializable interface
126 //
128 {
129  return (sizeof(uint32_t) + m_dimension * sizeof(double) * 2);
130 }
131 
132 void LineSegment::loadFromByteArray(const uint8_t* ptr)
133 {
134  uint32_t dimension;
135  memcpy(&dimension, ptr, sizeof(uint32_t));
136  ptr += sizeof(uint32_t);
137 
138  makeDimension(dimension);
139  memcpy(m_pStartPoint, ptr, m_dimension * sizeof(double));
140  ptr += m_dimension * sizeof(double);
141  memcpy(m_pEndPoint, ptr, m_dimension * sizeof(double));
142  //ptr += m_dimension * sizeof(double);
143 }
144 
145 void LineSegment::storeToByteArray(uint8_t** data, uint32_t& len)
146 {
147  len = getByteArraySize();
148  *data = new uint8_t[len];
149  uint8_t* ptr = *data;
150 
151  memcpy(ptr, &m_dimension, sizeof(uint32_t));
152  ptr += sizeof(uint32_t);
153  memcpy(ptr, m_pStartPoint, m_dimension * sizeof(double));
154  ptr += m_dimension * sizeof(double);
155  memcpy(ptr, m_pEndPoint, m_dimension * sizeof(double));
156  //ptr += m_dimension * sizeof(double);
157 }
158 
159 //
160 // IShape interface
161 //
163 {
164  const LineSegment* ps = dynamic_cast<const LineSegment*>(&s);
165  if (ps != nullptr) return intersectsLineSegment(*ps);
166 
167  const Region* pr = dynamic_cast<const Region*>(&s);
168  if (pr != nullptr) return intersectsRegion(*pr);
169 
171  "LineSegment::intersectsShape: Not implemented yet!"
172  );
173 }
174 
176 {
177  return false;
178 }
179 
181 {
183  "LineSegment::touchesShape: Not implemented yet!"
184  );
185 }
186 
188 {
189  double* coords = new double[m_dimension];
190  for (uint32_t cDim = 0; cDim < m_dimension; ++cDim)
191  {
192  coords[cDim] =
193  (std::abs(m_pStartPoint[cDim] - m_pEndPoint[cDim]) / 2.0) +
194  std::min(m_pStartPoint[cDim], m_pEndPoint[cDim]);
195  }
196 
197  out = Point(coords, m_dimension);
198  delete[] coords;
199 }
200 
202 {
203  return m_dimension;
204 }
205 
206 void LineSegment::getMBR(Region& out) const
207 {
208  double* low = new double[m_dimension];
209  double* high = new double[m_dimension];
210  for (uint32_t cDim = 0; cDim < m_dimension; ++cDim)
211  {
212  low[cDim] = std::min(m_pStartPoint[cDim], m_pEndPoint[cDim]);
213  high[cDim] = std::max(m_pStartPoint[cDim], m_pEndPoint[cDim]);
214  }
215 
216  out = Region(low, high, m_dimension);
217  delete[] low;
218  delete[] high;
219 }
220 
221 double LineSegment::getArea() const
222 {
223  return 0.0;
224 }
225 
227 {
228  const Point* ppt = dynamic_cast<const Point*>(&s);
229  if (ppt != nullptr)
230  {
231  return getMinimumDistance(*ppt);
232  }
233 
234 /*
235  const Region* pr = dynamic_cast<const Region*>(&s);
236  if (pr != 0)
237  {
238  return pr->getMinimumDistance(*this);
239  }
240 */
241 
243  "LineSegment::getMinimumDistance: Not implemented yet!"
244  );
245 }
246 
248 {
249  if (m_dimension == 1)
251  "LineSegment::getMinimumDistance: Use an Interval instead."
252  );
253 
254  if (m_dimension != 2)
256  "LineSegment::getMinimumDistance: Distance for high dimensional spaces not supported!"
257  );
258 
259  if (m_pEndPoint[0] >= m_pStartPoint[0] - std::numeric_limits<double>::epsilon() &&
260  m_pEndPoint[0] <= m_pStartPoint[0] + std::numeric_limits<double>::epsilon()) return std::abs(p.m_pCoords[0] - m_pStartPoint[0]);
261 
262  if (m_pEndPoint[1] >= m_pStartPoint[1] - std::numeric_limits<double>::epsilon() &&
263  m_pEndPoint[1] <= m_pStartPoint[1] + std::numeric_limits<double>::epsilon()) return std::abs(p.m_pCoords[1] - m_pStartPoint[1]);
264 
265  double x1 = m_pStartPoint[0];
266  double x2 = m_pEndPoint[0];
267  double x0 = p.m_pCoords[0];
268  double y1 = m_pStartPoint[1];
269  double y2 = m_pEndPoint[1];
270  double y0 = p.m_pCoords[1];
271 
272  return std::abs((x2 - x1) * (y1 - y0) - (x1 - x0) * (y2 - y1)) / (std::sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)));
273 }
274 
276 {
277  if (m_dimension != 2)
279  "LineSegment::intersectsRegion: only supported for 2 dimensions"
280  );
281 
282  if (m_dimension != r.m_dimension)
284  "LineSegment::intersectsRegion: LineSegment and Region have different number of dimensions."
285  );
286 
287  return r.intersectsLineSegment((*this));
288 }
289 
291 {
292  if (m_dimension != 2)
294  "LineSegment::intersectsLineSegment: only supported for 2 dimensions"
295  );
296 
297  if (m_dimension != l.m_dimension)
299  "LineSegment::intersectsLineSegment: LineSegments have different number of dimensions."
300  );
301 
302  // use Geometry::intersects
303  Point p1, p2, p3, p4;
304  p1 = Point(m_pStartPoint, 2);
305  p2 = Point(m_pEndPoint, 2);
306  p3 = Point(l.m_pStartPoint, 2);
307  p4 = Point(l.m_pEndPoint, 2);
308  return intersects(p1, p2, p3, p4);
309 }
310 
311 // assuming moving from start to end, positive distance is from right hand side.
313 {
314  if (m_dimension == 1)
316  "LineSegment::getRelativeMinimumDistance: Use an Interval instead."
317  );
318 
319  if (m_dimension != 2)
321  "LineSegment::getRelativeMinimumDistance: Distance for high dimensional spaces not supported!"
322  );
323 
324  if (m_pEndPoint[0] >= m_pStartPoint[0] - std::numeric_limits<double>::epsilon() &&
325  m_pEndPoint[0] <= m_pStartPoint[0] + std::numeric_limits<double>::epsilon())
326  {
327  if (m_pStartPoint[1] < m_pEndPoint[1]) return m_pStartPoint[0] - p.m_pCoords[0];
328  if (m_pStartPoint[1] >= m_pEndPoint[1]) return p.m_pCoords[0] - m_pStartPoint[0];
329  }
330 
331  if (m_pEndPoint[1] >= m_pStartPoint[1] - std::numeric_limits<double>::epsilon() &&
332  m_pEndPoint[1] <= m_pStartPoint[1] + std::numeric_limits<double>::epsilon())
333  {
334  if (m_pStartPoint[0] < m_pEndPoint[0]) return p.m_pCoords[1] - m_pStartPoint[1];
335  if (m_pStartPoint[0] >= m_pEndPoint[0]) return m_pStartPoint[1] - p.m_pCoords[1];
336  }
337 
338  double x1 = m_pStartPoint[0];
339  double x2 = m_pEndPoint[0];
340  double x0 = p.m_pCoords[0];
341  double y1 = m_pStartPoint[1];
342  double y2 = m_pEndPoint[1];
343  double y0 = p.m_pCoords[1];
344 
345  return ((x1 - x0) * (y2 - y1) - (x2 - x1) * (y1 - y0)) / (std::sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)));
346 }
347 
349 {
350  if (m_dimension == 1)
352  "LineSegment::getRelativeMaximumDistance: Use an Interval instead."
353  );
354 
355  if (m_dimension != 2)
357  "LineSegment::getRelativeMaximumDistance: Distance for high dimensional spaces not supported!"
358  );
359 
360  // clockwise.
361  double d1 = getRelativeMinimumDistance(Point(r.m_pLow, 2));
362 
363  double coords[2];
364  coords[0] = r.m_pLow[0];
365  coords[1] = r.m_pHigh[1];
366  double d2 = getRelativeMinimumDistance(Point(coords, 2));
367 
368  double d3 = getRelativeMinimumDistance(Point(r.m_pHigh, 2));
369 
370  coords[0] = r.m_pHigh[0];
371  coords[1] = r.m_pLow[1];
372  double d4 = getRelativeMinimumDistance(Point(coords, 2));
373 
374  return std::max(d1, std::max(d2, std::max(d3, d4)));
375 }
376 
378 {
379  if (m_dimension == 1)
381  "LineSegment::getAngleOfPerpendicularRay: Use an Interval instead."
382  );
383 
384  if (m_dimension != 2)
386  "LineSegment::getAngleOfPerpendicularRay: Distance for high dimensional spaces not supported!"
387  );
388 
389  if (m_pStartPoint[0] >= m_pEndPoint[0] - std::numeric_limits<double>::epsilon() &&
390  m_pStartPoint[0] <= m_pEndPoint[0] + std::numeric_limits<double>::epsilon()) return 0.0;
391 
392  if (m_pStartPoint[1] >= m_pEndPoint[1] - std::numeric_limits<double>::epsilon() &&
393  m_pStartPoint[1] <= m_pEndPoint[1] + std::numeric_limits<double>::epsilon()) return M_PI_2;
394 
395  return std::atan(-(m_pStartPoint[0] - m_pEndPoint[0]) / (m_pStartPoint[1] - m_pEndPoint[1]));
396 }
397 
398 void LineSegment::makeInfinite(uint32_t dimension)
399 {
400  makeDimension(dimension);
401  for (uint32_t cIndex = 0; cIndex < m_dimension; ++cIndex)
402  {
403  m_pStartPoint[cIndex] = std::numeric_limits<double>::max();
404  m_pEndPoint[cIndex] = std::numeric_limits<double>::max();
405  }
406 }
407 
408 void LineSegment::makeDimension(uint32_t dimension)
409 {
410  if (m_dimension != dimension)
411  {
412  delete[] m_pStartPoint;
413  delete[] m_pEndPoint;
414 
415  // remember that this is not a constructor. The object will be destructed normally if
416  // something goes wrong (bad_alloc), so we must take care not to leave the object at an intermediate state.
417  m_pStartPoint = nullptr;
418  m_pEndPoint = nullptr;
419 
420  m_dimension = dimension;
421  m_pStartPoint = new double[m_dimension];
422  m_pEndPoint = new double[m_dimension];
423  }
424 }
425 
426 // compute double the area of the triangle created by points a, b and c (only for 2 dimensional points)
428  double *pA, *pB, *pC;
429  pA = a.m_pCoords; pB = b.m_pCoords; pC = c.m_pCoords;
430  return (((pB[0] - pA[0]) * (pC[1] - pA[1])) - ((pC[0] - pA[0]) * (pB[1] - pA[1])));
431 }
432 
433 // determine whether point c is to the left of the segment comprised of points a & b (2-d only)
435  return (doubleAreaTriangle(a, b, c) > 0);
436 }
437 
438 // determine whether all 3 points are on the same line
440  return (doubleAreaTriangle(a, b, c) == 0);
441 }
442 
443 // determine whether the segment comprised of a, b and segment of c, d intersect (exclusive of their endpoints..hence the "Proper")
445  if ( collinear(a, b, c) || collinear(a, b, d) ||
446  collinear(c, d, a) || collinear(c, d, b)) {
447  return false;
448  }
449  return ((leftOf(a, b, c) ^ leftOf(a, b, d)) &&
450  (leftOf(c, d, a) ^ leftOf(c, d, b)));
451 }
452 
453 // if the points are collinear, is c between a & b
455  if ( !collinear(a, b, c) ) {
456  return false;
457  }
458  double *pA, *pB, *pC;
459  pA = a.m_pCoords; pB = b.m_pCoords; pC = c.m_pCoords;
460  if ( pA[0] != pB[0] ) { // a & b are not on the same vertical, compare on x axis
461  return between(pA[0], pB[0], pC[0]);
462  } else { // a & b are a vertical segment, we need to compare on y axis
463  return between(pA[1], pB[1], pC[1]);
464  }
465 }
466 
467 bool LineSegment::between(double a, double b, double c) {
468  return ( ((a <= c) && (c <= b)) || ((a >= c) && (c >= b)) );
469 }
470 
471 // intersection test, including endpoints
473  if (intersectsProper(a, b, c, d)) {
474  return true;
475  }
476  else if ( between(a, b, c) || between(a, b, d) ||
477  between(c, d, a) || between(c, d, b) ) {
478  return true;
479  }
480  else {
481  return false;
482  }
483 }
484 
485 std::ostream& SpatialIndex::operator<<(std::ostream& os, const LineSegment& l)
486 {
487  for (uint32_t cDim = 0; cDim < l.m_dimension; ++cDim)
488  {
489  os << l.m_pStartPoint[cDim] << ", " << l.m_pEndPoint[cDim] << " ";
490  }
491 
492  return os;
493 }
bool touchesShape(const IShape &in) const override
Definition: LineSegment.cc:180
uint32_t getDimension() const override
Definition: LineSegment.cc:201
static bool collinear(const Point &a, const Point &b, const Point &c)
Definition: LineSegment.cc:439
double getArea() const override
Definition: LineSegment.cc:221
void storeToByteArray(uint8_t **data, uint32_t &length) override
Definition: LineSegment.cc:145
virtual double getRelativeMaximumDistance(const Region &r) const
Definition: LineSegment.cc:348
virtual bool operator==(const LineSegment &p) const
Definition: LineSegment.cc:95
virtual bool intersectsLineSegment(const LineSegment &l) const
Definition: LineSegment.cc:290
static double doubleAreaTriangle(const Point &a, const Point &b, const Point &c)
Definition: LineSegment.cc:427
double * m_pLow
Definition: Region.h:98
static bool leftOf(const Point &a, const Point &b, const Point &c)
Definition: LineSegment.cc:434
void getCenter(Point &out) const override
Definition: LineSegment.cc:187
virtual bool intersectsRegion(const Region &p) const
Definition: LineSegment.cc:275
uint32_t m_dimension
Definition: Point.h:77
LineSegment * clone() override
Definition: LineSegment.cc:119
virtual void makeInfinite(uint32_t dimension)
Definition: LineSegment.cc:398
uint32_t m_dimension
Definition: Region.h:97
bool intersectsShape(const IShape &in) const override
Definition: LineSegment.cc:162
double * m_pHigh
Definition: Region.h:99
void loadFromByteArray(const uint8_t *data) override
Definition: LineSegment.cc:132
SIDX_DLL std::ostream & operator<<(std::ostream &os, const Ball &ball)
Definition: Ball.cc:215
double getMinimumDistance(const IShape &in) const override
Definition: LineSegment.cc:226
#define M_PI_2
Definition: SpatialIndex.h:33
static bool intersectsProper(const Point &a, const Point &b, const Point &c, const Point &d)
Definition: LineSegment.cc:444
static bool intersects(const Point &a, const Point &b, const Point &c, const Point &d)
Definition: LineSegment.cc:472
void getMBR(Region &out) const override
Definition: LineSegment.cc:206
virtual double getRelativeMinimumDistance(const Point &p) const
Definition: LineSegment.cc:312
virtual void makeDimension(uint32_t dimension)
Definition: LineSegment.cc:408
virtual LineSegment & operator=(const LineSegment &p)
Definition: LineSegment.cc:83
virtual double getAngleOfPerpendicularRay()
Definition: LineSegment.cc:377
bool containsShape(const IShape &in) const override
Definition: LineSegment.cc:175
double * m_pCoords
Definition: Point.h:78
static bool between(const Point &a, const Point &b, const Point &c)
Definition: LineSegment.cc:454
uint32_t getByteArraySize() override
Definition: LineSegment.cc:127
virtual bool intersectsLineSegment(const LineSegment &in) const
Definition: Region.cc:340