Coverage Report

Created: 2025-11-08 06:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/geos/include/geos/operation/buffer/OffsetSegmentString.h
Line
Count
Source
1
/**********************************************************************
2
 *
3
 * GEOS - Geometry Engine Open Source
4
 * http://geos.osgeo.org
5
 *
6
 * Copyright (C) 2011 Sandro Santilli <strk@kbt.io>
7
 * Copyright (C) 2007 Refractions Research Inc.
8
 *
9
 * This is free software; you can redistribute and/or modify it under
10
 * the terms of the GNU Lesser General Public Licence as published
11
 * by the Free Software Foundation.
12
 * See the COPYING file for more information.
13
 *
14
 **********************************************************************
15
 *
16
 * Last port: operation/buffer/OffsetSegmentString.java r378 (JTS-1.12)
17
 *
18
 **********************************************************************/
19
20
#pragma once
21
22
#include <geos/geom/Coordinate.h> // for inlines
23
#include <geos/geom/CoordinateSequence.h> // for inlines
24
#include <geos/geom/PrecisionModel.h> // for inlines
25
26
#include <vector>
27
#include <memory>
28
#include <cassert>
29
30
namespace geos {
31
namespace operation { // geos.operation
32
namespace buffer { // geos.operation.buffer
33
34
/// A dynamic list of the vertices in a constructed offset curve.
35
///
36
/// Automatically removes close vertices
37
/// which are closer than a given tolerance.
38
///
39
/// @author Martin Davis
40
///
41
class OffsetSegmentString {
42
43
private:
44
45
    geom::CoordinateSequence* ptList;
46
47
    const geom::PrecisionModel* precisionModel;
48
49
    /** \brief
50
     * The distance below which two adjacent points on the curve
51
     * are considered to be coincident.
52
     *
53
     * This is chosen to be a small fraction of the offset distance.
54
     */
55
    double minimumVertexDistance;
56
57
    /** \brief
58
     * Tests whether the given point is redundant relative to the previous
59
     * point in the list (up to tolerance)
60
     *
61
     * @param pt
62
     * @return true if the point is redundant
63
     */
64
    bool
65
    isRedundant(const geom::Coordinate& pt) const
66
0
    {
67
0
        if(ptList->size() < 1) {
68
0
            return false;
69
0
        }
70
0
        const geom::Coordinate& lastPt = ptList->back();
71
0
        double ptDist = pt.distance(lastPt);
72
0
        if(ptDist < minimumVertexDistance) {
73
0
            return true;
74
0
        }
75
0
        return false;
76
0
    }
77
78
    OffsetSegmentString(const OffsetSegmentString&) = delete;
79
    OffsetSegmentString& operator=(const OffsetSegmentString&) = delete;
80
81
public:
82
83
    friend std::ostream& operator<< (std::ostream& os, const OffsetSegmentString& node);
84
85
    OffsetSegmentString()
86
        :
87
0
        ptList(new geom::CoordinateSequence()),
88
0
        precisionModel(nullptr),
89
0
        minimumVertexDistance(0.0)
90
0
    {
91
0
    }
92
93
    ~OffsetSegmentString()
94
0
    {
95
0
        delete ptList;
96
0
    }
97
98
    void
99
    reset()
100
0
    {
101
0
        if(ptList) {
102
0
            ptList->clear();
103
0
        }
104
0
        else {
105
0
            ptList = new geom::CoordinateSequence();
106
0
        }
107
108
0
        precisionModel = nullptr;
109
0
        minimumVertexDistance = 0.0;
110
0
    }
111
112
    void
113
    setPrecisionModel(const geom::PrecisionModel* nPrecisionModel)
114
0
    {
115
0
        precisionModel = nPrecisionModel;
116
0
    }
117
118
    void
119
    setMinimumVertexDistance(double nMinVertexDistance)
120
0
    {
121
0
        minimumVertexDistance = nMinVertexDistance;
122
0
    }
123
124
    void
125
    addPt(const geom::Coordinate& pt)
126
0
    {
127
0
        assert(precisionModel);
128
129
0
        geom::Coordinate bufPt = pt;
130
0
        precisionModel->makePrecise(bufPt);
131
        // don't add duplicate (or near-duplicate) points
132
0
        if(isRedundant(bufPt)) {
133
0
            return;
134
0
        }
135
        // we ask to allow repeated as we checked this ourself
136
        // (JTS uses a vector for ptList, not a CoordinateSequence,
137
        // we should do the same)
138
0
        ptList->add(bufPt, true);
139
0
    }
140
141
    void
142
    addPts(const geom::CoordinateSequence& pts, bool isForward)
143
0
    {
144
0
        if(isForward) {
145
0
            for(std::size_t i = 0, n = pts.size(); i < n; ++i) {
146
0
                addPt(pts[i]);
147
0
            }
148
0
        }
149
0
        else {
150
0
            for(std::size_t i = pts.size(); i > 0; --i) {
151
0
                addPt(pts[i - 1]);
152
0
            }
153
0
        }
154
0
    }
155
156
    /// Check that points are a ring
157
    ///
158
    /// add the startpoint again if they are not
159
    void
160
    closeRing()
161
0
    {
162
0
        if(ptList->size() < 1) {
163
0
            return;
164
0
        }
165
0
        const geom::Coordinate& startPt = ptList->front();
166
0
        const geom::Coordinate& lastPt = ptList->back();
167
0
        if(startPt.equals(lastPt)) {
168
0
            return;
169
0
        }
170
        // we ask to allow repeated as we checked this ourself
171
0
        ptList->add(startPt, true);
172
0
    }
173
174
    /// Get coordinates by taking ownership of them
175
    ///
176
    /// After this call, the coordinates reference in
177
    /// this object are dropped. Calling twice will
178
    /// segfault...
179
    ///
180
    /// FIXME: refactor memory management of this
181
    ///
182
    geom::CoordinateSequence*
183
    getCoordinates()
184
0
    {
185
0
        closeRing();
186
0
        geom::CoordinateSequence* ret = ptList;
187
0
        ptList = nullptr;
188
0
        return ret;
189
0
    }
190
191
    inline size_t
192
    size() const
193
0
    {
194
0
        return ptList ? ptList->size() : 0 ;
195
0
    }
196
197
};
198
199
inline std::ostream&
200
operator<< (std::ostream& os,
201
            const OffsetSegmentString& lst)
202
0
{
203
0
    if(lst.ptList) {
204
0
        os << *(lst.ptList);
205
0
    }
206
0
    else {
207
0
        os << "empty (consumed?)";
208
0
    }
209
0
    return os;
210
0
}
211
212
} // namespace geos.operation.buffer
213
} // namespace geos.operation
214
} // namespace geos
215