summaryrefslogtreecommitdiff
path: root/inst/private/clippln.m
blob: 7d8a57578072609ea6147c9b0289d81a1a82b290 (plain)
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
## Copyright (C) 2014 Philip Nienhuis
## 
## 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 {Function File} [@var{val}, @var{npts}, @var{pprt}] = clipplg (@var{val}, @var{npts}, @var{pprt}, @var{sbox}, @var{styp})
## Undocumented internal function for clipping (poly)lines within a bounding
## box and interpolating M and Z-values.
##
## @seealso{}
## @end deftypefn

## Author: Philip Nienhuis <prnienhuis@users.sf.net>
## Created: 2014-12-01

function [val, tnpt, tnpr] = clippln (val, tnpt, tnpr, sbox, styp=3)

  ## Backup
  btnpr = tnpr;
  ## Prepare augmented npr array for easier indexing into subfeatures
  ttnpr = [tnpr tnpt];
  ctnpr = {};
  ## Initialize output array
  valt = [];

  ## First step is to check all polyline vertices whether they lie in sbox
  [aa, bb] = inpolygon (val(:, 1), val(:, 2), sbox(:, 1), sbox(:, 2));
  if (any (aa) || any (bb))
    ## So there are. Morph bounding box into something that OF geometry likes:
    ## Parametric representation of bounding box. "createLine" is in OF geometry
    ssbox = createLine (sbox(1:4, :), sbox (2:5, :)); 
