comparison scripts/geometry/rectint.m @ 20116:33e706b6b7be

rectint: fix for non-overlapping rectangle, and support ND boxes (bug #44904) * scripts/geometry/rectint.m: it was giving incorrect results (not zero) when the coordinates for X overlapped but the Y did not (added tests for this bug). This is a complete rewrite of the function. The new version performs approximately 1000 times faster and adds supports N dimensional boxes in which case it measures the volume or hypervolume of the hypervolume intersections. * NEWS: make note that rectint() now supports ND.
author Carnë Draug <carandraug@octave.org>
date Wed, 22 Apr 2015 20:54:14 +0100
parents 4197fc428c7d
children 7503499a252b
comparison
equal deleted inserted replaced
20115:7e0e8fb16201 20116:33e706b6b7be
1 ## Copyright (C) 2008-2015 Bill Denney 1 ## Copyright (C) 2015 Carnë Draug
2 ## 2 ##
3 ## This file is part of Octave. 3 ## This file is part of Octave.
4 ## 4 ##
5 ## Octave is free software; you can redistribute it and/or modify it 5 ## Octave is free software; you can redistribute it and/or modify it
6 ## under the terms of the GNU General Public License as published by 6 ## under the terms of the GNU General Public License as published by
16 ## along with Octave; see the file COPYING. If not, see 16 ## along with Octave; see the file COPYING. If not, see
17 ## <http://www.gnu.org/licenses/>. 17 ## <http://www.gnu.org/licenses/>.
18 18
19 ## -*- texinfo -*- 19 ## -*- texinfo -*-
20 ## @deftypefn {Function File} {@var{area} =} rectint (@var{a}, @var{b}) 20 ## @deftypefn {Function File} {@var{area} =} rectint (@var{a}, @var{b})
21 ## Compute area or volume of intersection of rectangles or ND boxes.
21 ## 22 ##
22 ## Compute the area of intersection of rectangles in @var{a} and 23 ## Compute the area of intersection of rectangles in @var{a} and
23 ## rectangles in @var{b}. Rectangles are defined as [x y width height] 24 ## rectangles in @var{b}. N dimensional boxes are supported in which
24 ## where x and y are the minimum values of the two orthogonal 25 ## case the volume, or hypervolume is computed according to the number
25 ## dimensions. 26 ## of dimensions.
26 ## 27 ##
27 ## If @var{a} or @var{b} are matrices, then the output, @var{area}, is a 28 ## 2 dimensional rectangles are defined as @code{[xpos ypos width height]}
28 ## matrix where the i-th row corresponds to the i-th row of a and the j-th 29 ## where xpos and ypos are the position of the bottom left corner.
29 ## column corresponds to the j-th row of b. 30 ## Higher dimensions are supported where the coordinates for the minimum
31 ## value of each dimension follow the length of the box in that dimension,
32 ## e.g., @code{[xpos ypos zpos kpos @dots{} width height depth k_length @dots{}]}.
33 ##
34 ## Each row of @var{a} and @var{b} define a rectangle, and if both define
35 ## multiple rectangles, then the output, @var{area}, is a matrix where
36 ## the i-th row corresponds to the i-th row of a and the j-th column
37 ## corresponds to the j-th row of b.
30 ## 38 ##
31 ## @seealso{polyarea} 39 ## @seealso{polyarea}
32 ## @end deftypefn 40 ## @end deftypefn
33 41
34 ## Author: Bill Denney <bill@denney.ws> 42 ## Author: 2015 Carnë Draug <carandraug@octave.org>
35 43
36 function area = rectint (a, b) 44 function dists = rectint (a, b)
37 45
38 if (nargin != 2) 46 if (nargin != 2)
39 print_usage (); 47 print_usage ();
40 elseif (ndims (a) != 2 || ndims (b) != 2) 48 elseif (columns (a) != columns (b))
41 error ("rectint: expecting arguments to be 2-d arrays"); 49 error ("rectint: A and B must have same number of columns");
42 elseif (columns (a) != 4) 50 elseif (ndims (a) > 2)
43 error ("rectint: A must have 4 columns"); 51 error ("rectint: A and B must be 2-d arrays");
44 elseif (columns (b) != 4) 52 elseif (mod (columns (a), 2))
45 error ("rectint: B must have 4 columns"); 53 error ("rectint: number of columns of A and B must be a multiple of two");
46 elseif (any ([a(:,3:4);b(:,3:4)](:) < 0))
47 error ("rectint: all widths and heights must be > 0");
48 endif 54 endif
49 55
50 ## This runs faster if the number of rows of a is greater than the 56 nd = columns (a) / 2;
51 ## number of rows of b. Swap them and transpose to make it run 57 na = rows (a);
52 ## faster. 58 nb = rows (b);
53 swapinputs = false ();
54 if (rows (a) > rows (b))
55 tmp = a;
56 a = b;
57 b = tmp;
58 swapinputs = true ();
59 endif
60 59
61 area = zeros (rows (a), rows (b)); 60 a_start = a(:,1:nd);
62 r1 = [a(:,1:2) a(:,1:2)+a(:,3:4)]; 61 b_start = b(:,1:nd);
63 r2 = [b(:,1:2) b(:,1:2)+b(:,3:4)];
64 for i = 1:columns (area)
65 ## Find the location of each point relative to the other points.
66 r1x1small = r1(:,1) < r2(i,1);
67 r1x1large = r1(:,1) > r2(i,3);
68 r1x1mid = (r1(:,1) >= r2(i,1)) & (r1(:,1) <= r2(i,3));
69 r1x2small = r1(:,3) < r2(i,1);
70 r1x2large = r1(:,3) > r2(i,3);
71 r1x2mid = (r1(:,3) >= r2(i,1)) & (r1(:,3) <= r2(i,3));
72 62
73 r1y1small = r1(:,2) < r2(i,2); 63 a_end = a_start + a(:,nd+1:end);
74 r1y1large = r1(:,2) > r2(i,4); 64 b_end = b_start + b(:,nd+1:end);
75 r1y1mid = (r1(:,2) >= r2(i,2)) & (r1(:,2) <= r2(i,4));
76 r1y2small = r1(:,4) < r2(i,2);
77 r1y2large = r1(:,4) > r2(i,4);
78 r1y2mid = (r1(:,4) >= r2(i,2)) & (r1(:,4) <= r2(i,4));
79 65
80 ## determine the width of the rectangle 66 a_start = reshape (a_start, [na 1 nd]);
81 ## r1 completely encloses r2 67 b_start = reshape (b_start, [1 nb nd]);
82 area(r1x1small & r1x2large,i) = r2(i,3) - r2(i,1);
83 ## the range goes from r2x min to r1x max
84 mask = r1x1small & r1x2mid;
85 area(mask,i) = r1(mask,3) - r2(i,1);
86 ## the range goes from r1x min to r2x max
87 mask = r1x1mid & r1x2large;
88 area(mask,i) = r2(i,3) - r1(mask,1);
89 ## the range goes from r1x min to r1x max
90 mask = r1x1mid & r1x2mid;
91 area(mask,i) = r1(mask,3) - r1(mask,1);
92 68
93 ## determine the height of the rectangle 69 a_end = reshape (a_end, [na 1 nd]);
94 ## r1 completely encloses r2 70 b_end = reshape (b_end, [1 nb nd]);
95 area(r1y1small & r1y2large,i) .*= r2(i,4) - r2(i,2);
96 ## the range goes from r2y min to r1y max
97 mask = r1y1small & r1y2mid;
98 area(mask,i) .*= r1(mask,4) - r2(i,2);
99 ## the range goes from r1y min to r2y max
100 mask = r1y1mid & r1y2large;
101 area(mask,i) .*= r2(i,4) - r1(mask,2);
102 ## the range goes from r1x min to r1x max
103 mask = r1y1mid & r1y2mid;
104 area(mask,i) .*= r1(mask,4) - r1(mask,2);
105 71
106 endfor 72 ## We get a 3d matrix where each dimension is in the 3rd dimension
107 73 dists = bsxfun (@min , a_end, b_end) - bsxfun (@max, a_start, b_start);
108 if (swapinputs) 74 dists(dists < 0) = 0;
109 area = area'; 75 dists = prod (dists, 3);
110 endif
111 76
112 endfunction 77 endfunction
113
114 78
115 ## Exactly overlapping 79 ## Exactly overlapping
116 %!assert (rectint ([0 0 1 1], [0 0 1 1]), 1) 80 %!assert (rectint ([0 0 1 1], [0 0 1 1]), 1)
117 ## rect2 completely enclosed by rect1 81 ## rect2 completely enclosed by rect1
118 %!assert (rectint ([-1 -1 3 3], [0 0 1 1]), 1) 82 %!assert (rectint ([-1 -1 3 3], [0 0 1 1]), 1)
128 %!assert (rectint ([0 0 1 1], [0 1 2 2]), 0) 92 %!assert (rectint ([0 0 1 1], [0 1 2 2]), 0)
129 ## Correct orientation of output 93 ## Correct orientation of output
130 %!assert (rectint ([0 0 1 1;0.5 0.5 1 1;-1 -1 2 2], [1 1 2 2]), [0;0.25;0]) 94 %!assert (rectint ([0 0 1 1;0.5 0.5 1 1;-1 -1 2 2], [1 1 2 2]), [0;0.25;0])
131 %!assert (rectint ([1 1 2 2], [0 0 1 1;0.5 0.5 1 1;-1 -1 2 2]), [0 0.25 0]) 95 %!assert (rectint ([1 1 2 2], [0 0 1 1;0.5 0.5 1 1;-1 -1 2 2]), [0 0.25 0])
132 96
97 ## bug #44904
98 %!assert (rectint ([0 0 5 5], [6 6 5 5]), 0)
99 %!assert (rectint ([0 0 5 5], [0 6 5 5]), 0)
100 %!assert (rectint ([0 0 5 5], [6 0 5 5]), 0)
101 %!assert (rectint ([0 0 0 5 5 5], [0 0 6 5 5 5]), 0)
102
103 ## Test volumes
104 %!shared r1, r2, r3, r4, r5
105 %! r1 = [ 5 3 0 7 5 2];
106 %! r2 = [ 2 5 0 4 2 2];
107 %! r3 = [ 10 7 0 10 3 2];
108 %! r4 = [ 10 -5 0 5 7 2];
109 %! r5 = [-10 0 0 40 11 2];
110
111 %!assert (rectint (r5, r1), 70)
112 %!assert (rectint (r5, r4), 20)
113 %!assert (rectint (r5, [r1; r2; r3; r4]), [70 16 60 20])
114
115 ## Test multiple volumes in both A and B
116 %!assert (rectint ([r2; r5], [r1; r3; r4]), [4 0 0; 70 60 20])