Compare two images in terms of width and height - ruby

EDIT: I believe I didn't state correctly my question, so here is the edit.
I want to be able to compare (and score) a set of images with one image in terms of width and height.
Ideally, I would have a BASE_SCORE value (for example 100) that would be used in order to score each image depending on how close they look to the main image (in terms of width and height).
So, if for example, the main image looks like {:width => 100, :height => 100}, and set_images look like [{:width => 100, :height => 100}, {:width => 10, :height => 40}], the first element would have a score of BASE_SCORE, because they look exactly the same.
I fail to see how to compare width/heights in order to score each element of set_images.

Is there a problem with just using the Euclidean distance? Zero represents equality:
def euclidean_distance(a, b)
dx = a[:width] - b[:width]
dy = a[:height] - b[:height]
Math.sqrt((dx * dx) + (dy * dy))
end
test_subject = { width: 200, height: 50 }
samples = [
{ width: 100, height: 100 },
{ width: 80, height: 200 },
{ width: 200, height: 50 },
{ width: 10, height: 10 }
]
distances = samples.map { |s| euclidean_distance(test_subject, s) }
samples.zip(distances) { |img, dist| puts "#{img[:width]}x#{img[:height]} => #{dist}" }
Output:
100x100 => 111.80339887498948
80x200 => 192.09372712298546
200x50 => 0.0
10x10 => 194.164878389476
You can then use sort easily enough:
sorted = samples.sort { |a, b| euclidean_distance(test_subject, a) <=> euclidean_distance(test_subject, b) }

Something like this seems to work. Excuse the formatting...
$ cat foo.rb
require 'pp'
main_image = {:width => 100, :height => 50}
set_of_images = [{:width => 200, :height => 300, :id => 2},
{:width => 100, :height => 50, :id => 9}]
aspect_ratio = main_image[:width] / main_image[:height].to_f
sorted_images = set_of_images.
map{|i| i[:score] = (aspect_ratio - i[:width]/i[:height].to_f).abs; i}.
sort_by{|i| i[:score]}
pp sorted_images
$ ruby foo.rb
[{:width=>100, :height=>50, :id=>9, :score=>0.0},
{:width=>200, :height=>300, :id=>2, :score=>1.3333333333333335}]

Related

Get coordinate with NaN at x axis

i got the following block of code to generate a new enemy each 1.5.
each new enemy is added to an array using scan operator
i did the replace suggested.
i did a small change to be able to replicate
const enemies$ = rxjs.from([0,1])
.pipe(
rxjs.scan( (enemyArray) => {
const enemy = {
x: Math.floor(Math.random() * 100),
y: -30
}
console.log(enemy)
enemyArray.push(enemy);
console.log(enemyArray); //debug.
return enemyArray;
}, [])
);
enemies$.subscribe(
(enemies) => console.log(enemies)
);
The result in the console is the following for the first element (enemy)
{x: 312, y: -30}
But when the enemy is added to enemyArray , the following results are shown in the console
(1)[{...}]
0: {x: NaN, y: 515}
1: {x: NaN, y: 65}
length: 2
[[Prototype]]: Array(0)
parseInt() takes a string as the first argument, so it should be
parseInt(String(Math.random() * 100), 10)

Display number of times click on screen using Gosu.button_down

