1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.hipparchus.geometry.euclidean.oned;
23
24 import java.util.ArrayList;
25 import java.util.Collection;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.NoSuchElementException;
29
30 import org.hipparchus.geometry.Point;
31 import org.hipparchus.geometry.partitioning.AbstractRegion;
32 import org.hipparchus.geometry.partitioning.BSPTree;
33 import org.hipparchus.geometry.partitioning.BoundaryProjection;
34 import org.hipparchus.geometry.partitioning.SubHyperplane;
35 import org.hipparchus.util.Precision;
36
37
38
39 public class IntervalsSet extends AbstractRegion<Euclidean1D, Euclidean1D> implements Iterable<double[]> {
40
41
42
43
44 public IntervalsSet(final double tolerance) {
45 super(tolerance);
46 }
47
48
49
50
51
52
53
54
55 public IntervalsSet(final double lower, final double upper, final double tolerance) {
56 super(buildTree(lower, upper, tolerance), tolerance);
57 }
58
59
60
61
62
63
64
65
66
67
68
69 public IntervalsSet(final BSPTree<Euclidean1D> tree, final double tolerance) {
70 super(tree, tolerance);
71 }
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93 public IntervalsSet(final Collection<SubHyperplane<Euclidean1D>> boundary,
94 final double tolerance) {
95 super(boundary, tolerance);
96 }
97
98
99
100
101
102
103
104
105
106 private static BSPTree<Euclidean1D> buildTree(final double lower, final double upper,
107 final double tolerance) {
108 if (Double.isInfinite(lower) && (lower < 0)) {
109 if (Double.isInfinite(upper) && (upper > 0)) {
110
111 return new BSPTree<Euclidean1D>(Boolean.TRUE);
112 }
113
114 final SubHyperplane<Euclidean1D> upperCut =
115 new OrientedPoint(new Vector1D(upper), true, tolerance).wholeHyperplane();
116 return new BSPTree<Euclidean1D>(upperCut,
117 new BSPTree<Euclidean1D>(Boolean.FALSE),
118 new BSPTree<Euclidean1D>(Boolean.TRUE),
119 null);
120 }
121 final SubHyperplane<Euclidean1D> lowerCut =
122 new OrientedPoint(new Vector1D(lower), false, tolerance).wholeHyperplane();
123 if (Double.isInfinite(upper) && (upper > 0)) {
124
125 return new BSPTree<Euclidean1D>(lowerCut,
126 new BSPTree<Euclidean1D>(Boolean.FALSE),
127 new BSPTree<Euclidean1D>(Boolean.TRUE),
128 null);
129 }
130
131
132 final SubHyperplane<Euclidean1D> upperCut =
133 new OrientedPoint(new Vector1D(upper), true, tolerance).wholeHyperplane();
134 return new BSPTree<Euclidean1D>(lowerCut,
135 new BSPTree<Euclidean1D>(Boolean.FALSE),
136 new BSPTree<Euclidean1D>(upperCut,
137 new BSPTree<Euclidean1D>(Boolean.FALSE),
138 new BSPTree<Euclidean1D>(Boolean.TRUE),
139 null),
140 null);
141
142 }
143
144
145 @Override
146 public IntervalsSet buildNew(final BSPTree<Euclidean1D> tree) {
147 return new IntervalsSet(tree, getTolerance());
148 }
149
150
151 @Override
152 protected void computeGeometricalProperties() {
153 if (getTree(false).getCut() == null) {
154 setBarycenter((Point<Euclidean1D>) Vector1D.NaN);
155 setSize(((Boolean) getTree(false).getAttribute()) ? Double.POSITIVE_INFINITY : 0);
156 } else {
157 double size = 0.0;
158 double sum = 0.0;
159 for (final Interval interval : asList()) {
160 size += interval.getSize();
161 sum += interval.getSize() * interval.getBarycenter();
162 }
163 setSize(size);
164 if (Double.isInfinite(size)) {
165 setBarycenter((Point<Euclidean1D>) Vector1D.NaN);
166 } else if (size >= Precision.SAFE_MIN) {
167 setBarycenter((Point<Euclidean1D>) new Vector1D(sum / size));
168 } else {
169 setBarycenter((Point<Euclidean1D>) ((OrientedPoint) getTree(false).getCut().getHyperplane()).getLocation());
170 }
171 }
172 }
173
174
175
176
177
178
179
180 public double getInf() {
181 BSPTree<Euclidean1D> node = getTree(false);
182 double inf = Double.POSITIVE_INFINITY;
183 while (node.getCut() != null) {
184 final OrientedPoint op = (OrientedPoint) node.getCut().getHyperplane();
185 inf = op.getLocation().getX();
186 node = op.isDirect() ? node.getMinus() : node.getPlus();
187 }
188 return ((Boolean) node.getAttribute()) ? Double.NEGATIVE_INFINITY : inf;
189 }
190
191
192
193
194
195
196
197 public double getSup() {
198 BSPTree<Euclidean1D> node = getTree(false);
199 double sup = Double.NEGATIVE_INFINITY;
200 while (node.getCut() != null) {
201 final OrientedPoint op = (OrientedPoint) node.getCut().getHyperplane();
202 sup = op.getLocation().getX();
203 node = op.isDirect() ? node.getPlus() : node.getMinus();
204 }
205 return ((Boolean) node.getAttribute()) ? Double.POSITIVE_INFINITY : sup;
206 }
207
208
209
210 @Override
211 public BoundaryProjection<Euclidean1D> projectToBoundary(final Point<Euclidean1D> point) {
212
213
214 final double x = ((Vector1D) point).getX();
215
216 double previous = Double.NEGATIVE_INFINITY;
217 for (final double[] a : this) {
218 if (x < a[0]) {
219
220
221 final double previousOffset = x - previous;
222 final double currentOffset = a[0] - x;
223 if (previousOffset < currentOffset) {
224 return new BoundaryProjection<Euclidean1D>(point, finiteOrNullPoint(previous), previousOffset);
225 } else {
226 return new BoundaryProjection<Euclidean1D>(point, finiteOrNullPoint(a[0]), currentOffset);
227 }
228 } else if (x <= a[1]) {
229
230
231 final double offset0 = a[0] - x;
232 final double offset1 = x - a[1];
233 if (offset0 < offset1) {
234 return new BoundaryProjection<Euclidean1D>(point, finiteOrNullPoint(a[1]), offset1);
235 } else {
236 return new BoundaryProjection<Euclidean1D>(point, finiteOrNullPoint(a[0]), offset0);
237 }
238 }
239 previous = a[1];
240 }
241
242
243 return new BoundaryProjection<Euclidean1D>(point, finiteOrNullPoint(previous), x - previous);
244
245 }
246
247
248
249
250
251 private Vector1D finiteOrNullPoint(final double x) {
252 return Double.isInfinite(x) ? null : new Vector1D(x);
253 }
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268 public List<Interval> asList() {
269 final List<Interval> list = new ArrayList<>();
270 for (final double[] a : this) {
271 list.add(new Interval(a[0], a[1]));
272 }
273 return list;
274 }
275
276
277
278
279
280 private BSPTree<Euclidean1D> getFirstLeaf(final BSPTree<Euclidean1D> root) {
281
282 if (root.getCut() == null) {
283 return root;
284 }
285
286
287 BSPTree<Euclidean1D> smallest = null;
288 for (BSPTree<Euclidean1D> n = root; n != null; n = previousInternalNode(n)) {
289 smallest = n;
290 }
291
292 return leafBefore(smallest);
293
294 }
295
296
297
298
299
300 private BSPTree<Euclidean1D> getFirstIntervalBoundary() {
301
302
303 BSPTree<Euclidean1D> node = getTree(false);
304 if (node.getCut() == null) {
305 return null;
306 }
307
308
309 node = getFirstLeaf(node).getParent();
310
311
312 while (node != null && !(isIntervalStart(node) || isIntervalEnd(node))) {
313 node = nextInternalNode(node);
314 }
315
316 return node;
317
318 }
319
320
321
322
323
324 private boolean isIntervalStart(final BSPTree<Euclidean1D> node) {
325
326 if ((Boolean) leafBefore(node).getAttribute()) {
327
328 return false;
329 }
330
331 if (!(Boolean) leafAfter(node).getAttribute()) {
332
333 return false;
334 }
335
336
337
338 return true;
339
340 }
341
342
343
344
345
346 private boolean isIntervalEnd(final BSPTree<Euclidean1D> node) {
347
348 if (!(Boolean) leafBefore(node).getAttribute()) {
349
350 return false;
351 }
352
353 if ((Boolean) leafAfter(node).getAttribute()) {
354
355 return false;
356 }
357
358
359
360 return true;
361
362 }
363
364
365
366
367
368
369 private BSPTree<Euclidean1D> nextInternalNode(BSPTree<Euclidean1D> node) {
370
371 if (childAfter(node).getCut() != null) {
372
373 return leafAfter(node).getParent();
374 }
375
376
377 while (isAfterParent(node)) {
378 node = node.getParent();
379 }
380 return node.getParent();
381
382 }
383
384
385
386
387
388
389 private BSPTree<Euclidean1D> previousInternalNode(BSPTree<Euclidean1D> node) {
390
391 if (childBefore(node).getCut() != null) {
392
393 return leafBefore(node).getParent();
394 }
395
396
397 while (isBeforeParent(node)) {
398 node = node.getParent();
399 }
400 return node.getParent();
401
402 }
403
404
405
406
407
408 private BSPTree<Euclidean1D> leafBefore(BSPTree<Euclidean1D> node) {
409
410 node = childBefore(node);
411 while (node.getCut() != null) {
412 node = childAfter(node);
413 }
414
415 return node;
416
417 }
418
419
420
421
422
423 private BSPTree<Euclidean1D> leafAfter(BSPTree<Euclidean1D> node) {
424
425 node = childAfter(node);
426 while (node.getCut() != null) {
427 node = childBefore(node);
428 }
429
430 return node;
431
432 }
433
434
435
436
437
438 private boolean isBeforeParent(final BSPTree<Euclidean1D> node) {
439 final BSPTree<Euclidean1D> parent = node.getParent();
440 if (parent == null) {
441 return false;
442 } else {
443 return node == childBefore(parent);
444 }
445 }
446
447
448
449
450
451 private boolean isAfterParent(final BSPTree<Euclidean1D> node) {
452 final BSPTree<Euclidean1D> parent = node.getParent();
453 if (parent == null) {
454 return false;
455 } else {
456 return node == childAfter(parent);
457 }
458 }
459
460
461
462
463
464 private BSPTree<Euclidean1D> childBefore(BSPTree<Euclidean1D> node) {
465 if (isDirect(node)) {
466
467 return node.getMinus();
468 } else {
469
470 return node.getPlus();
471 }
472 }
473
474
475
476
477
478 private BSPTree<Euclidean1D> childAfter(BSPTree<Euclidean1D> node) {
479 if (isDirect(node)) {
480
481 return node.getPlus();
482 } else {
483
484 return node.getMinus();
485 }
486 }
487
488
489
490
491
492 private boolean isDirect(final BSPTree<Euclidean1D> node) {
493 return ((OrientedPoint) node.getCut().getHyperplane()).isDirect();
494 }
495
496
497
498
499
500 private double getAngle(final BSPTree<Euclidean1D> node) {
501 return ((OrientedPoint) node.getCut().getHyperplane()).getLocation().getX();
502 }
503
504
505
506
507
508
509
510
511
512 @Override
513 public Iterator<double[]> iterator() {
514 return new SubIntervalsIterator();
515 }
516
517
518 private class SubIntervalsIterator implements Iterator<double[]> {
519
520
521 private BSPTree<Euclidean1D> current;
522
523
524 private double[] pending;
525
526
527
528 SubIntervalsIterator() {
529
530 current = getFirstIntervalBoundary();
531
532 if (current == null) {
533
534 if ((Boolean) getFirstLeaf(getTree(false)).getAttribute()) {
535
536 pending = new double[] {
537 Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY
538 };
539 } else {
540 pending = null;
541 }
542 } else if (isIntervalEnd(current)) {
543
544
545 pending = new double[] {
546 Double.NEGATIVE_INFINITY, getAngle(current)
547 };
548 } else {
549 selectPending();
550 }
551 }
552
553
554
555 private void selectPending() {
556
557
558 BSPTree<Euclidean1D> start = current;
559 while (start != null && !isIntervalStart(start)) {
560 start = nextInternalNode(start);
561 }
562
563 if (start == null) {
564
565 current = null;
566 pending = null;
567 return;
568 }
569
570
571 BSPTree<Euclidean1D> end = start;
572 while (end != null && !isIntervalEnd(end)) {
573 end = nextInternalNode(end);
574 }
575
576 if (end != null) {
577
578
579 pending = new double[] {
580 getAngle(start), getAngle(end)
581 };
582
583
584 current = end;
585
586 } else {
587
588
589 pending = new double[] {
590 getAngle(start), Double.POSITIVE_INFINITY
591 };
592
593
594 current = null;
595
596 }
597
598 }
599
600
601 @Override
602 public boolean hasNext() {
603 return pending != null;
604 }
605
606
607 @Override
608 public double[] next() {
609 if (pending == null) {
610 throw new NoSuchElementException();
611 }
612 final double[] next = pending;
613 selectPending();
614 return next;
615 }
616
617
618 @Override
619 public void remove() {
620 throw new UnsupportedOperationException();
621 }
622
623 }
624
625 }