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