I am working on a ruby program using Gosu. I want to display the number of mouse click times every time I click on a button on the screen. Please help me with this. Thank you so much
require 'rubygems'
require 'gosu'
module ZOrder
BACKGROUND, MIDDLE, TOP = *0..2
end
WIN_WIDTH = 640
WIN_HEIGHT = 400
class DemoWindow < Gosu::Window
def initialize
super(WIN_WIDTH, WIN_HEIGHT, false)
#background = Gosu::Color::WHITE
#button_font = Gosu::Font.new(20)
#info_font = Gosu::Font.new(10)
#locs = [60,60]
end
def draw
Gosu.draw_rect(0, 0, WIN_WIDTH, WIN_HEIGHT, #background, ZOrder::BACKGROUND, mode=:default)
if area_clicked(mouse_x, mouse_y)
draw_line(50, 50, Gosu::Color::BLACK, 150, 50, Gosu::Color::BLACK, ZOrder::TOP, mode=:default)
draw_line(50, 50, Gosu::Color::BLACK, 50, 100, Gosu::Color::BLACK, ZOrder::TOP, mode=:default)
draw_line(150, 50, Gosu::Color::BLACK, 150, 101, Gosu::Color::BLACK, ZOrder::TOP, mode=:default)
draw_line(50, 100, Gosu::Color::BLACK, 150, 100, Gosu::Color::BLACK, ZOrder::TOP, mode=:default)
end
Gosu.draw_rect(50, 50, 100, 50, Gosu::Color::GREEN, ZOrder::MIDDLE, mode=:default)
#button_font.draw_text("Click me", 60, 60, ZOrder::TOP, 1.0, 1.0, Gosu::Color::BLACK)
#info_font.draw_text("mouse_x: #{mouse_x}", 0, 350, ZOrder::TOP, 1.0, 1.0, Gosu::Color::BLACK)
#info_font.draw_text("mouse_y: #{mouse_y}", 100, 350, ZOrder::TOP, 1.0, 1.0, Gosu::Color::BLACK )
if Gosu.button_down? Gosu::MsLeft
index =0
button = area_clicked(mouse_x, mouse_y)
case button
when 1
index+=1
#info_font.draw("Times Click: #{index}", 370, 350, ZOrder::TOP, 1.0, 1.0, Gosu::Color::BLACK)
end
end
end
def needs_cursor?; true; end
def area_clicked(mouse_x, mouse_y)
if ((mouse_x > 50 and mouse_x < 150) and (mouse_y > 50 and mouse_y < 100))
return 1
else
end
end
def button_down(id)
if id == Gosu::KB_ESCAPE
close
else
super
end
end
end
DemoWindow.new.show
Below is the code I have added. When I click on the button, it only displays the number of times click is 1, however, I need it to display the number of times I clicked on.
This is because you're setting index back to zero every time the mouse is clicked (index =0). You should probably set your index to zero in your initialize function, and make it a class variable (add # to the front).
Then your initialize code would be:
def initialize
super(WIN_WIDTH, WIN_HEIGHT, false)
#background = Gosu::Color::WHITE
#button_font = Gosu::Font.new(20)
#info_font = Gosu::Font.new(10)
#locs = [60,60]
#index = 0
end
and the code for the mouse click would be:
if Gosu.button_down? Gosu::MsLeft
button = area_clicked(mouse_x, mouse_y)
case button
when 1
#index += 1
#info_font.draw("Times Click: #{#index}", 370, 350, ZOrder::TOP, 1.0, 1.0, Gosu::Color::BLACK)

d3.scaleLog ticks with base 2

I trying to produce ticks for scaleLog().base(2).
Seems to be, it does not work correctly.
For instance, for the call:
d3.scaleLog().base(2).domain([50,500]).ticks(10)
I got:
[ 50, 100, 150, 200, 250, 300, 350, 400, 450, 500 ]
Which just linear placed ticks. For base(10) it works properly.
d3.scaleLog().base(10).domain([50,500]).ticks(10)
[ 50, 60, 70, 80, 90, 100, 200, 300, 400, 500 ]
I using d3.js version 6.1.1.
I am missing something?
You're not missing anything, but there is this line, inside the source code:
if (z.length * 2 < n) z = ticks(u, v, n);
Here, z is the generated array (in this case [64, 128, 256]), n is the required number of ticks (10), and u and v are the domain (50 and 500).
Because the number of generated ticks is too low, d3 defaults to a linear scale. Try one of the following instead:
console.log(d3.scaleLog().base(2).domain([50, 500]).ticks(6));
console.log(d3.scaleLog().base(2).domain([32, 512]).ticks(10));
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.1.1/d3.min.js"></script>
If all parameters are variable, you could calculate the maximum possible number of ticks and use that as an upper bound:
const domain = [50, 500];
const ticks = 100;
console.log(d3.scaleLog().base(2).domain(domain).ticks(ticks));
function getNTicks(domain, ticks) {
const maxPossibleTicks = Math.floor(Math.log2(domain[1]) - Math.log2(domain[0]));
return Math.min(ticks, maxPossibleTicks);
}
console.log(d3.scaleLog().base(2).domain(domain).ticks(getNTicks(domain, ticks)));
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.1.1/d3.min.js"></script>

Query Mongoid for all circle areas that include a given point

Let say I have two classes:
class Cirle
include Mongoid::Document
field :lat, type: Float
field :lon, type: Float
field :radius, type: Integer
end
class Point
include Mongoid::Document
field :lat, type: Float
field :lon, type: Float
end
How can I find all Circles that include a given Point?
I'm not familiar with Mongoid, but perhaps the following will help. Suppose:
circles = [
{ x: 1, y: 2, radius: 3 },
{ x: 3, y: 1, radius: 2 },
{ x: 2, y: 2, radius: 4 },
]
and
point = { x: 4.5, y: 1 }
then the circles containing point are obtained with the help of Math::hypot:
circles.select { |c|
Math.hypot((c[:x]-point[:x]).abs, (c[:y]-point[:y]).abs) <= c[:radius] }
#=> [{ x: 3, y: 1, radius: 2 }, { x: 2, y: 2, radius: 4 }]
Edit: to improve efficiency as #Drenmi suggests:
x, y = point.values_at(:x, :y)
circles.select do |c|
d0, d1, r = (c[:x]-x).abs, (c[:y]-y).abs, c[:radius]
d0*d0 + d1*d1 <= r*r
end

How do I generate a hash values of 3 arrays in Ruby?

Given I have 3 arrays that are mapped to each other.
fruit = ['apple', 'avocado', 'banana']
color = ['red', 'purple', 'yellow']
price = [30, 20, 50]
How do create an array of hashes with the following value
[
{fruit: 'apple', color: 'red', price: 30},
{fruit: 'avocado', color: 'purple', price: 20},
{fruit: 'banana', color: 'yellow', price: 50}
]
You can use zip to interleave the arrays, and then map them into an array of hashes:
fruit.zip(color, price).map { |f, c, p| { fruit: f, color: c, price: p } }
# => [{:fruit=>"apple", :color=>"red", :price=>30},
# => {:fruit=>"avocado", :color=>"purple", :price=>20},
# => {:fruit=>"banana", :color=>"yellow", :price=>50}
# => ]

Resources