diff options
| -rw-r--r-- | lib/ruby2d/image.rb | 4 | ||||
| -rw-r--r-- | lib/ruby2d/line.rb | 17 | ||||
| -rw-r--r-- | lib/ruby2d/quad.rb | 18 | ||||
| -rw-r--r-- | lib/ruby2d/rectangle.rb | 4 | ||||
| -rw-r--r-- | lib/ruby2d/renderable.rb | 4 | ||||
| -rw-r--r-- | lib/ruby2d/text.rb | 4 | ||||
| -rw-r--r-- | lib/ruby2d/triangle.rb | 17 | ||||
| -rw-r--r-- | test/contains.rb | 32 | ||||
| -rw-r--r-- | test/image_spec.rb | 16 | ||||
| -rw-r--r-- | test/line_spec.rb | 23 | ||||
| -rw-r--r-- | test/quad_spec.rb | 26 | ||||
| -rw-r--r-- | test/rectangle_spec.rb | 18 | ||||
| -rw-r--r-- | test/text_spec.rb | 15 | ||||
| -rw-r--r-- | test/triangle_spec.rb | 24 |
14 files changed, 221 insertions, 1 deletions
diff --git a/lib/ruby2d/image.rb b/lib/ruby2d/image.rb index 6660a4d..2691f78 100644 --- a/lib/ruby2d/image.rb +++ b/lib/ruby2d/image.rb @@ -26,5 +26,9 @@ module Ruby2D def color=(c) @color = Color.new(c) end + + def contains?(x, y) + @x < x and @x + @width > x and @y < y and @y + @height > y + end end end diff --git a/lib/ruby2d/line.rb b/lib/ruby2d/line.rb index 9ef534e..769c37f 100644 --- a/lib/ruby2d/line.rb +++ b/lib/ruby2d/line.rb @@ -19,8 +19,25 @@ module Ruby2D update_color(@color) end + def length + points_distance(@x1, @y1, @x2, @y2) + end + + # Line contains a point if the point is closer than the length of line from both ends + # and if the distance from point to line is smaller than half of the width. + # Check https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line for reference + def contains?(x, y) + points_distance(x1, y1, x, y) < length and + points_distance(x2, y2, x, y) < length and + (((@y2 - @y1) * x - (@x2 - @x1) * y + @x2 * @y1 - @y2 * @x1).abs / length) < 0.5 * @width + end + private + def points_distance(x1, y1, x2, y2) + Math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2) + end + def update_color(c) if c.is_a? Color::Set if c.length == 4 diff --git a/lib/ruby2d/quad.rb b/lib/ruby2d/quad.rb index e3906c0..4fac328 100644 --- a/lib/ruby2d/quad.rb +++ b/lib/ruby2d/quad.rb @@ -29,8 +29,26 @@ module Ruby2D update_color(@color) end + # The logic is the same as for a triangle + # See triangle.rb for reference + def contains?(x, y) + self_area = triangle_area(@x1, @y1, @x2, @y2, @x3, @y3) + + triangle_area(@x1, @y1, @x3, @y3, @x4, @y4) + + questioned_area = triangle_area(@x1, @y1, @x2, @y2, x, y) + + triangle_area(@x2, @y2, @x3, @y3, x, y) + + triangle_area(@x3, @y3, @x4, @y4, x, y) + + triangle_area(@x4, @y4, @x1, @y1, x, y) + + questioned_area <= self_area + end + private + def triangle_area(x1, y1, x2, y2, x3, y3) + (x1*y2 + x2*y3 + x3*y1 - x3*y2 - x1*y3 - x2*y1).abs / 2 + end + def update_color(c) if c.is_a? Color::Set if c.length == 4 diff --git a/lib/ruby2d/rectangle.rb b/lib/ruby2d/rectangle.rb index 569b8b5..db54b46 100644 --- a/lib/ruby2d/rectangle.rb +++ b/lib/ruby2d/rectangle.rb @@ -39,6 +39,10 @@ module Ruby2D update_coords(@x, @y, @width, h) end + def contains?(x, y) + @x < x and @x + @width > x and @y < y and @y + @height > y + end + private def update_coords(x, y, w, h) diff --git a/lib/ruby2d/renderable.rb b/lib/ruby2d/renderable.rb index d80482a..8a581ae 100644 --- a/lib/ruby2d/renderable.rb +++ b/lib/ruby2d/renderable.rb @@ -27,5 +27,9 @@ module Ruby2D def opacity=(val) self.color.opacity = val end + + def contains?(x, y) + raise "Not implemented yet" + end end end diff --git a/lib/ruby2d/text.rb b/lib/ruby2d/text.rb index d3c525f..c036a92 100644 --- a/lib/ruby2d/text.rb +++ b/lib/ruby2d/text.rb @@ -33,6 +33,10 @@ module Ruby2D @color = Color.new(c) end + def contains?(x, y) + @x < x and @x + @width > x and @y < y and @y + @height > y + end + private def resolve_path(font) diff --git a/lib/ruby2d/triangle.rb b/lib/ruby2d/triangle.rb index eb6b07f..e3b091b 100644 --- a/lib/ruby2d/triangle.rb +++ b/lib/ruby2d/triangle.rb @@ -25,8 +25,25 @@ module Ruby2D update_color(@color) end + # Point is inside a triangle if + # the area of 3 triangles, constructed from triangle sides and that point + # is equal to the area of triangle. + def contains?(x, y) + self_area = triangle_area(@x1, @y1, @x2, @y2, @x3, @y3) + questioned_area = + triangle_area(@x1, @y1, @x2, @y2, x, y) + + triangle_area(@x2, @y2, @x3, @y3, x, y) + + triangle_area(@x3, @y3, @x1, @y1, x, y) + + questioned_area <= self_area + end + private + def triangle_area(x1, y1, x2, y2, x3, y3) + (x1*y2 + x2*y3 + x3*y1 - x3*y2 - x1*y3 - x2*y1).abs / 2 + end + def update_color(c) if c.is_a? Color::Set if c.length == 3 diff --git a/test/contains.rb b/test/contains.rb new file mode 100644 index 0000000..5a11064 --- /dev/null +++ b/test/contains.rb @@ -0,0 +1,32 @@ +require 'ruby2d' + +set title: "Ruby 2D — Contains", height: 350 + +if RUBY_ENGINE == 'opal' + media = "../test/media" + font = "sans-serif" +else + media = "media" + font = "#{media}/bitstream_vera/vera.ttf" +end + +objects = [] +objects.push Square.new(50, 50, 100) +objects.push Rectangle.new(200, 50, 100, 75) +objects.push Quad.new(350, 50, 500, 75, 450, 150, 375, 125) +objects.push Triangle.new(550, 50, 600, 125, 500, 150) +objects.push Line.new(225, 175, 375, 225, 20) +objects.push Image.new(50, 200, "#{media}/colors.png") +objects.push Text.new(450, 200, "Hello", 50, font) + +on :key_down do |event| + close if event.key == 'escape' +end + +update do + objects.each do |o| + o.contains?(get(:mouse_x), get(:mouse_y)) ? o.opacity = 1.0 : o.opacity = 0.5 + end +end + +show diff --git a/test/image_spec.rb b/test/image_spec.rb index 3eb5d86..33dee35 100644 --- a/test/image_spec.rb +++ b/test/image_spec.rb @@ -1,11 +1,25 @@ require 'ruby2d' RSpec.describe Ruby2D::Image do - describe '#new' do it "raises exception if image file doesn't exist" do expect { Image.new(0, 0, 'bad_image.png') }.to raise_error(Ruby2D::Error) end end + # Image has 100 width and 100 height + describe '#contains?' do + it "returns true if point is inside image" do + image = Image.new(0, 0, "test/media/image.bmp") + expect(image.contains?(50, 50)).to be true + end + + it "returns true if point is not inside image" do + image = Image.new(0, 0, "test/media/image.bmp") + expect(image.contains?(-50, 50)).to be false + expect(image.contains?(50, -50)).to be false + expect(image.contains?(50, 150)).to be false + expect(image.contains?(150, 50)).to be false + end + end end diff --git a/test/line_spec.rb b/test/line_spec.rb new file mode 100644 index 0000000..04445d1 --- /dev/null +++ b/test/line_spec.rb @@ -0,0 +1,23 @@ +require 'ruby2d' + +RSpec.describe Ruby2D::Triangle do + describe '#contains?' do + it "returns true if point is inside line" do + line = Line.new( + 0, 0, + 100, 100 + ) + expect(line.contains?(25, 25)).to be true + end + + it "returns true if point is inside text" do + line = Line.new( + 0, 0, + 100, 100 + ) + + expect(line.contains?(0, 10)).to be false + expect(line.contains?(10, 0)).to be false + end + end +end diff --git a/test/quad_spec.rb b/test/quad_spec.rb index 58f2dfd..a6cfca4 100644 --- a/test/quad_spec.rb +++ b/test/quad_spec.rb @@ -89,4 +89,30 @@ RSpec.describe Ruby2D::Quad do end.to raise_error("Quads require 4 colors, one for each vertex. 5 were given.") end end + + describe '#contains?' do + it "returns true if point is inside quad" do + quad = Quad.new( + -25, 0, + 0, -25, + 25, 0, + 0, 25 + ) + expect(quad.contains?(0, 0)).to be true + end + + it "returns true if point is not inside quad" do + quad = Quad.new( + -25, 0, + 0, -25, + 25, 0, + 0, 25 + ) + + expect(quad.contains?( 20, 20)).to be false + expect(quad.contains?(-20, 20)).to be false + expect(quad.contains?( 20, -20)).to be false + expect(quad.contains?(-20, -20)).to be false + end + end end diff --git a/test/rectangle_spec.rb b/test/rectangle_spec.rb new file mode 100644 index 0000000..493f3d1 --- /dev/null +++ b/test/rectangle_spec.rb @@ -0,0 +1,18 @@ +require 'ruby2d' + +RSpec.describe Ruby2D::Rectangle do + describe '#contains?' do + it "returns true if point is inside rectangle" do + rectangle = Rectangle.new(0, 0, 50, 50) + expect(rectangle.contains?(25, 25)).to be true + end + + it "returns true if point is not inside rectangle" do + rectangle = Rectangle.new(0, 0, 50, 50) + expect(rectangle.contains?(-25, 25)).to be false + expect(rectangle.contains?(25, -25)).to be false + expect(rectangle.contains?(25, 50)).to be false + expect(rectangle.contains?(50, 25)).to be false + end + end +end diff --git a/test/text_spec.rb b/test/text_spec.rb index ad12cbf..15038d2 100644 --- a/test/text_spec.rb +++ b/test/text_spec.rb @@ -41,4 +41,19 @@ RSpec.describe Ruby2D::Text do expect(t.height).to eq(48) end end + + describe '#contains?' do + it "returns true if point is inside text" do + text = Text.new(0, 0, "Hello world!", 40, "test/media/bitstream_vera/vera.ttf") + expect(text.contains?(text.width / 2, text.height / 2)).to be true + end + + it "returns true if point is not inside text" do + text = Text.new(0, 0, "Hello world!", 40, "test/media/bitstream_vera/vera.ttf") + expect(text.contains?( - text.width / 2, text.height / 2)).to be false + expect(text.contains?( text.width / 2, - text.height / 2)).to be false + expect(text.contains?(3 * text.width / 2, text.height / 2)).to be false + expect(text.contains?( text.width / 2, 3 * text.height / 2)).to be false + end + end end diff --git a/test/triangle_spec.rb b/test/triangle_spec.rb index f71b097..b562e59 100644 --- a/test/triangle_spec.rb +++ b/test/triangle_spec.rb @@ -79,4 +79,28 @@ RSpec.describe Ruby2D::Triangle do end.to raise_error("Triangles require 3 colors, one for each vertex. 4 were given.") end end + + + describe '#contains?' do + it "returns true if point is inside triangle" do + triangle = Triangle.new( + 0, 0, + 0, 100, + 100, 0 + ) + expect(triangle.contains?(25, 25)).to be true + end + + it "returns true if point is inside text" do + triangle = Triangle.new( + 0, 0, + 0, 100, + 100, 0 + ) + + expect(triangle.contains?(25, -25)).to be false + expect(triangle.contains?(-25, 25)).to be false + expect(triangle.contains?(100, 100)).to be false + end + end end |
