From 90caa8c70ba61fd4b42efb50022bff98d0cf0235 Mon Sep 17 00:00:00 2001 From: David Kaczowka Date: Thu, 11 Aug 2016 12:19:16 -0500 Subject: [PATCH] Allow intersection_point_with to work with BigDecimal. The method should only convert the numerator to a float if both numerator and denominator are integers. --- lib/geometry/segment.rb | 34 +++++++++++--------- test/segment/intersection_point_with_test.rb | 13 ++++++++ 2 files changed, 32 insertions(+), 15 deletions(-) diff --git a/lib/geometry/segment.rb b/lib/geometry/segment.rb index b593d86..2f7f004 100644 --- a/lib/geometry/segment.rb +++ b/lib/geometry/segment.rb @@ -1,10 +1,10 @@ module Geometry class SegmentsDoNotIntersect < Exception; end class SegmentsOverlap < Exception; end - + class Segment < Struct.new(:point1, :point2) def self.new_by_arrays(point1_coordinates, point2_coordinates) - self.new(Point.new_by_array(point1_coordinates), + self.new(Point.new_by_array(point1_coordinates), Point.new_by_array(point2_coordinates)) end @@ -23,8 +23,8 @@ def topmost_endpoint def bottommost_endpoint ((point1.y <=> point2.y) == -1) ? point1 : point2 end - - def contains_point?(point) + + def contains_point?(point) Geometry.distance(point1, point2) === Geometry.distance(point1, point) + Geometry.distance(point, point2) end @@ -43,26 +43,30 @@ def intersects_with?(segment) lies_on_line_intersecting?(segment) && segment.lies_on_line_intersecting?(self) end - + def overlaps?(segment) Segment.have_intersecting_bounds?(self, segment) && lies_on_one_line_with?(segment) end - + def intersection_point_with(segment) raise SegmentsDoNotIntersect unless intersects_with?(segment) raise SegmentsOverlap if overlaps?(segment) - + numerator = (segment.point1.y - point1.y) * (segment.point1.x - segment.point2.x) - (segment.point1.y - segment.point2.y) * (segment.point1.x - point1.x); - denominator = (point2.y - point1.y) * (segment.point1.x - segment.point2.x) - + denominator = (point2.y - point1.y) * (segment.point1.x - segment.point2.x) - (segment.point1.y - segment.point2.y) * (point2.x - point1.x); - t = numerator.to_f / denominator; - + if numerator.is_a?(Integer) && denominator.is_a?(Integer) + numerator = numerator.to_f + end + + t = numerator / denominator; + x = point1.x + t * (point2.x - point1.x) - y = point1.y + t * (point2.y - point1.y) - + y = point1.y + t * (point2.y - point1.y) + Point.new(x, y) end @@ -91,13 +95,13 @@ def distance_to(point) return Geometry.distance(q, p) end - def length + def length Geometry.distance(point1, point2) end def to_vector Vector.new(point2.x - point1.x, point2.y - point1.y) - end + end protected @@ -107,7 +111,7 @@ def self.have_intersecting_bounds?(segment1, segment2) segment1.leftmost_endpoint.x == segment2.rightmost_endpoint.x) && (segment2.leftmost_endpoint.x < segment1.rightmost_endpoint.x || segment2.leftmost_endpoint.x == segment1.rightmost_endpoint.x) - + intersects_on_y_axis = (segment1.bottommost_endpoint.y < segment2.topmost_endpoint.y || segment1.bottommost_endpoint.y == segment2.topmost_endpoint.y) && diff --git a/test/segment/intersection_point_with_test.rb b/test/segment/intersection_point_with_test.rb index 6c62864..2cd0756 100644 --- a/test/segment/intersection_point_with_test.rb +++ b/test/segment/intersection_point_with_test.rb @@ -1,5 +1,6 @@ require 'minitest/autorun' require 'geometry' +require 'bigdecimal' class IntersectionPointWithTest < Minitest::Test include Geometry @@ -18,6 +19,18 @@ def test_segments_intersect_at_the_endpoint assert_equal Point.new(2, 2), segment1.intersection_point_with(segment2) end + def test_big_decimal_segments_intersect_at_the_endpoint + segment1 = Segment.new_by_arrays([BigDecimal.new('-109.775390625'), BigDecimal.new('42.734102391081')], + [BigDecimal.new('-91.23046875'), BigDecimal.new('42.734102391081')]) + + segment2 = Segment.new_by_arrays([BigDecimal.new('-91.23046875'), BigDecimal.new('42.734102391081')], + [BigDecimal.new('-91.23046875'), BigDecimal.new('34.147272023649')]) + + + assert_equal Point.new(BigDecimal.new('-91.23046875'), BigDecimal.new('42.734102391081')), + segment1.intersection_point_with(segment2) + end + def test_segments_do_not_intersect segment1 = Segment.new_by_arrays([0, 0], [0, 2]) segment2 = Segment.new_by_arrays([1, 1], [2, 1])