## Create sbx from sbox ([minx max miny maxy])
sbx = [min(sbox(:, 1)), max(sbox(:, 1)), min(sbox(:, 2)), max(sbox(:, 2))];
    ## For those points ON the boundary no interpolation is needed.
    ## First treat those points requiring interpolation (1st inside,
    ## 2nd outside or vice versa)
    for ll=1:numel (tnpr)
      ## Start at end of polyline-part to avoid mixing up tnpr pointers
      ii = numel (tnpr) - ll + 1;
      nprt = [];
      ## Select polyline part
      valn = val(ttnpr(ii)+1:ttnpr(ii+1), :);
      a = aa(ttnpr(ii)+1:ttnpr(ii+1));
      if (any (a))
        b = bb(ttnpr(ii)+1:ttnpr(ii+1));
        da = diff (a);
        idx = find (da);
        ## Find out which bounding box segments have been crossed where
        ## 1. Parametric representation of line
        lines = createLine (valn(idx, 1:2), valn(idx+1, 1:2));
        ## 2. Compute intersections. clipLine & distancePoints are in OF geometry
        for jj=1:numel (idx)
          ## Start at end of polyline-part to avoid mixing up tnpr pointers
          kk = numel (idx) - jj + 1;
          ## Skip points on border
          if (! b(idx(kk)+1))
            intsc = reshape (clipLine (lines(kk, :), sbx), 2, 2)';
            dst = distancePoints (intsc, valn(idx(kk):idx(kk)+1, 1:2));
            ## If segment goes out, take nearest point to valn(idx(kk)+1)
            if (da(idx(kk)) < 1)
              [~, ix] = min (dst(:, 2));
            else
              [~, ix] = min (dst(:, 1));
            endif
            fac = dst(ix, 2) / sum (dst(2, :));
            intsc = intsc (ix, :);
            ## Insert new intersection point
            valn(idx(kk)+2:end+1, :) = valn(idx(kk)+1:end, :);
            valn(idx(kk)+1, 1:2) = intsc(1:2);
            if (styp > 3)
              valn(idx(kk)+1, 3:4) = valn(idx(kk)+2, 3:4) + fac * ...
                                     (valn(idx(kk), 3:4) - valn(idx(kk)+2, 3:4));
            else
              valn(idx(kk)+1, 3:4) = [NaN NaN];
            endif
            ## Also update a, b, da and idx. Also mark first point outside sbox
            aa(ttnpr(ii)+idx(kk):ttnpr(ii)+idx(kk)+1) = 1;
            a = [ a(1:idx(kk)); 1; a(idx(kk)+1:end) ];
            b = [ b(1:idx(kk)); 1; b(idx(kk)+1:end) ];
          endif
          if (da(idx(kk)) < 0)
            ## Segment leaves bbox. Replace first outer point with NaN row
            valn(idx(kk)+2, :) = NaN (1, 6);
            ## Be sure to include NaN row
            a(idx(kk)+2) = 1;
          endif
        endfor
        ## Update valt, ttnpr
        valn = valn(find(a), :);
        if (! isempty (valn))
          ## Remove last NaN row if present
          if (all (isnan (valn(end, 1:2))))
            valn(end,:) = [];
          endif
          isn = 1;
          while (! isempty (isn))
            isn = find (isnan (valn(:, 1)));
            if (! isempty (isn))
              ## Insert new subfeature pointer. npart pointers are 0-based
              nprt = [ nprt isn(1) ];
              valn(isn(1), :) = [];
            endif
          endwhile
          ## Build up new points from top down
          valt = [ valn; valt ];
          ## nprt only gets non-empty if a polyline part was split up
          ## FIXME moet net als bij polygons
          ttnpr = [ ttnpr(1:ii) (nprt+ttnpr(ii)-1) ttnpr(ii+1:end) ];
        endif
      endif
    endfor
    tnpt = size (valt, 1);
    tnpr = ttnpr(1:end-1);
  endif

  ## There's still the possibility of segments crossing through Bounding Box
  ## w/o having points inside. Search these segments one by one
  cc = find (! aa);
  if (numel (cc) > 1)
    ## Find discontinuities; and merge wit tnpr (multipart) discontinuities
    ci = find (diff(cc) > 1)';
    ## Make sure to only include multipart discontinuities within range of cc
    ctnpr = unique ([max(btnpr, cc(1)-1) cc(ci)' min(cc(end), size (val, 1))]);
    ## Create sbx from sbox ([minx max miny maxy])
    sbx = [min(sbox(:, 1)), max(sbox(:, 1)), min(sbox(:, 2)), max(sbox(:, 2))];
    for ic=1:numel (ctnpr) - 1
      valc = val(ctnpr(ic)+1:ctnpr(ic+1), :);
      ## Eliminate segments that lie to one outside of the bounding box.
      ## Step 1: assess which side of the bounding box the segments lie
      f1 = valc(:, 1) < min (sbox(:, 1));
      f2 = valc(:, 1) > max (sbox(:, 1));
      f3 = valc(:, 2) < min (sbox(:, 2));
      f4 = valc(:, 2) > max (sbox(:, 2));
      ff = [f1'; f2' ; f3' ; f4']';
      ## Step 2: find those segments that cross > 2 extended sbox boundary line
      dd = find (sum ([abs(diff(f1)'); abs(diff(f2)'); ...
                       abs(diff(f3)'); abs(diff(f4)')]) > 1);
      ## If there are any vertices changing orientation w.r.t. bounding box:
      if (! isempty (dd))
        ## Create array of lines potentially crossing sbox
        lines = [];
        for ii=1:numel (dd)
          lines = [ lines; createLine(valc(dd(ii), 1:2), valc(dd(ii)+1, 1:2))];
        endfor
        ## For each of that segment, try to compute intersections with sbox
        for ii=1:numel (dd)
          ## clipLine is in OF geometry
          jntsc = reshape (clipLine (lines, sbx), 2, 2)';
          ## Check if there are any intersections at all
          if (! any (isnan (jntsc)))
            valn = [ jntsc NaN(2, 4) ];
            ## Interpolate M and Z values. valc(dd(ii)+1) also required ...
            dist = distancePoints (valc(dd(ii):dd(ii)+1, 1:2), valn(:, 1:2));
            ## ... for length of original segment:
            sl = sum (dist(:, 1));
            ## Find closest point of original segment
            valn(1, 3:4) = valc(dd(ii), 3:4) + dist(1, 1) / sl * ...
                           (valc(dd(ii)+1, 3:4) - valc(dd(ii), 3:4));
            valn(2, 3:4) = valc(dd(ii), 3:4) + dist(1, 2) / sl * ...
                           (valc(dd(ii)+1, 3:4) - valc(dd(ii), 3:4));
            ## Adapt tnpr
            tnpr = [ tnpr size(valt, 1) ];
            valt = [ valt; valn ];
          endif
        endfor
        tnpr = unique (tnpr);
      endif
    endfor
  endif
  
  valt(:, 5:6) = repmat (val(1, 5:6), size (valt, 1), 1);
  val = valt;

endfunction