interval.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. // Copyright 2014 Google Inc. All rights reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package r1
  15. import (
  16. "fmt"
  17. "math"
  18. )
  19. // Interval represents a closed interval on ℝ.
  20. // Zero-length intervals (where Lo == Hi) represent single points.
  21. // If Lo > Hi then the interval is empty.
  22. type Interval struct {
  23. Lo, Hi float64
  24. }
  25. // EmptyInterval returns an empty interval.
  26. func EmptyInterval() Interval { return Interval{1, 0} }
  27. // IntervalFromPoint returns an interval representing a single point.
  28. func IntervalFromPoint(p float64) Interval { return Interval{p, p} }
  29. // IsEmpty reports whether the interval is empty.
  30. func (i Interval) IsEmpty() bool { return i.Lo > i.Hi }
  31. // Equal returns true iff the interval contains the same points as oi.
  32. func (i Interval) Equal(oi Interval) bool {
  33. return i == oi || i.IsEmpty() && oi.IsEmpty()
  34. }
  35. // Center returns the midpoint of the interval.
  36. // It is undefined for empty intervals.
  37. func (i Interval) Center() float64 { return 0.5 * (i.Lo + i.Hi) }
  38. // Length returns the length of the interval.
  39. // The length of an empty interval is negative.
  40. func (i Interval) Length() float64 { return i.Hi - i.Lo }
  41. // Contains returns true iff the interval contains p.
  42. func (i Interval) Contains(p float64) bool { return i.Lo <= p && p <= i.Hi }
  43. // ContainsInterval returns true iff the interval contains oi.
  44. func (i Interval) ContainsInterval(oi Interval) bool {
  45. if oi.IsEmpty() {
  46. return true
  47. }
  48. return i.Lo <= oi.Lo && oi.Hi <= i.Hi
  49. }
  50. // InteriorContains returns true iff the the interval strictly contains p.
  51. func (i Interval) InteriorContains(p float64) bool {
  52. return i.Lo < p && p < i.Hi
  53. }
  54. // InteriorContainsInterval returns true iff the interval strictly contains oi.
  55. func (i Interval) InteriorContainsInterval(oi Interval) bool {
  56. if oi.IsEmpty() {
  57. return true
  58. }
  59. return i.Lo < oi.Lo && oi.Hi < i.Hi
  60. }
  61. // Intersects returns true iff the interval contains any points in common with oi.
  62. func (i Interval) Intersects(oi Interval) bool {
  63. if i.Lo <= oi.Lo {
  64. return oi.Lo <= i.Hi && oi.Lo <= oi.Hi // oi.Lo ∈ i and oi is not empty
  65. }
  66. return i.Lo <= oi.Hi && i.Lo <= i.Hi // i.Lo ∈ oi and i is not empty
  67. }
  68. // InteriorIntersects returns true iff the interior of the interval contains any points in common with oi, including the latter's boundary.
  69. func (i Interval) InteriorIntersects(oi Interval) bool {
  70. return oi.Lo < i.Hi && i.Lo < oi.Hi && i.Lo < i.Hi && oi.Lo <= oi.Hi
  71. }
  72. // Intersection returns the interval containing all points common to i and j.
  73. func (i Interval) Intersection(j Interval) Interval {
  74. // Empty intervals do not need to be special-cased.
  75. return Interval{
  76. Lo: math.Max(i.Lo, j.Lo),
  77. Hi: math.Min(i.Hi, j.Hi),
  78. }
  79. }
  80. // AddPoint returns the interval expanded so that it contains the given point.
  81. func (i Interval) AddPoint(p float64) Interval {
  82. if i.IsEmpty() {
  83. return Interval{p, p}
  84. }
  85. if p < i.Lo {
  86. return Interval{p, i.Hi}
  87. }
  88. if p > i.Hi {
  89. return Interval{i.Lo, p}
  90. }
  91. return i
  92. }
  93. // ClampPoint returns the closest point in the interval to the given point "p".
  94. // The interval must be non-empty.
  95. func (i Interval) ClampPoint(p float64) float64 {
  96. return math.Max(i.Lo, math.Min(i.Hi, p))
  97. }
  98. // Expanded returns an interval that has been expanded on each side by margin.
  99. // If margin is negative, then the function shrinks the interval on
  100. // each side by margin instead. The resulting interval may be empty. Any
  101. // expansion of an empty interval remains empty.
  102. func (i Interval) Expanded(margin float64) Interval {
  103. if i.IsEmpty() {
  104. return i
  105. }
  106. return Interval{i.Lo - margin, i.Hi + margin}
  107. }
  108. // Union returns the smallest interval that contains this interval and the given interval.
  109. func (i Interval) Union(other Interval) Interval {
  110. if i.IsEmpty() {
  111. return other
  112. }
  113. if other.IsEmpty() {
  114. return i
  115. }
  116. return Interval{math.Min(i.Lo, other.Lo), math.Max(i.Hi, other.Hi)}
  117. }
  118. func (i Interval) String() string { return fmt.Sprintf("[%.7f, %.7f]", i.Lo, i.Hi) }
  119. // epsilon is a small number that represents a reasonable level of noise between two
  120. // values that can be considered to be equal.
  121. const epsilon = 1e-14
  122. // ApproxEqual reports whether the interval can be transformed into the
  123. // given interval by moving each endpoint a small distance.
  124. // The empty interval is considered to be positioned arbitrarily on the
  125. // real line, so any interval with a small enough length will match
  126. // the empty interval.
  127. func (i Interval) ApproxEqual(other Interval) bool {
  128. if i.IsEmpty() {
  129. return other.Length() <= 2*epsilon
  130. }
  131. if other.IsEmpty() {
  132. return i.Length() <= 2*epsilon
  133. }
  134. return math.Abs(other.Lo-i.Lo) <= epsilon &&
  135. math.Abs(other.Hi-i.Hi) <= epsilon
  136. }