bound2.lua 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. --- A 2 component bounding box.
  2. -- @module bound2
  3. local modules = (...):gsub('%.[^%.]+$', '') .. "."
  4. local vec2 = require(modules .. "vec2")
  5. local bound2 = {}
  6. local bound2_mt = {}
  7. -- Private constructor.
  8. local function new(min, max)
  9. return setmetatable({
  10. min=min, -- min: vec2, minimum value for each component
  11. max=max, -- max: vec2, maximum value for each component
  12. }, bound2_mt)
  13. end
  14. -- Do the check to see if JIT is enabled. If so use the optimized FFI structs.
  15. local status, ffi
  16. if type(jit) == "table" and jit.status() then
  17. status, ffi = pcall(require, "ffi")
  18. if status then
  19. ffi.cdef "typedef struct { cpml_vec2 min, max; } cpml_bound2;"
  20. new = ffi.typeof("cpml_bound2")
  21. end
  22. end
  23. bound2.zero = new(vec2.zero, vec2.zero)
  24. --- The public constructor.
  25. -- @param min Can be of two types: </br>
  26. -- vec2 min, minimum value for each component
  27. -- nil Create bound at single point 0,0
  28. -- @tparam vec2 max, maximum value for each component
  29. -- @treturn bound2 out
  30. function bound2.new(min, max)
  31. if min and max then
  32. return new(min:clone(), max:clone())
  33. elseif min or max then
  34. error("Unexpected nil argument to bound2.new")
  35. else
  36. return new(vec2.zero, vec2.zero)
  37. end
  38. end
  39. --- Clone a bound.
  40. -- @tparam bound2 a bound to be cloned
  41. -- @treturn bound2 out
  42. function bound2.clone(a)
  43. return new(a.min, a.max)
  44. end
  45. --- Construct a bound covering one or two points
  46. -- @tparam vec2 a Any vector
  47. -- @tparam vec2 b Any second vector (optional)
  48. -- @treturn vec2 Minimum bound containing the given points
  49. function bound2.at(a, b) -- "bounded by". b may be nil
  50. if b then
  51. return bound2.new(a,b):check()
  52. else
  53. return bound2.zero:with_center(a)
  54. end
  55. end
  56. --- Get size of bounding box as a vector
  57. -- @tparam bound2 a bound
  58. -- @treturn vec2 Vector spanning min to max points
  59. function bound2.size(a)
  60. return a.max - a.min
  61. end
  62. --- Resize bounding box from minimum corner
  63. -- @tparam bound2 a bound
  64. -- @tparam vec2 new size
  65. -- @treturn bound2 resized bound
  66. function bound2.with_size(a, size)
  67. return bound2.new(a.min, a.min + size)
  68. end
  69. --- Get half-size of bounding box as a vector. A more correct term for this is probably "apothem"
  70. -- @tparam bound2 a bound
  71. -- @treturn vec2 Vector spanning center to max point
  72. function bound2.radius(a)
  73. return a:size()/2
  74. end
  75. --- Get center of bounding box
  76. -- @tparam bound2 a bound
  77. -- @treturn bound2 Point in center of bound
  78. function bound2.center(a)
  79. return (a.min + a.max)/2
  80. end
  81. --- Move bounding box to new center
  82. -- @tparam bound2 a bound
  83. -- @tparam vec2 new center
  84. -- @treturn bound2 Bound with same size as input but different center
  85. function bound2.with_center(a, center)
  86. return bound2.offset(a, center - a:center())
  87. end
  88. --- Resize bounding box from center
  89. -- @tparam bound2 a bound
  90. -- @tparam vec2 new size
  91. -- @treturn bound2 resized bound
  92. function bound2.with_size_centered(a, size)
  93. local center = a:center()
  94. local rad = size/2
  95. return bound2.new(center - rad, center + rad)
  96. end
  97. --- Convert possibly-invalid bounding box to valid one
  98. -- @tparam bound2 a bound
  99. -- @treturn bound2 bound with all components corrected for min-max property
  100. function bound2.check(a)
  101. if a.min.x > a.max.x or a.min.y > a.max.y then
  102. return bound2.new(vec2.component_min(a.min, a.max), vec2.component_max(a.min, a.max))
  103. end
  104. return a
  105. end
  106. --- Shrink bounding box with fixed margin
  107. -- @tparam bound2 a bound
  108. -- @tparam vec2 a margin
  109. -- @treturn bound2 bound with margin subtracted from all edges. May not be valid, consider calling check()
  110. function bound2.inset(a, v)
  111. return bound2.new(a.min + v, a.max - v)
  112. end
  113. --- Expand bounding box with fixed margin
  114. -- @tparam bound2 a bound
  115. -- @tparam vec2 a margin
  116. -- @treturn bound2 bound with margin added to all edges. May not be valid, consider calling check()
  117. function bound2.outset(a, v)
  118. return bound2.new(a.min - v, a.max + v)
  119. end
  120. --- Offset bounding box
  121. -- @tparam bound2 a bound
  122. -- @tparam vec2 offset
  123. -- @treturn bound2 bound with same size, but position moved by offset
  124. function bound2.offset(a, v)
  125. return bound2.new(a.min + v, a.max + v)
  126. end
  127. --- Test if point in bound
  128. -- @tparam bound2 a bound
  129. -- @tparam vec2 point to test
  130. -- @treturn boolean true if point in bounding box
  131. function bound2.contains(a, v)
  132. return a.min.x <= v.x and a.min.y <= v.y and a.min.z <= v.z
  133. and a.max.x >= v.x and a.max.y >= v.y and a.max.z >= v.z
  134. end
  135. bound2_mt.__index = bound2
  136. bound2_mt.__tostring = bound2.to_string
  137. function bound2_mt.__call(_, a, b)
  138. return bound2.new(a, b)
  139. end
  140. if status then
  141. ffi.metatype(new, bound2_mt)
  142. end
  143. return setmetatable({}, bound2_mt)