Guest User

Untitled

a guest
Feb 21st, 2018
66
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 5.56 KB | None | 0 0
  1. require 'geometry'
  2.  
  3. module Kernel
  4. # Create a 2D Point
  5. def XY(x,y)
  6. Geometry::Point.xy(x, y)
  7. end
  8.  
  9. # Create a 3D Point
  10. def XYZ(x,y,z)
  11. Geometry::Point.xyz(x, y, z)
  12. end
  13. end
  14.  
  15. module Geometry
  16. # immutable
  17. # Represents a point in an n-dimensional space
  18. class Point < Entity
  19. class <<self
  20. # create a 2-dimensional point
  21. def xy(x, y)
  22. new(x, y)
  23. end
  24.  
  25. # create a 3-dimensional point
  26. def xyz(x, y, z)
  27. new(x, y, z)
  28. end
  29. end
  30.  
  31. intersects_with(Point) { |a,b|
  32. a =~ b
  33. }
  34.  
  35. def initialize(*components)
  36. raise TypeError, "Not numeric component" unless components.all? { |c| Numeric === c }
  37. @components = components
  38. @components.freeze
  39. end
  40.  
  41. # Value-equality of this point with another
  42. # Use =~ if you deal with floats
  43. # Tests if all components of the same dimension are == to the other
  44. def ==(point)
  45. raise DimensionError, "Dimension mismatch, #{dimensions} vs. #{point.dimensions}" unless dimensions == point.dimensions
  46. point.to_a == @components
  47. end
  48.  
  49. # Float-equality of this point with another (see Numeric#=~)
  50. # Tests if all components of the same dimension are =~ to the other
  51. def =~(point)
  52. raise DimensionError, "Dimension mismatch, #{dimensions} vs. #{point.dimensions}" unless dimensions == point.dimensions
  53. @components.zip(point.to_a).all? { |(a,b)| a =~ b }
  54. end
  55.  
  56. def include?(entity)
  57. entity.is_a?(Point) ? self =~ entity : false
  58. end
  59.  
  60. # Array-like access to the components of this point
  61. def [](*index)
  62. @components[*index]
  63. end
  64.  
  65. # Returns component at index 0
  66. def x
  67. @components[0]
  68. end
  69.  
  70. # Returns component at index 1
  71. def y
  72. @components[1]
  73. end
  74.  
  75. # Returns component at index 2
  76. def z
  77. @components[2]
  78. end
  79.  
  80. # The dimensions of this point, e.g. Point.new(1,2,3).dimensions # => 3
  81. def dimensions
  82. @components.length
  83. end
  84.  
  85. # The center of a point is self
  86. def center
  87. dup
  88. end
  89.  
  90. # Returns a point of given dimension. If new point has more
  91. # dimensions than the old one the additional dimensions are filled
  92. # with zeros. If it has less, the are truncated.
  93. def redim(d, fill=0)
  94. Point[*(@components[0,d]+Array.new([d-dimensions,0].max, fill))]
  95. end
  96.  
  97. # add a vector, get a new point
  98. def +(vector)
  99. raise ArgumentError, "Can only add vectors" unless Geometry::Vector === vector
  100. raise DimensionError, "Dimension mismatch, #{dimensions} vs. #{vector.dimensions}" unless dimensions == vector.dimensions
  101.  
  102. Point.new(*@components.zip(vector.to_a).map { |(a,b)| a+b })
  103. end
  104.  
  105. # subtract a vector, get a new point
  106. # subtract a point, get a vector
  107. def -(vector)
  108. raise ArgumentError, "Can only subtract vectors or points" unless Vector === vector || Point === vector
  109. raise DimensionError, "Dimension mismatch, #{dimensions} vs. #{vector.dimensions}" unless dimensions == vector.dimensions
  110.  
  111. if vector.is_a?(Point) then
  112. Vector.new(*@components.zip(vector.to_a).map { |(a,b)| a-b })
  113. else
  114. Point.new(*@components.zip(vector.to_a).map { |(a,b)| a-b })
  115. end
  116. end
  117.  
  118. # Returns the direct distance from point to point
  119. def distance_to(point)
  120. vector_to(point).magnitude
  121. end
  122.  
  123. # Returns the vector needed to go from self to point
  124. def vector_to(point)
  125. raise ArgumentError, "#{point} is not a Point" unless Point === point
  126. raise DimensionError, "Dimension mismatch, #{dimensions} vs. #{vector.dimensions}" unless dimensions == point.dimensions
  127.  
  128. Vector[*@components.zip(point.to_a).map { |(a,b)| b-a }]
  129. end
  130.  
  131. # Returns the vector needed to go from point to self
  132. def vector_from(point)
  133. raise ArgumentError, "#{point} is not a Point" unless Point === point
  134. raise DimensionError, "Dimension mismatch, #{dimensions} vs. #{vector.dimensions}" unless dimensions == point.dimensions
  135.  
  136. Vector[*@components.zip(point.to_a).map { |(a,b)| a-b }]
  137. end
  138.  
  139. # Returns a point moved/translated by given vector
  140. def moved_by(vector)
  141. raise ArgumentError, "Can only add vectors" unless Geometry::Vector === vector
  142. raise DimensionError, "Dimension mismatch, #{dimensions} vs. #{vector.dimensions}" unless dimensions == vector.dimensions
  143.  
  144. Point.new(*@components.zip(vector.to_a).map { |(a,b)| a+b })
  145. end
  146.  
  147. # Returns a point mirrored at given line
  148. def mirrored_at(line)
  149. super
  150. end
  151.  
  152. # Returns a point rotated around given point by given amount (radian)
  153. def rotated_around(point, by_degree=Math::PI)
  154. raise ArgumentError, "#{point} is not a Point" unless Point === point
  155. raise ArgumentError, "#{by_degree} is not Numeric" unless Numeric === by_degree
  156. raise DimensionError, "Dimension mismatch, #{dimensions} vs. #{vector.dimensions}" unless dimensions == point.dimensions
  157. raise DimensionError, "rotate_around is only defined for 2 dimensions" unless dimensions == 2
  158.  
  159. point.moved_by(point.vector_to(self).rotated_by(by_degree))
  160. end
  161.  
  162. # Returns a point rotated by given amount (radian) around given point
  163. def rotated_by(degree, around_point=XY(0,0))
  164. rotated_around(around_point, degree)
  165. end
  166.  
  167. # Position vector of this point
  168. def to_vector
  169. Vector[*@components]
  170. end
  171.  
  172. # A point can always become converted to a point :)
  173. def to_point
  174. dup
  175. end
  176.  
  177. # The components of the point as array
  178. def to_a
  179. @components.dup
  180. end
  181.  
  182. # String representation of point
  183. def to_s
  184. @components.map { |c| c.inspect }.join(", ")
  185. end
  186.  
  187. def inspect # :nodoc:
  188. "#<Point #{self}>"
  189. end
  190.  
  191. def dup # :nodoc:
  192. Point.new(*@components)
  193. end
  194. end
  195. end
Add Comment
Please, Sign In to add comment