1 /**********************************************************************
2  *
3  * GEOS - Geometry Engine Open Source
4  * http://geos.osgeo.org
5  *
6  * Copyright (C) 2020 Paul Ramsey <pramsey@cleverelephant.ca>
7  *
8  * This is free software; you can redistribute and/or modify it under
9  * the terms of the GNU Lesser General Public Licence as published
10  * by the Free Software Foundation.
11  * See the COPYING file for more information.
12  *
13  **********************************************************************/
14 
15 #include <geos/operation/overlayng/LineLimiter.h>
16 #include <geos/geom/CoordinateSequence.h>
17 #include <geos/geom/Envelope.h>
18 #include <geos/geom/Coordinate.h>
19 
20 #include <algorithm>
21 
22 namespace geos {      // geos
23 namespace operation { // geos.operation
24 namespace overlayng { // geos.operation.overlayng
25 
26 /*public*/
27 std::vector<std::unique_ptr<CoordinateArraySequence>>&
limit(const CoordinateSequence * pts)28 LineLimiter::limit(const CoordinateSequence *pts)
29 {
30     // Reset for new limit run
31     lastOutside = nullptr;
32     ptList.reset(nullptr);
33     sections.clear();
34 
35     for (std::size_t i = 0; i < pts->size(); i++) {
36         const Coordinate* p = &(pts->getAt(i));
37         if (limitEnv->intersects(*p)) {
38             addPoint(p);
39         }
40         else {
41             addOutside(p);
42         }
43     }
44     // finish last section, if any
45     finishSection();
46     return sections;
47 }
48 
49 /*private*/
50 void
addPoint(const Coordinate * p)51 LineLimiter::addPoint(const Coordinate* p)
52 {
53     startSection();
54     ptList->emplace_back(*p);
55 }
56 
57 /*private*/
58 void
addOutside(const Coordinate * p)59 LineLimiter::addOutside(const Coordinate* p)
60 {
61     bool segIntersects = isLastSegmentIntersecting(p);
62     if (!segIntersects) {
63         finishSection();
64     }
65     else {
66         if(lastOutside != nullptr) {
67             addPoint(lastOutside);
68         }
69         addPoint(p);
70     }
71     lastOutside = p;
72 }
73 
74 /*private*/
75 bool
isLastSegmentIntersecting(const Coordinate * p)76 LineLimiter::isLastSegmentIntersecting(const Coordinate* p)
77 {
78     if (lastOutside == nullptr) {
79         // last point must have been inside
80         if (isSectionOpen())
81             return true;
82         return false;
83     }
84     return limitEnv->intersects(*lastOutside, *p);
85 }
86 
87 /*private*/
88 bool
isSectionOpen()89 LineLimiter::isSectionOpen()
90 {
91     return ptList != nullptr;
92 }
93 
94 /*private*/
95 void
startSection()96 LineLimiter::startSection()
97 {
98     if (!isSectionOpen()) {
99         ptList.reset(new std::vector<Coordinate>);
100     }
101 
102     if (lastOutside != nullptr) {
103         ptList->emplace_back(*lastOutside);
104     }
105     lastOutside = nullptr;
106 }
107 
108 /*private*/
109 void
finishSection()110 LineLimiter::finishSection()
111 {
112     if (!isSectionOpen())
113         return;
114 
115     // finish off this section
116     if (lastOutside != nullptr) {
117         ptList->emplace_back(*lastOutside);
118         lastOutside = nullptr;
119     }
120 
121     // remove repeated points from the section
122     ptList->erase(std::unique(ptList->begin(), ptList->end()), ptList->end());
123 
124     // std::unique_ptr<CoordinateArraySequence> cas();
125     CoordinateArraySequence* cas = new CoordinateArraySequence(ptList.release());
126     sections.emplace_back(cas);
127     ptList.reset(nullptr);
128 }
129 
130 
131 
132 
133 } // namespace geos.operation.overlayng
134 } // namespace geos.operation
135 } // namespace geos
136