Coverage Report

Created: 2025-10-10 06:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/geos/src/algorithm/Orientation.cpp
Line
Count
Source
1
/**********************************************************************
2
 *
3
 * GEOS - Geometry Engine Open Source
4
 * http://geos.osgeo.org
5
 *
6
 * Copyright (C) 2018 Paul Ramsey <pramsey@cleverlephant.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
 * Last port: algorithm/Orientation.java @ 2017-09-04
16
 *
17
 **********************************************************************/
18
19
#include <cassert>
20
#include <cmath>
21
#include <vector>
22
23
#include <geos/algorithm/Area.h>
24
#include <geos/algorithm/Orientation.h>
25
#include <geos/algorithm/CGAlgorithmsDD.h>
26
#include <geos/geom/CoordinateSequence.h>
27
#include <geos/geom/Coordinate.h>
28
#include <geos/util/IllegalArgumentException.h>
29
30
using geos::geom::CoordinateXY;
31
32
namespace geos {
33
namespace algorithm { // geos.algorithm
34
35
/* public static */
36
// inlining this method worsened performance slightly
37
int
38
Orientation::index(const geom::CoordinateXY& p1, const geom::CoordinateXY& p2,
39
                   const geom::CoordinateXY& q)
40
1.08G
{
41
1.08G
    return CGAlgorithmsDD::orientationIndex(p1, p2, q);
42
1.08G
}
43
44
45
/* public static */
46
bool
47
Orientation::isCCW(const geom::CoordinateSequence* ring)
48
820k
{
49
    // # of points without closing endpoint
50
820k
    int inPts = static_cast<int>(ring->size()) - 1;
51
    // sanity check
52
820k
    if (inPts < 3)
53
20.9k
        return false;
54
55
799k
    uint32_t nPts = static_cast<uint32_t>(inPts);
56
    /**
57
     * Find first highest point after a lower point, if one exists
58
     * (e.g. a rising segment)
59
     * If one does not exist, hiIndex will remain 0
60
     * and the ring must be flat.
61
     * Note this relies on the convention that
62
     * rings have the same start and end point.
63
     */
64
799k
    const CoordinateXY* upHiPt = &ring->getAt<CoordinateXY>(0);
65
799k
    const CoordinateXY* upLowPt = &CoordinateXY::getNull();
66
67
799k
    double prevY = upHiPt->y;
68
799k
    uint32_t iUpHi = 0;
69
26.3M
    for (uint32_t i = 1; i <= nPts; i++) {
70
25.5M
        double py = ring->getY(i);
71
        /**
72
        * If segment is upwards and endpoint is higher, record it
73
        */
74
25.5M
        if (py > prevY && py >= upHiPt->y) {
75
1.41M
            iUpHi = i;
76
1.41M
            upHiPt = &ring->getAt<CoordinateXY>(i);
77
1.41M
            upLowPt = &ring->getAt<CoordinateXY>(i-1);
78
1.41M
        }
79
25.5M
        prevY = py;
80
25.5M
    }
81
    /**
82
     * Check if ring is flat and return default value if so
83
     */
84
799k
    if (iUpHi == 0) return false;
85
86
    /**
87
     * Find the next lower point after the high point
88
     * (e.g. a falling segment).
89
     * This must exist since ring is not flat.
90
     */
91
797k
    uint32_t iDownLow = iUpHi;
92
827k
    do {
93
827k
        iDownLow = (iDownLow + 1) % nPts;
94
827k
    } while (iDownLow != iUpHi && ring->getY(iDownLow) == upHiPt->y );
95
96
797k
    const CoordinateXY& downLowPt = ring->getAt<CoordinateXY>(iDownLow);
97
797k
    uint32_t iDownHi = iDownLow > 0 ? iDownLow - 1 : nPts - 1;
98
797k
    const CoordinateXY& downHiPt = ring->getAt<CoordinateXY>(iDownHi);
99
100
    /**
101
     * Two cases can occur:
102
     * 1) the hiPt and the downPrevPt are the same.
103
     *    This is the general position case of a "pointed cap".
104
     *    The ring orientation is determined by the orientation of the cap
105
     * 2) The hiPt and the downPrevPt are different.
106
     *    In this case the top of the cap is flat.
107
     *    The ring orientation is given by the direction of the flat segment
108
     */
109
797k
    if (upHiPt->equals2D(downHiPt)) {
110
        /**
111
        * Check for the case where the cap has configuration A-B-A.
112
        * This can happen if the ring does not contain 3 distinct points
113
        * (including the case where the input array has fewer than 4 elements), or
114
        * it contains coincident line segments.
115
        */
116
756k
        if (upLowPt->equals2D(*upHiPt) || downLowPt.equals2D(*upHiPt) || upLowPt->equals2D(downLowPt))
117
30.9k
            return false;
118
119
        /**
120
        * It can happen that the top segments are coincident.
121
        * This is an invalid ring, which cannot be computed correctly.
122
        * In this case the orientation is 0, and the result is false.
123
        */
124
725k
        int orientationIndex = index(*upLowPt, *upHiPt, downLowPt);
125
725k
        return orientationIndex == COUNTERCLOCKWISE;
126
756k
    }
127
41.1k
    else {
128
        /**
129
        * Flat cap - direction of flat top determines orientation
130
        */
131
41.1k
        double delX = downHiPt.x - upHiPt->x;
132
41.1k
        return delX < 0;
133
41.1k
    }
134
797k
}
135
136
/* public static */
137
bool
138
Orientation::isCCWArea(const geom::CoordinateSequence* ring)
139
0
{
140
0
    return algorithm::Area::ofRingSigned(ring) < 0;
141
0
}
142
143
144
145
146
} // namespace geos.algorithm
147
} //namespace geos