NDEVR
API Documentation
PolyLine.hpp
1/*--------------------------------------------------------------------------------------------
2Copyright (c) 2019, NDEVR LLC
3tyler.parke@ndevr.org
4 __ __ ____ _____ __ __ _______
5 | \ | | | __ \ | ___|\ \ / / | __ \
6 | \ | | | | \ \ | |___ \ \ / / | |__) |
7 | . \| | | |__/ / | |___ \ V / | _ /
8 | |\ |_|_____/__|_____|___\_/____| | \ \
9 |__| \__________________________________| \__\
10
11Subject to the terms of the Enterprise+ Agreement, NDEVR hereby grants
12Licensee a limited, non-exclusive, non-transferable, royalty-free license
13(without the right to sublicense) to use the API solely for the purpose of
14Licensee's internal development efforts to develop applications for which
15the API was provided.
16
17The above copyright notice and this permission notice shall be included in all
18copies or substantial portions of the Software.
19
20THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
21INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
22PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
23FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
24OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25DEALINGS IN THE SOFTWARE.
26
27Library: Base
28File: PolyLine
29Included in API: True
30Author(s): Tyler Parke
31 *-----------------------------------------------------------------------------------------**/
32#pragma once
33#include <NDEVR/BaseValues.h>
34#include <NDEVR/Buffer.h>
35#include <NDEVR/Vertex.h>
36#include <NDEVR/Bounds.h>
37#include <NDEVR/LineSegment.h>
38
39namespace NDEVR
40{
41
53 template<uint01 t_dims, class t_type, class t_vertex = Vertex<t_dims, t_type>>
54 class Polyline
55 {
56 public:
57 typedef AlocatingAlignedBuffer<Polyline<t_dims, t_type, t_vertex>, 32> PolyLineBuffer;
58 Polyline(uint04 size = 0)
59 : m_vertices(size)
60 , m_cache_bounds(Constant<Bounds<t_dims, t_type>>::Invalid)
61 , m_cache_length(Constant<t_type>::Invalid)
62 {}
63 explicit Polyline(Buffer<t_vertex>& vertices)
64 : m_vertices(vertices)
65 , m_cache_bounds(Constant<Bounds<t_dims, t_type>>::Invalid)
66 , m_cache_length(Constant<t_type>::Invalid)
67 {}
68 Polyline(const Polyline& polygon) noexcept
69 : m_vertices(polygon.m_vertices)
70 , m_cache_bounds(polygon.m_cache_bounds)
71 , m_cache_length(polygon.m_cache_length)
72 {}
73 Polyline(Polyline&& polygon) noexcept
74 : m_vertices()
75 , m_cache_bounds()
76 , m_cache_length()
77 {
78 std::swap(m_vertices, polygon.m_vertices);
79 std::swap(m_cache_bounds, polygon.m_cache_bounds);
80 std::swap(m_cache_length, polygon.m_cache_length);
81 }
82
92
94 {
95 if (IsInvalid(m_cache_bounds))
96 {
97 m_cache_bounds = Constant<Bounds<t_dims, t_type, t_vertex>>::Min;
98 for (uint04 i = 0; i < m_vertices.size(); i++)
99 {
100 m_cache_bounds.addToBounds(m_vertices[i]);
101 }
102 }
103 return m_cache_bounds;
104 }
105
106 decltype(auto) begin()
107 {
108 return m_vertices.begin();
109 }
110 decltype(auto) begin() const
111 {
112 return m_vertices.begin();
113 }
114
115 decltype(auto) begin(uint04 index) const
116 {
117 return m_vertices.begin(index);
118 }
119 decltype(auto) begin(uint04 index)
120 {
121 return m_vertices.begin(index);
122 }
123
124 decltype(auto) end()
125 {
126 return m_vertices.end();
127 }
128 decltype(auto) end() const
129 {
130 return m_vertices.end();
131 }
132
145
146 const t_vertex& vertex(uint04 index) const
147 {
148 return m_vertices[index];
149 }
150
163
168
178
179 inline const Buffer<t_vertex>& vertices() const
180 {
181 return m_vertices;
182 }
183
193
194 inline uint04 vertexCount() const
195 {
196 return m_vertices.size();
197 }
198
208
209 inline uint04 segmentCount() const
210 {
211 if (m_vertices.size() == 0)
212 return 0;
213 else
214 return m_vertices.size() - 1;
215 }
216
227
229 {
230 updateVertices(vertices);
231 }
232
243
244 void add(const t_vertex& vertex)
245 {
246 m_vertices.add(vertex);
247 invalidateCache();
248 }
249 void addAndSimplify(const t_vertex& vertex)
250 {
251 addAndSimplify(vertexCount(), vertex);
252 }
253
265
266 void add(uint04 index, const t_vertex& vertex)
267 {
268 m_vertices.add(index, vertex);
269 invalidateCache();
270 }
271 void addAndSimplify(uint04 index, const t_vertex& vertex)
272 {
273 if (IsInvalid(vertex))
274 {
275 if (index < vertexCount() && IsInvalid(this->vertex(index)))
276 return;//nothing to do
277 if (index > 1 && IsInvalid(this->vertex(index - 1)))
278 return;//nothing to do
279 m_vertices.add(index, vertex);
280 invalidateCache();
281 }
282 else
283 {
284 m_vertices.add(index, vertex);
285 if (index < vertexCount() - 1)
286 {
287 if (segment(index).template isParallel<t_type>(segment(index - 1), 0.00001))
288 {
289 remove(index);
290 }
291 }
292 if (index > 2)
293 {
294 if (segment(index - 1).template isParallel<t_type>(segment(index - 2), 0.00001))
295 {
296 remove(index - 1);
297 }
298 }
299 }
300 invalidateCache();
301 }
302
314
315 void replace(uint04 index, const t_vertex& vertex)
316 {
317 if (m_vertices[index] != vertex)
318 {
319 m_vertices[index] = vertex;
320 invalidateCache();
321 }
322 }
323
334
335 void remove(uint04 index)
336 {
337 m_vertices.removeIndex(index);
338 invalidateCache();
339 }
340
350 const t_vertex& lastVertex() const
351 {
352 return m_vertices.last();
353 }
354
363 {
364 m_vertices.removeLast();
365 }
366
373 void simplify()
374 {
375 if (vertexCount() <= 1)
376 return;
377 for (uint04 i = 0; i < vertexCount() - 1; i++)
378 {
379 if (vertex(i) == vertex(i + 1))
380 {
381 remove(i + 1);
382 i--;
383 }
384 else if (i > 0 && segment(i).template isParallel<t_type>(segment(i - 1), 0.00001))
385 {
386 remove(i);
387 i--;
388 }
389 }
390 }
391
398
399 void clear()
400 {
401 m_vertices.clear();
402 invalidateCache();
403 }
404
414 template<uint01 t_new_dims, class t_new_type, class t_new_vertex_type = Vertex<t_new_dims, t_new_type>>
415 Polyline<t_dims, t_new_type, t_new_vertex_type> as() const
416 {
417 Polyline<t_dims, t_new_type, t_new_vertex_type> poly;
418 for (uint04 i = 0; i < vertexCount(); i++)
419 {
420 poly.add(t_new_vertex_type(vertex(i).template as<t_dims, t_new_type, t_new_vertex_type>()));
421 }
422 return poly;
423 }
424
434 template<class t_precision>
435 t_precision length() const
436 {
437 if (IsInvalid(m_cache_length))
438 {
439 if (vertexCount() <= 1)
440 {
441 m_cache_length = cast<t_precision>(0);
442 }
443 else
444 {
445 m_cache_length = cast<t_precision>(0);
446 for (uint04 i = 0; i < segmentCount(); i++)
447 {
448 if(IsValid(segment(i)))
449 m_cache_length += segment(i).template length<t_precision>();
450 }
451 }
452 }
453 return m_cache_length;
454 }
455
456
469 template<class t_inter_type>
470 constexpr inline t_vertex pointAt(t_inter_type value) const
471 {
472 uint04 index = cast<uint04>(value);
473 return segment(index).pointAt(value - cast<t_inter_type>(index));
474 }
475
476 constexpr inline t_vertex pointAtLength(fltp08 value) const
477 {
478 if (vertexCount() == 0)
479 return Constant<t_vertex>::Invalid;
480 fltp08 accumulated_distance = 0;
481 for (uint04 i = 1; i < vertexCount(); i++)
482 {
483 fltp08 local_distance = distance<fltp08>(vertex(i - 1), vertex(i));
484 if (accumulated_distance + local_distance > value)
485 {
486 fltp08 line_percent = clip((value - accumulated_distance) / local_distance, 0.0, 1.0);
487 return (vertex(i - 1).template as<t_dims, fltp08>() * (1.0 - line_percent)
488 + vertex(i).template as<t_dims, fltp08>() * line_percent).template as<t_dims, t_type>();
489
490 }
491 accumulated_distance += local_distance;
492 }
493 return lastVertex();
494 }
495
508
509 bool operator==(const Polyline& polygon) const
510 {
511 return (m_vertices == polygon.m_vertices);
512 }
513 Polyline& operator=(const Polyline& polygon)
514 {
515 m_vertices = polygon.m_vertices;
516 m_cache_length = polygon.m_cache_length;
517 m_cache_bounds = polygon.m_cache_bounds;
518 return *this;
519 }
520 Polyline& operator=(Polyline&& polygon) noexcept
521 {
522 std::swap(m_vertices, polygon.m_vertices);
523 m_cache_length = polygon.m_cache_length;
524 m_cache_bounds = polygon.m_cache_bounds;
525 return *this;
526 }
527 Polyline<t_dims, t_type> breakIntoSegmentsByLength(t_type length) const
528 {
529 Polyline<t_dims, t_type> new_poly;
530 if (vertexCount() == 0)
531 return new_poly;
532
533 t_type remainder_distance(0);
534 new_poly.add(vertex(0));//add initial vertex
535 for (uint04 i = 0; i < segmentCount(); i++)
536 {
537 const t_type seg_length = segment(i).template length<t_type>();
538 if (seg_length + remainder_distance > length)
539 {
540 t_type local_accumulation = length - remainder_distance;
541 lib_assert(local_accumulation > 0.0, "Bad accumulation");
542 auto ray = segment(i).ray().template normalized<fltp08>();
543 do
544 {
545 new_poly.add(segment(i).vertex(A) + ray * local_accumulation);
546 local_accumulation += length;
547 } while (local_accumulation < seg_length);
548 remainder_distance = length + seg_length - local_accumulation;
549 }
550 else if (IsValid(seg_length))
551 {
552 remainder_distance += seg_length;
553 }
554 }
555 return new_poly;
556 }
557 Polyline<t_dims, t_type> breakIntoSegmentsByDistance(t_type d) const
558 {
559 t_type dist_squared = d * d;
560 Polyline<t_dims, t_type> new_poly;
561 if (vertexCount() == 0)
562 return new_poly;
563 new_poly.add(vertex(0));
564 for (uint04 i = 1; i < vertexCount(); i++)
565 {
566 if (distanceSquared(new_poly.lastVertex(), vertex(i)) > dist_squared)
567 {
568
569 LineSegment<t_dims, t_type> seg(vertex(i - 1), vertex(i));
570 const Vector<t_dims, t_type> ab = seg.ray();
571 const Vector<t_dims, t_type> ap = new_poly.lastVertex() - seg[A];
572 Vector<t_dims, t_type> normal = ab.template normalized<t_type>();
573 if(ap != 0.0 && !equals(ab, ap, 0.000001) && !equals(ab, -ap, 0.000001))//check if collinear
574 {
575 //project P onto AB
576 //use triangle to solve for point on line d distance away from last vertex
577 t_type sum = (ab * ap).sum();
578 const t_type mag_squared = ab.magnitudeSquared();
579 sum /= mag_squared;
580 const Vector<t_dims, t_type> p0 = seg[A] + (ab * sum);
581 t_type new_distance = sqrt(dist_squared - distanceSquared(p0, new_poly.lastVertex()));
582 new_poly.add(new_distance * normal + p0);
583 }
584 //simplified when back on line
585 while (distanceSquared(new_poly.lastVertex(), vertex(i)) > dist_squared)
586 {
587 new_poly.add(d * normal + new_poly.lastVertex());
588 }
589 }
590 }
591 return new_poly;
592 }
593
594 PolyLineBuffer breakIntoPolylinesByLength(t_type max_distance) const
595 {
596 PolyLineBuffer new_polys;
597 if (vertexCount() == 0)
598 return new_polys;
599 Polyline<t_dims, t_type> new_poly;
600 new_poly.add(vertex(0));
601 t_type accumulated_distance = 0;
602 for (uint04 i = 1; i < vertexCount(); i++)
603 {
604 t_type local_distance = distance<t_type>(vertex(i - 1), vertex(i));
605 while (accumulated_distance + local_distance > max_distance)
606 {
607 auto direction = (vertex(i) - new_poly.lastVertex()).template normalized<t_type>();
608 t_type adjusted_distance = max_distance - accumulated_distance;
609
610 auto final_vertex = adjusted_distance * direction + new_poly.lastVertex();
611 new_poly.add(final_vertex);
612 new_polys.add(new_poly);
613
614 new_poly.clear();
615 new_poly.add(final_vertex);
616 local_distance = local_distance - adjusted_distance;
617 accumulated_distance = 0.0;
618
619 }
620 new_poly.add(vertex(i));
621 accumulated_distance += local_distance;
622 }
623 new_polys.add(new_poly);
624 return new_polys;
625 }
626
627 Polyline<t_dims, t_type> clipPolyline(const Bounds<t_vertex::NumberOfDimensions(), t_type>& bounds) const
628 {
629 if (bounds.contains<true>(this->bounds()))
630 return *this;
631 if (!bounds.intersects(this->bounds()))
632 return Polyline();
633 Polyline polyline(vertexCount());
634 for (uint04 ii = 0; ii < vertexCount() - 1; ++ii)
635 {
636 LineSegment<t_vertex::NumberOfDimensions(), t_type> line(vertex(ii), vertex(ii + 1));
637 line = intersection(bounds, line);
638 if (IsValid(line))
639 {
640 if (polyline.vertexCount() == 0 || polyline.lastVertex() != line.vertex(A))
641 polyline.add(line.vertex(A));
642 polyline.add(line.vertex(B));
643 }
644 }
645 polyline.simplify();
646 return polyline;
647 }
648 private:
649
657
658 inline void invalidateCache() const
659 {
660 m_cache_length = Constant<t_type>::Invalid;
661 m_cache_bounds = Constant<Bounds<t_dims, t_type>>::Invalid;
662 }
663
674
675 void updateVertices(const Buffer<t_vertex>& vertices)
676 {
677 m_vertices = vertices;
678 invalidateCache();
679 }
680 private:
682 Buffer<t_vertex> m_vertices;
683
691
692 mutable Bounds<t_dims, t_type, t_vertex> m_cache_bounds;
693
701
702 mutable t_type m_cache_length;
703 };
704}
A specification of upper and lower bounds in N-dimensions.
Definition Bounds.hpp:54
The equivelent of std::vector but with a bit more control.
Definition Buffer.hpp:58
Class: LineSegment.
Definition Line.hpp:52
A sequence of connected line segments defined by ordered vertices along a path.
Definition PolyLine.hpp:55
bool operator==(const Polyline &polygon) const
Definition PolyLine.hpp:509
uint04 vertexCount() const
Definition PolyLine.hpp:194
void setVertices(const Buffer< t_vertex > &vertices)
Definition PolyLine.hpp:228
void clear()
Definition PolyLine.hpp:399
LineSegment< t_dims, t_type, t_vertex > segment(uint04 index) const
Definition PolyLine.hpp:164
void simplify()
Definition PolyLine.hpp:373
void add(uint04 index, const t_vertex &vertex)
Definition PolyLine.hpp:266
const t_vertex & lastVertex() const
Definition PolyLine.hpp:350
void removeLastVertex()
Definition PolyLine.hpp:362
const t_vertex & vertex(uint04 index) const
Definition PolyLine.hpp:146
void replace(uint04 index, const t_vertex &vertex)
Definition PolyLine.hpp:315
uint04 segmentCount() const
Definition PolyLine.hpp:209
t_precision length() const
Definition PolyLine.hpp:435
void remove(uint04 index)
Definition PolyLine.hpp:335
void add(const t_vertex &vertex)
Definition PolyLine.hpp:244
Polyline< t_dims, t_new_type, t_new_vertex_type > as() const
Definition PolyLine.hpp:415
const Buffer< t_vertex > & vertices() const
Definition PolyLine.hpp:179
Bounds< t_dims, t_type, t_vertex > bounds() const
Definition PolyLine.hpp:93
constexpr t_vertex pointAt(t_inter_type value) const
Definition PolyLine.hpp:470
The primary namespace for the NDEVR SDK.
static constexpr bool IsValid(const Angle< t_type > &value)
Checks whether the given Angle holds a valid value.
Definition Angle.h:398
constexpr HSLColor Constant< HSLColor >::Invalid
The invalid HSLColor constant with all components set to invalid.
Definition HSLColor.h:264
uint32_t uint04
-Defines an alias representing a 4 byte, unsigned integer -Can represent exact integer values 0 throu...
static Bounds< t_dims, t_type, t_vertex > intersection(const Bounds< t_dims, t_type, t_vertex > &bounds_1, const Bounds< t_dims, t_type, t_vertex > &bounds_2)
Given two bounds, returns the intersection between the two of them.
double fltp08
Defines an alias representing an 8 byte floating-point number.
static constexpr bool IsInvalid(const Angle< t_type > &value)
Checks whether the given Angle holds an invalid value.
Definition Angle.h:388
t_type sqrt(const t_type &value)
constexpr bool equals(const LineSegment< t_dims, t_type, t_vertex > &left, const LineSegment< t_dims, t_type, t_vertex > &right, const t_type &epsilon=cast< t_type >(0))
Tests if objects are considered equal.
Definition Line.hpp:791
constexpr t_type clip(const t_type &value, const t_type &lower_bound, const t_type &upper_bound)
Clips the value given so that that the returned value falls between upper and lower bound.
constexpr t_to cast(const Angle< t_from > &value)
Casts an Angle from one backing type to another.
Definition Angle.h:408
Defines for a given type (such as sint04, fltp08, UUID, etc) a maximum, minimum, and reserved 'invali...