File: isPolygonCCW.m

package info (click to toggle)
octave-geometry 4.1.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 720 kB
  • sloc: cpp: 5,358; python: 379; objc: 328; makefile: 25
file content (126 lines) | stat: -rw-r--r-- 4,230 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
## Copyright (C) 2016-2017 Juan Pablo Carbajal
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 3 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program. If not, see <http://www.gnu.org/licenses/>.

## -*- texinfo -*-
## @deftypefn {} {@var{ccw} =} isPolygonCCW (@var{p})
## @deftypefnx {} {@var{ccw} =} isPolygonCCW (@var{px}, @var{py})
## Returns true if the polygon @var{p} are oriented Counter-Clockwise.
##
## @var{p} is a N-by-2 array containing coordinates of vertices. The coordinates
## of the vertices of the polygon can also be given as two N-by-1 arrays
## @var{px}, @var{py}.
##
## Optional third argument @var{library} can be one of "geometry" or
## "clipper". In the latter case the potentially faster Clipper polygon
## library will be invoked to assess winding directions.  The default is
## "geometry".
##
## If any polygon is self-crossing, the result is undefined.
##
## If @var{p} is a cell, each element is considered a polygon, the
## resulting @var{cww} is a cell of the same size.
##
## @seealso{polygonArea}
## @end deftypefn

## Author: Juan Pablo Carbajal <ajuanpi+dev@gmail.com>

function ccw = isPolygonCCW (px, py=[], lib="geometry")

  ## case of wrong number of input arguments
  if (nargin > 3 || nargin < 1)
    print_usage ();
  endif

  if (ischar (py))
    py = lower (py);
    if (ismember (py, {"geometry", "clipper"}))
      lib = py;
      py = [];
    else
      error ('Octave:invalid-fun-call', "use of '%s' not implemented", lib);
    endif
  endif
  if (! isempty (py))
    if (! strcmp (typeinfo (px), typeinfo (py)))
      error ('Octave:invalid-input-arg', 'X and Y should be of the same type');
    endif
    if (any (size (px) != size (py)) )
      error ('Octave:invalid-input-arg', 'X and Y should be of the same size');
    endif
  endif

  if (iscell (px))
    ## Cell Array Format
    ## Call this function on each cell
    if (isempty (py))
      py = cell (size (px));
    endif

    ccw = cellfun (@(u,v) isPolygonCCW (u,v,lib), px, py, "unif", 0);

  else
    ## Input are matrices
    ## merge them to one
    px = [px py];
    if (isempty (px))
      error ("isPolygonCW: empty input polygon encountered");
    elseif (strcmpi (lib, "clipper"))
      ## Clipper can do all of them in one call
      ccw = logical (isPolygonCW_Clipper (px));
      return
    endif

    if (any (isnan (px(:))))
      ## Inputs are many polygons separated with NaN
      ## Split them and call this function on each of them
      px  = splitPolygons (px);
      ccw = cellfun (@(u) isPolygonCCW (u,[],lib), px);

    else ## Here do the actual work
      ccw = polygonArea (px) > 0;

    endif

  endif

endfunction

%!shared pccw, pcw, pxccw, pyccw, pxnan, pynan, pnan
%! pccw  = [0 0; 1 0; 1 1; 0 1];
%! pcw   = reversePolygon (pccw);
%! pxccw = pccw(:,1);
%! pyccw = pccw(:,2);
%! pxnan = [2; 2; 0; 0; NaN; 0; 0; 2];
%! pynan = [0; 2; 2; 0; NaN; 0; 3; 0];
%! pnan  = [pxnan pynan];

## Native testing
%!assert (isPolygonCCW (pccw));
%!assert (isPolygonCCW (pxccw, pyccw));
%!assert (isPolygonCCW ({pxccw, pxccw}, {pyccw, pyccw}), {true, true});
%!assert (~isPolygonCCW (pcw));
%!assert (isPolygonCCW ({pccw;pcw}), {true;false});
%!assert (isPolygonCCW(pnan), [true; false]);
%!assert (isPolygonCCW({pnan,pcw}),{[true;false], false});

## Clipper testing
%!assert (isPolygonCCW (pccw,[],"clipper"));
%!assert (isPolygonCCW (pxccw, pyccw,"clipper"));
%!assert (isPolygonCCW ({pxccw, pxccw}, {pyccw, pyccw},"clipper"), {true, true});
%!assert (~isPolygonCCW (pcw,[],"clipper"));
%!assert (isPolygonCCW ({pccw;pcw},[],"clipper"), {true;false});
%!xtest assert (isPolygonCCW(pnan,[],"clipper"), [true; false]);
%!xtest assert (isPolygonCCW({pnan,pcw},[],"clipper"),{[true; false], false});