fplot 1.7.1
A Fortran library providing a convenient interface for plotting with Gnuplot.
Loading...
Searching...
No Matches
fplot_simplify.f90
1! fplot_simplify.f90
2
3! References:
4! - https://www.codeproject.com/Articles/114797/Polyline-Simplification
5! - https://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm
6
7submodule(fplot_core) fplot_simplify
8contains
9 module function simplify_polyline_2d1(x, y, tol, err) result(ln)
10 ! Arguments
11 real(real64), intent(in), dimension(:) :: x, y
12 real(real64), intent(in) :: tol
13 class(errors), intent(inout), optional, target :: err
14 real(real64), allocatable, dimension(:,:) :: ln
15
16 ! Local Variables
17 class(errors), pointer :: errmgr
18 type(errors), target :: deferr
19 character(len = 256) :: errmsg
20 integer(int32) :: n
21 real(real64) :: eps
22
23 ! Initialization
24 n = size(x)
25 eps = epsilon(eps)
26 if (present(err)) then
27 errmgr => err
28 else
29 errmgr => deferr
30 end if
31
32 ! Input Check
33 if (size(y) /= n) then
34 write(errmsg, 100) "The array sizes did not match. " // &
35 "The x array contained ", size(x), &
36 " items, but the y array contained ", size(y), "."
37 call errmgr%report_error("simplify_polyline_2d1", trim(errmsg), &
38 plot_array_size_mismatch_error)
39 return
40 end if
41
42 if (tol < eps) then
43 call errmgr%report_error("simplify_polyline_2d1", &
44 "The tolerance value is either negative or less " // &
45 "than machine precision.", plot_invalid_input_error)
46 return
47 end if
48
49 ! Process
50 ln = radial_distance_2d(x, y, tol, err)
51
52100 format(a, i0, a, i0, a)
53 end function
54
55
56 module function simplify_polyline_3d1(x, y, z, tol, err) result(ln)
57 ! Arguments
58 real(real64), intent(in), dimension(:) :: x, y, z
59 real(real64), intent(in) :: tol
60 class(errors), intent(inout), optional, target :: err
61 real(real64), allocatable, dimension(:,:) :: ln
62
63 ! Local Variables
64 class(errors), pointer :: errmgr
65 type(errors), target :: deferr
66 character(len = 256) :: errmsg
67 integer(int32) :: n
68 real(real64) :: eps
69
70 ! Initialization
71 n = size(x)
72 eps = epsilon(eps)
73 if (present(err)) then
74 errmgr => err
75 else
76 errmgr => deferr
77 end if
78
79 ! Input Check
80 if (size(y) /= n .or. size(z) /= n) then
81 write(errmsg, 100) "The array sizes did not match. " // &
82 "The x array contained ", size(x), &
83 " items, the y array contained ", size(y), &
84 ", and the z array contained ", size(z), "."
85 call errmgr%report_error("simplify_polyline_3d1", trim(errmsg), &
86 plot_array_size_mismatch_error)
87 return
88 end if
89
90 if (tol < eps) then
91 call errmgr%report_error("simplify_polyline_3d1", &
92 "The tolerance value is either negative or less " // &
93 "than machine precision.", plot_invalid_input_error)
94 return
95 end if
96
97 ! Process
98 ln = radial_distance_3d(x, y, z, tol, errmgr)
99
100100 format(a, i0, a, i0, a, i0, a)
101 end function
102
103
104
105 module function simplify_polyline_mtx(xy, tol, err) result(ln)
106 ! Arguments
107 real(real64), intent(in), dimension(:,:) :: xy
108 real(real64), intent(in) :: tol
109 class(errors), intent(inout), optional, target :: err
110 real(real64), allocatable, dimension(:,:) :: ln
111
112 ! Local Variables
113 class(errors), pointer :: errmgr
114 type(errors), target :: deferr
115 character(len = 256) :: errmsg
116
117 ! Initialization
118 if (present(err)) then
119 errmgr => err
120 else
121 errmgr => deferr
122 end if
123
124 ! Ensure there are at least 2 columns of data in XY
125 if (size(xy, 2) < 2) then
126 write(errmsg, 100) "The input matrix must have at " // &
127 "least 2 columns; however, only ", size(xy, 2), " was found."
128 call errmgr%report_error("simplify_polyline_mtx", trim(errmsg), &
129 plot_array_size_mismatch_error)
130 return
131 end if
132
133 ! Process
134 if (size(xy, 2) == 2) then
135 ln = simplify_polyline_2d1(xy(:,1), xy(:,2), tol, errmgr)
136 else
137 ln = simplify_polyline_3d1(xy(:,1), xy(:,2), xy(:,3), tol, errmgr)
138 end if
139
140100 format(a, i0, a)
141 end function
142
143
144
145 function radial_distance_2d(x, y, tol, err) result(pts)
146 ! Arguments
147 real(real64), intent(in), dimension(:) :: x, y
148 real(real64), intent(in) :: tol
149 class(errors), intent(inout) :: err
150 real(real64), allocatable, dimension(:,:) :: pts
151
152 ! Local Variables
153 integer(int32) :: i, j, n, nvalid, flag
154 logical, allocatable, dimension(:) :: valid
155 real(real64) :: r, xref, yref
156
157 ! Initialization
158 n = size(x)
159 if (n == 0) return
160 i = 2
161 xref = x(1)
162 yref = y(1)
163 nvalid = 1
164
165 ! Local Memory Allocation
166 allocate(valid(n), stat = flag)
167 if (flag /= 0) then
168 call err%report_error("radial_distance_2d", &
169 "Insufficient memory available.", &
170 plot_out_of_memory_error)
171 return
172 end if
173 valid(1) = .true.
174
175 ! Cycle through and determine which points to keep
176 do
177 if (i > n) exit
178 r = pythag2(x(i), y(i), xref, yref)
179 if (r < tol) then
180 ! The point is too close, reject it
181 valid(i) = .false.
182 else
183 ! The point is outside the tolerance, and is OK
184 valid(i) = .true.
185 nvalid = nvalid + 1
186
187 ! Move the reference point
188 xref = x(i)
189 yref = y(i)
190 end if
191 i = i + 1
192 end do
193
194 ! Allocate space, and collect all valid points
195 allocate(pts(nvalid, 2), stat = flag)
196 if (flag /= 0) then
197 call err%report_error("radial_distance_2d", &
198 "Insufficient memory available.", &
199 plot_out_of_memory_error)
200 return
201 end if
202 j = 1
203 do i = 1, n
204 if (valid(i)) then
205 pts(j,1) = x(i)
206 pts(j,2) = y(i)
207 j = j + 1
208 end if
209 end do
210 end function
211
212
213 function radial_distance_3d(x, y, z, tol, err) result(pts)
214 ! Arguments
215 real(real64), intent(in), dimension(:) :: x, y, z
216 real(real64), intent(in) :: tol
217 class(errors), intent(inout) :: err
218 real(real64), allocatable, dimension(:,:) :: pts
219
220 ! Local Variables
221 integer(int32) :: i, j, n, nvalid, flag
222 logical, allocatable, dimension(:) :: valid
223 real(real64) :: r, xref, yref, zref
224
225 ! Initialization
226 n = size(x)
227 if (n == 0) return
228 i = 2
229 xref = x(1)
230 yref = y(1)
231 zref = z(1)
232 nvalid = 1
233
234 ! Local Memory Allocation
235 allocate(valid(n), stat = flag)
236 if (flag /= 0) then
237 call err%report_error("radial_distance_3d", &
238 "Insufficient memory available.", &
239 plot_out_of_memory_error)
240 return
241 end if
242 valid(1) = .true.
243
244 ! Cycle through and determine which points to keep
245 do
246 if (i > n) exit
247 r = pythag3(x(i), y(i), z(i), xref, yref, zref)
248 if (r < tol) then
249 ! The point is too close, reject it
250 valid(i) = .false.
251 else
252 ! The point is outside the tolerance, and is OK
253 valid(i) = .true.
254 nvalid = nvalid + 1
255
256 ! Move the reference point
257 xref = x(i)
258 yref = y(i)
259 zref = z(i)
260 end if
261 i = i + 1
262 end do
263
264 ! Allocate space, and collect all valid points
265 allocate(pts(nvalid, 3), stat = flag)
266 if (flag /= 0) then
267 call err%report_error("radial_distance_3d", &
268 "Insufficient memory available.", &
269 plot_out_of_memory_error)
270 return
271 end if
272 j = 1
273 do i = 1, n
274 if (valid(i)) then
275 pts(j,1) = x(i)
276 pts(j,2) = y(i)
277 pts(j,3) = z(i)
278 j = j + 1
279 end if
280 end do
281 end function
282
283 pure function pythag2(x, y, xo, yo) result(r)
284 ! Arguments
285 real(real64), intent(in) :: x, y, xo, yo
286 real(real64) :: r
287
288 ! Local Variables
289 real(real64) :: w, xabs, yabs
290
291 ! Process
292 xabs = abs(x - xo)
293 yabs = abs(y - yo)
294 w = max(xabs, yabs)
295 if (w < epsilon(w)) then
296 r = xabs + yabs
297 else
298 r = w * sqrt((xabs / w)**2 + (yabs / w)**2)
299 end if
300 end function
301
302 pure function pythag3(x, y, z, xo, yo, zo) result(r)
303 ! Arguments
304 real(real64), intent(in) :: x, y, z, xo, yo, zo
305 real(real64) :: r
306
307 ! Local Variables
308 real(real64) :: w, xabs, yabs, zabs
309
310 ! Process
311 xabs = abs(x - xo)
312 yabs = abs(y - yo)
313 zabs = abs(z - zo)
314 w = max(xabs, yabs, zabs)
315 if (w < epsilon(w)) then
316 r = xabs + yabs + zabs
317 else
318 r = w * sqrt((xabs / w)**2 + (yabs / w)**2 + (zabs / w)**2)
319 end if
320 end function
321
322end submodule
fplot_core