1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package s1
16
17 import (
18 "math"
19 "strconv"
20 )
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 type Interval struct {
44 Lo, Hi float64
45 }
46
47
48
49
50 func IntervalFromEndpoints(lo, hi float64) Interval {
51 i := Interval{lo, hi}
52 if lo == -math.Pi && hi != math.Pi {
53 i.Lo = math.Pi
54 }
55 if hi == -math.Pi && lo != math.Pi {
56 i.Hi = math.Pi
57 }
58 return i
59 }
60
61
62
63 func IntervalFromPointPair(a, b float64) Interval {
64 if a == -math.Pi {
65 a = math.Pi
66 }
67 if b == -math.Pi {
68 b = math.Pi
69 }
70 if positiveDistance(a, b) <= math.Pi {
71 return Interval{a, b}
72 }
73 return Interval{b, a}
74 }
75
76
77 func EmptyInterval() Interval { return Interval{math.Pi, -math.Pi} }
78
79
80 func FullInterval() Interval { return Interval{-math.Pi, math.Pi} }
81
82
83 func (i Interval) IsValid() bool {
84 return (math.Abs(i.Lo) <= math.Pi && math.Abs(i.Hi) <= math.Pi &&
85 !(i.Lo == -math.Pi && i.Hi != math.Pi) &&
86 !(i.Hi == -math.Pi && i.Lo != math.Pi))
87 }
88
89
90 func (i Interval) IsFull() bool { return i.Lo == -math.Pi && i.Hi == math.Pi }
91
92
93 func (i Interval) IsEmpty() bool { return i.Lo == math.Pi && i.Hi == -math.Pi }
94
95
96 func (i Interval) IsInverted() bool { return i.Lo > i.Hi }
97
98
99 func (i Interval) Invert() Interval {
100 return Interval{i.Hi, i.Lo}
101 }
102
103
104
105 func (i Interval) Center() float64 {
106 c := 0.5 * (i.Lo + i.Hi)
107 if !i.IsInverted() {
108 return c
109 }
110 if c <= 0 {
111 return c + math.Pi
112 }
113 return c - math.Pi
114 }
115
116
117
118 func (i Interval) Length() float64 {
119 l := i.Hi - i.Lo
120 if l >= 0 {
121 return l
122 }
123 l += 2 * math.Pi
124 if l > 0 {
125 return l
126 }
127 return -1
128 }
129
130
131 func (i Interval) fastContains(p float64) bool {
132 if i.IsInverted() {
133 return (p >= i.Lo || p <= i.Hi) && !i.IsEmpty()
134 }
135 return p >= i.Lo && p <= i.Hi
136 }
137
138
139
140 func (i Interval) Contains(p float64) bool {
141 if p == -math.Pi {
142 p = math.Pi
143 }
144 return i.fastContains(p)
145 }
146
147
148 func (i Interval) ContainsInterval(oi Interval) bool {
149 if i.IsInverted() {
150 if oi.IsInverted() {
151 return oi.Lo >= i.Lo && oi.Hi <= i.Hi
152 }
153 return (oi.Lo >= i.Lo || oi.Hi <= i.Hi) && !i.IsEmpty()
154 }
155 if oi.IsInverted() {
156 return i.IsFull() || oi.IsEmpty()
157 }
158 return oi.Lo >= i.Lo && oi.Hi <= i.Hi
159 }
160
161
162
163 func (i Interval) InteriorContains(p float64) bool {
164 if p == -math.Pi {
165 p = math.Pi
166 }
167 if i.IsInverted() {
168 return p > i.Lo || p < i.Hi
169 }
170 return (p > i.Lo && p < i.Hi) || i.IsFull()
171 }
172
173
174 func (i Interval) InteriorContainsInterval(oi Interval) bool {
175 if i.IsInverted() {
176 if oi.IsInverted() {
177 return (oi.Lo > i.Lo && oi.Hi < i.Hi) || oi.IsEmpty()
178 }
179 return oi.Lo > i.Lo || oi.Hi < i.Hi
180 }
181 if oi.IsInverted() {
182 return i.IsFull() || oi.IsEmpty()
183 }
184 return (oi.Lo > i.Lo && oi.Hi < i.Hi) || i.IsFull()
185 }
186
187
188 func (i Interval) Intersects(oi Interval) bool {
189 if i.IsEmpty() || oi.IsEmpty() {
190 return false
191 }
192 if i.IsInverted() {
193 return oi.IsInverted() || oi.Lo <= i.Hi || oi.Hi >= i.Lo
194 }
195 if oi.IsInverted() {
196 return oi.Lo <= i.Hi || oi.Hi >= i.Lo
197 }
198 return oi.Lo <= i.Hi && oi.Hi >= i.Lo
199 }
200
201
202 func (i Interval) InteriorIntersects(oi Interval) bool {
203 if i.IsEmpty() || oi.IsEmpty() || i.Lo == i.Hi {
204 return false
205 }
206 if i.IsInverted() {
207 return oi.IsInverted() || oi.Lo < i.Hi || oi.Hi > i.Lo
208 }
209 if oi.IsInverted() {
210 return oi.Lo < i.Hi || oi.Hi > i.Lo
211 }
212 return (oi.Lo < i.Hi && oi.Hi > i.Lo) || i.IsFull()
213 }
214
215
216 func positiveDistance(a, b float64) float64 {
217 d := b - a
218 if d >= 0 {
219 return d
220 }
221 return (b + math.Pi) - (a - math.Pi)
222 }
223
224
225 func (i Interval) Union(oi Interval) Interval {
226 if oi.IsEmpty() {
227 return i
228 }
229 if i.fastContains(oi.Lo) {
230 if i.fastContains(oi.Hi) {
231
232 if i.ContainsInterval(oi) {
233 return i
234 }
235 return FullInterval()
236 }
237 return Interval{i.Lo, oi.Hi}
238 }
239 if i.fastContains(oi.Hi) {
240 return Interval{oi.Lo, i.Hi}
241 }
242
243
244 if i.IsEmpty() || oi.fastContains(i.Lo) {
245 return oi
246 }
247
248
249 if positiveDistance(oi.Hi, i.Lo) < positiveDistance(i.Hi, oi.Lo) {
250 return Interval{oi.Lo, i.Hi}
251 }
252 return Interval{i.Lo, oi.Hi}
253 }
254
255
256 func (i Interval) Intersection(oi Interval) Interval {
257 if oi.IsEmpty() {
258 return EmptyInterval()
259 }
260 if i.fastContains(oi.Lo) {
261 if i.fastContains(oi.Hi) {
262
263
264
265
266
267 if oi.Length() < i.Length() {
268 return oi
269 }
270 return i
271 }
272 return Interval{oi.Lo, i.Hi}
273 }
274 if i.fastContains(oi.Hi) {
275 return Interval{i.Lo, oi.Hi}
276 }
277
278
279 if oi.fastContains(i.Lo) {
280 return i
281 }
282 return EmptyInterval()
283 }
284
285
286
287 func (i Interval) AddPoint(p float64) Interval {
288 if math.Abs(p) > math.Pi {
289 return i
290 }
291 if p == -math.Pi {
292 p = math.Pi
293 }
294 if i.fastContains(p) {
295 return i
296 }
297 if i.IsEmpty() {
298 return Interval{p, p}
299 }
300 if positiveDistance(p, i.Lo) < positiveDistance(i.Hi, p) {
301 return Interval{p, i.Hi}
302 }
303 return Interval{i.Lo, p}
304 }
305
306
307
308
309
310 var (
311
312
313 epsilon = 1e-15
314
315 dblEpsilon = 2.220446049e-16
316 )
317
318
319
320
321
322
323 func (i Interval) Expanded(margin float64) Interval {
324 if margin >= 0 {
325 if i.IsEmpty() {
326 return i
327 }
328
329
330 if i.Length()+2*margin+2*dblEpsilon >= 2*math.Pi {
331 return FullInterval()
332 }
333 } else {
334 if i.IsFull() {
335 return i
336 }
337
338
339 if i.Length()+2*margin-2*dblEpsilon <= 0 {
340 return EmptyInterval()
341 }
342 }
343 result := IntervalFromEndpoints(
344 math.Remainder(i.Lo-margin, 2*math.Pi),
345 math.Remainder(i.Hi+margin, 2*math.Pi),
346 )
347 if result.Lo <= -math.Pi {
348 result.Lo = math.Pi
349 }
350 return result
351 }
352
353
354
355
356
357
358
359 func (i Interval) ApproxEqual(other Interval) bool {
360
361
362 if i.IsEmpty() {
363 return other.Length() <= 2*epsilon
364 }
365 if other.IsEmpty() {
366 return i.Length() <= 2*epsilon
367 }
368 if i.IsFull() {
369 return other.Length() >= 2*(math.Pi-epsilon)
370 }
371 if other.IsFull() {
372 return i.Length() >= 2*(math.Pi-epsilon)
373 }
374
375
376
377 return (math.Abs(math.Remainder(other.Lo-i.Lo, 2*math.Pi)) <= epsilon &&
378 math.Abs(math.Remainder(other.Hi-i.Hi, 2*math.Pi)) <= epsilon &&
379 math.Abs(i.Length()-other.Length()) <= 2*epsilon)
380
381 }
382
383 func (i Interval) String() string {
384
385 return "[" + strconv.FormatFloat(i.Lo, 'f', 7, 64) + ", " + strconv.FormatFloat(i.Hi, 'f', 7, 64) + "]"
386 }
387
388
389
390
391
392
393 func (i Interval) Complement() Interval {
394 if i.Lo == i.Hi {
395
396 return FullInterval()
397 }
398
399 return Interval{i.Hi, i.Lo}
400 }
401
402
403
404
405 func (i Interval) ComplementCenter() float64 {
406 if i.Lo != i.Hi {
407 return i.Complement().Center()
408 }
409
410 if i.Hi <= 0 {
411 return i.Hi + math.Pi
412 }
413 return i.Hi - math.Pi
414 }
415
416
417
418
419
420
421
422 func (i Interval) DirectedHausdorffDistance(y Interval) Angle {
423 if y.ContainsInterval(i) {
424 return 0
425 }
426 if y.IsEmpty() {
427 return Angle(math.Pi)
428 }
429 yComplementCenter := y.ComplementCenter()
430 if i.Contains(yComplementCenter) {
431 return Angle(positiveDistance(y.Hi, yComplementCenter))
432 }
433
434
435
436 hiHi := 0.0
437 if IntervalFromEndpoints(y.Hi, yComplementCenter).Contains(i.Hi) {
438 hiHi = positiveDistance(y.Hi, i.Hi)
439 }
440
441 loLo := 0.0
442 if IntervalFromEndpoints(yComplementCenter, y.Lo).Contains(i.Lo) {
443 loLo = positiveDistance(i.Lo, y.Lo)
444 }
445
446 return Angle(math.Max(hiHi, loLo))
447 }
448
449
450
451 func (i Interval) Project(p float64) float64 {
452 if p == -math.Pi {
453 p = math.Pi
454 }
455 if i.fastContains(p) {
456 return p
457 }
458
459 dlo := positiveDistance(p, i.Lo)
460 dhi := positiveDistance(i.Hi, p)
461 if dlo < dhi {
462 return i.Lo
463 }
464 return i.Hi
465 }
466
View as plain text