Coverage Report

Created: 2026-08-13 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/geos/src/operation/overlayng/RingClipper.cpp
Line
Count
Source
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/RingClipper.h>
16
17
using geos::geom::CoordinateSequence;
18
using geos::geom::CoordinateXYZM;
19
20
namespace geos {      // geos
21
namespace operation { // geos.operation
22
namespace overlayng { // geos.operation.overlayng
23
24
25
/*public*/
26
std::unique_ptr<CoordinateSequence>
27
RingClipper::clip(const CoordinateSequence* cs) const
28
0
{
29
0
    std::unique_ptr<CoordinateSequence> pts;
30
0
    for (int edgeIndex = 0; edgeIndex < 4; edgeIndex++) {
31
0
        bool closeRing = (edgeIndex == 3);
32
0
        pts = clipToBoxEdge(cs, edgeIndex, closeRing);
33
0
        if (pts->size() == 0)
34
0
            return pts;
35
0
        cs = pts.get();
36
0
    }
37
0
    return pts;
38
0
}
39
40
/*private*/
41
std::unique_ptr<CoordinateSequence>
42
RingClipper::clipToBoxEdge(const CoordinateSequence* pts, int edgeIndex, bool closeRing) const
43
0
{
44
    // TODO: is it possible to avoid copying array 4 times?
45
0
    auto ptsClip = std::make_unique<CoordinateSequence>(0, pts->hasZ(), pts->hasM());
46
47
0
    CoordinateXYZM p0;
48
0
    pts->getAt(pts->size() - 1, p0);
49
0
    for (std::size_t i = 0; i < pts->size(); i++) {
50
0
        CoordinateXYZM p1;
51
0
        pts->getAt(i, p1);
52
0
        if (isInsideEdge(p1, edgeIndex)) {
53
0
            if (!isInsideEdge(p0, edgeIndex)) {
54
0
                CoordinateXY intPt;
55
0
                intersection(p0, p1, edgeIndex, intPt);
56
0
                ptsClip->add(intPt, false);
57
0
            }
58
            // TODO: avoid copying so much?
59
0
            ptsClip->add(p1, false);
60
61
0
        }
62
0
        else if (isInsideEdge(p0, edgeIndex)) {
63
0
            CoordinateXY intPt;
64
0
            intersection(p0, p1, edgeIndex, intPt);
65
0
            ptsClip->add(intPt, false);
66
0
        }
67
68
        // else p0-p1 is outside box, so it is dropped
69
0
        p0 = p1;
70
0
    }
71
72
    // add closing point if required
73
0
    if (closeRing) {
74
0
        ptsClip->closeRing();
75
0
    }
76
77
0
    return ptsClip;
78
0
}
79
80
/*private*/
81
void
82
RingClipper::intersection(const CoordinateXY& a, const CoordinateXY& b, int edgeIndex, CoordinateXY& rsltPt) const
83
0
{
84
0
    switch (edgeIndex) {
85
0
    case BOX_BOTTOM:
86
0
        rsltPt = CoordinateXY(intersectionLineY(a, b, clipEnv.getMinY()), clipEnv.getMinY());
87
0
        break;
88
0
    case BOX_RIGHT:
89
0
        rsltPt = CoordinateXY(clipEnv.getMaxX(), intersectionLineX(a, b, clipEnv.getMaxX()));
90
0
        break;
91
0
    case BOX_TOP:
92
0
        rsltPt = CoordinateXY(intersectionLineY(a, b, clipEnv.getMaxY()), clipEnv.getMaxY());
93
0
        break;
94
0
    case BOX_LEFT:
95
0
    default:
96
0
        rsltPt = Coordinate(clipEnv.getMinX(), intersectionLineX(a, b, clipEnv.getMinX()));
97
0
    }
98
0
    return;
99
0
}
100
101
/*private*/
102
double
103
RingClipper::intersectionLineY(const CoordinateXY& a, const CoordinateXY& b, double y)
104
0
{
105
0
    double m = (b.x - a.x) / (b.y - a.y);
106
0
    double intercept = (y - a.y) * m;
107
0
    return a.x + intercept;
108
0
}
109
110
/*private*/
111
double
112
RingClipper::intersectionLineX(const CoordinateXY& a, const CoordinateXY& b, double x)
113
0
{
114
0
    double m = (b.y - a.y) / (b.x - a.x);
115
0
    double intercept = (x - a.x) * m;
116
0
    return a.y + intercept;
117
0
}
118
119
/*private*/
120
bool
121
RingClipper::isInsideEdge(const CoordinateXY& p, int edgeIndex) const
122
0
{
123
0
    if (clipEnv.isNull()) {
124
0
        return false;
125
0
    }
126
127
0
    bool isInside = false;
128
0
    switch (edgeIndex) {
129
0
    case BOX_BOTTOM: // bottom
130
0
        isInside = p.y > clipEnv.getMinY();
131
0
        break;
132
0
    case BOX_RIGHT: // right
133
0
        isInside = p.x < clipEnv.getMaxX();
134
0
        break;
135
0
    case BOX_TOP: // top
136
0
        isInside = p.y < clipEnv.getMaxY();
137
0
        break;
138
0
    case BOX_LEFT:
139
0
    default: // left
140
0
        isInside = p.x > clipEnv.getMinX();
141
0
    }
142
0
    return isInside;
143
0
}
144
145
146
147
} // namespace geos.operation.overlayng
148
} // namespace geos.operation
149
} // namespace geos