Edge check in cartesian coordinate system - ruby

I have a cartesian plane with fixed dimensions
WIDTH, HEIGHT = 10, 20
I want to determine if a point represented as an ordered pair is an edge or a corner of that plane. For example, [0, 0] and [9, 19] are corners; [0, 5] and [6, 19] are edges.
This is my code:
# max allowable x and y coordinates
MAXX = WIDTH - 1
MAXY = HEIGHT - 1
# is this coordinate at the corner of the plane?
def corner?(*coords)
coords.uniq == [0] || # lower-left corner
coords == [MAXX, MAXY] || # upper-right corner
coords == [MAXX, 0] || # lower-right corner
coords == [0, MAXY] # upper-left corner
end
# is this coordinate at the edge of the plane?
def edge?(*coords)
return (
(
coords.include?(0) || # if x or y coordinate is at
coords[0] == MAXX || # its min or max, the coordinate
coords[1] == MAXY # is an edge coordinate
) &&
!corner?(coords) # it's not an edge if it's a corner
)
end
It gives these results, which I would expect:
corner?(0, 0) #=> true
corner?(0, 5) #=> false
edge?(0, 5) #=> true
corner?(5, 5) #=> false
edge?(5, 5) #=> false
However, while I expect the following:
edge?(0, 0) #=> true
it gives
edge?(0, 0) #=> false
What am I doing wrong?

Note that in your edge? method, you are calling:
def edge?(*coords)
#...
!corner?(coords)
end
in which coords is a single Array, i.e, you are calling corner?([0, 0]), not corner?(0, 0). Instead, expand the argument like this:
def edge?(*coords)
#...
!corner?(*coords)
end

Related

algorithm to convert array integers into RGB value (similar to excel)

Given an array of positive and negative integers...
I would like to return a green or red gradient color relative to its value in the array.
Similar to excel conditional formatting, the greens should be darker the closer they are to the maximum. lighter if they're closer to the minimum.
SIMILAR TO THIS IMAGE BELOW:
Currently i'm doing this
if value == Float::INFINITY
"rgba(0,255,0,1)"
elsif value > 0
"rgba(0,255,0, #{Rational(value, maximum).to_f.round(2)})"
else
"rgba(255,0,0,#{Rational(value, minimum).to_f.abs.round(2)})"
end
Suppose you have an array of floats (or strings or BigDecimals converted to floats):
arr = [
[25.1, 13.5, 4.3],
[28.3, 11.6, 5.9],
[16.5, 17.3, 6.4]
]
It's of course arbitrary how these numbers are to be converted to shades of red and green, but here is one possibility. Suppose we compute:
mn, mx = arr.flatten.minmax
#=> [4.3, 28.3]
av = (mn+mx).fdiv(2)
#=> 16.3
Then the red hues could decrease linearly from 255 at 28.3 to 0 at 16.3 and the green hues could increase linearly from 0 at 16.3 to 255 at 4.3:
def rg_gradient(arr)
mn, mx = arr.flatten.minmax
av = (mn+mx).fdiv(2)
above = mx-av
below = av-mn
arr.map do |a|
a.map { |n| n > av ? [(255*(n-av)/above).round, 0] :
[0, (255*(1-(av-n)/below)).round] }
end
end
rg_gradient(arr)
#=> [[[187, 0], [ 0, 195], [0, 0]],
# [[255, 0], [ 0, 155], [0, 34]],
# [[ 4, 0], [21, 0], [0, 45]]]

Is it possible to get principal point from a projection matrix?

Is it possible to get principal point (cx, cy) from a 4x4 projection matrix? This is the same matrix asked in this question: Getting focal length and focal point from a projection matrix
(SCNMatrix4)
s = (m11 = 1.83226573,
m12 = 0,
m13 = 0,
m14 = 0,
m21 = 0,
m22 = 2.44078445,
m23 = 0,
m24 = 0,
m31 = -0.00576340035,
m32 = -0.0016724075,
m33 = -1.00019991,
m34 = -1,
m41 = 0,
m42 = 0,
m43 = -0.20002,
m44 = 0)
The values I'm trying to calculate in this 3x3 camera matrix is x0 and y0.
I recently confronted this problem, and quite astonished I couldn't find a relevant solution on Internet, because it seems to be a simple mathematics problem.
After a few days of struggling with matrices, I found a solution.
Let's define two Cartesian coordinate system, the camera coordinate system with x', y', z' axes, and the world coordinate system with x, y, z axes. The camera(or the eye) is positioned at the origin of the camera coordinate system and the image plane(a plane containing the screen) is z' = -n, where n is the focal length and the focal point is the position of the camera. I am using the convention of OpenGL and n is the nearVal argument of the glFrustum().
You can define a 4x4 transformation matrix M in a homogeneous coordinate system to deal with a projection. The M transforms a coordinate (x, y, z) in the world coordinate system into a coordinate (x', y', z') in the camera coordinate system like the following, where # means a matrix multiplication.
[
[x_prime_h],
[y_prime_h],
[z_prime_h],
[w_prime_h],
] = M # [
[x_h],
[y_h],
[z_h],
[w_h],
]
[x, y, z] = [x_h, y_h, z_h] / w_h
[x_prime, y_prime, z_prime] = [x_prime_h, y_prime_h, z_prime_h] / w_prime_h
Now assume you are given M = P V, where P is a perspective projection matrix and V is a view transformation matrix. The theoretical projection matrix is like the following.
P_theoretical = [
[n, 0, 0, 0],
[0, n, 0, 0],
[0, 0, n, 0],
[0, 0, -1, 0],
]
In OpenGL, an augmented matrix like the following is used to cover the normalization and nonlinear scaling on z coordinates, where l, r, b, t, n, f are the left, right, bottom, top, nearVal, farVal arguments of the glFrustum().(The resulting z' coordinate is not actually the coordinate of a projected point, but a value used for Z-buffering.)
P = [
[2*n/(r-l), 0, (r+l)/(r-l), 0],
[0, 2*n/(t-b), (t+b)/(t-b), 0],
[0, 0, -(f+n)/(f-n), -2*n*f/(f-n)],
[0, 0, -1, 0],
]
The transformation V is like the following, where r_ij is the element at i-th row and j-th column of the 3x3 rotational matrix R and (c_0, c_1, c_2) is the coordinate of the camera.
V = [
[r_00, r_01, r_02, -(r_00*c_0 + r_01*c_1 + r_02*c_2)],
[r_10, r_11, r_12, -(r_10*c_0 + r_11*c_1 + r_12*c_2)],
[r_20, r_21, r_22, -(r_20*c_0 + r_21*c_1 + r_22*c_2)],
[0, 0, 0, 1],
]
The P and V can be represented with block matrices like the following.
C = [
[c_0],
[c_1],
[c_2],
]
A = [
[2*n/(r-l), 0, (r+l)/(r-l)],
[0, 2*n/(t-b), (t+b)/(t-b)],
[0, 0, -(f+n)/(f-n)],
]
B = [
[0],
[0],
[-2*n*f/(f-n)],
]
P = [
[A,B],
[[0, 0, -1], [0]],
]
V = [
[R, -R # C],
[[0, 0, 0], [1]],
]
M = P # V = [
[A # R, -A # R # C + B],
[[0, 0, -1] # R, [0, 0, 1] # R # C],
]
Let m_ij be the element of M at i-th row and j-th column. Taking the first element of the second row of the above block notation of M, you can solve for the elementary z' vector of the camera coordinate system, the opposite direction from the camera point to the intersection point between the image plane and its normal line passing through the focal point.(The intersection point is the principal point.)
e_z_prime = [0, 0, 1] # R = -[m_30, m_31, m_32]
Taking the second column of the above block notation of M, you can solve for C like the following, where inv(X) is an inverse of a matrix X.
C = - inv([
[m_00, m_01, m_02],
[m_10, m_11, m_12],
[m_30, m_31, m_32],
]) # [
[m_03],
[m_13],
[m_33],
]
Let p_ij be the element of P at i-th row and j-th column.
Now you can solve for p_23 = -2nf/(f-n) like the following.
B = [
[m_03],
[m_13],
[m_23],
] + [
[m_00, m_01, m_02],
[m_10, m_11, m_12],
[m_20, m_21, m_22],
] # C
p_23 = B[2] = m_23 + (m_20*c_0 + m_21*c_1 + m_22*c_2)
Now using the fact p_20 = p_21 = 0, you can get p_22 = -(f+n)/(f-n) like the following.
p_22 * e_z_prime = [m_20, m_21, m_22]
p_22 = -(m_20*m_30 + m_21*m_31 + m_22*m_32)
Now you can get n and f from p_22 and p_23 like the following.
n = p_23/(p_22-1)
= -(m_23 + m_20*c_0+m_21*c_1+m_22*c_2) / (m_20*m_30+m_21*m_31+m_22*m_32 + 1)
f = p_23/(p_22+1)
= -(m_23 + m_20*c_0+m_21*c_1+m_22*c_2) / (m_20*m_30+m_21*m_31+m_22*m_32 - 1)
From the camera position C, the focal length n and the elementary z' vector e_z_prime, you can get the principal point, C - n * e_z_prime.
As a side note, you can prove the input matrix of inv() in the formula for getting C is nonsingular. And you can also find elementary x' and y' vectors of the camera coordinate system, and find the l, r, b, t using these vectors.(There will be two valid solutions for the (e_x_prime, e_y_prime, l, r, b, t) tuple, due to the symmetry.) And finally this solution can be expanded when the transformation matrix is mixed with the world transformation which does an anisotropic scaling, that is when M = P V W and W can have unequal eigenvalues.

How to track and look up coordinates in a 2d array

I am working on a problem from adventofcode.com. I need to track the movement on an undetermined grid. I created a move function that uses the case method like this.
def move(direction)
case move
when ">"
x += 1
when "<"
x -= 1
when "^"
y += 1
when "v"
y -= 1
end
end
I have to keep track of coordinates visited and I thought of creating a 2d array that keeps track and pushes to the back of it when we visit a new location. What I dont know is how to do keep track of the unique visited locations with an if statement.
If you only need to keep track of visited coordinates and their order doesn't matter, then you should use a Set instead:
visited = Set.new
visited << [0,0]
visited << [1,0]
visited << [1,1]
visited << [1,0]
p visited
# => #<Set: {[0, 0], [1, 0], [1, 1]}>
p visited.include?([1,1])
# => true
If for some reason you can't use a Set you can accomplish the same thing with a Hash (this is basically how Set works under the covers):
visited = {}
visited[ [0,0] ] = true
visited[ [1,0] ] = true
# Or:
visited.store([0, 0], true)
p visited
# { [0, 0] => true,
# [1, 0] => true
# }
p visited[ [1,0] ] # => true
p visited[ [2,5] ] # => nil
# Or:
p visited.key?([2,5]) # => false
Here's an example of how you could use Set in a Grid class:
require 'set'
class Grid
attr_reader :visited
def initialize
#visited = Set.new
end
def visit!(x, y)
visited << [x, y]
end
def visited?(x, y)
visited.include?([x, y])
end
end
grid = Grid.new
grid.visit!(0, 0)
grid.visit!(1, 0)
grid.visit!(1, 1)
grid.visit!(1, 0)
p grid.visited
# => #<Set: {[0, 0], [1, 0], [1, 1]}>
p grid.visited?(1, 0) # => true
p grid.visited?(3, 3) # => false

Ruby count duplicates in diagonal rows of matrix

I'm implementing gomoku game in Ruby, this is a variation of tic-tac-toe played on 15x15 board, and the first player who places 5 O's or X's in horizontal, vertical or diagonal row wins.
First, I assigning Matrix to a variable and fill it with numbers from 0 to 224, so there are no repetitions and I could count them later
gomoku = Matrix.zero(15)
num = 0
15.times do |i|
15.times do |j|
gomoku[i, j] = num
num += 1
end
end
then players take turns, and after every turn I check a win with the method win?
def win? matrix
15.times do |i|
return true if matrix.row_vectors[i].chunk{|e| e}.map{|_, v| v.length}.max > 4 # thanks to sawa for this way of counting adjacent duplicates
return true if matrix.column_vectors[i].chunk{|e| e}.map{|_, v| v.length}.max > 4
end
return false
end
I know, that I'm probably doing it wrong, but my problem isn't that, though suggestions are welcome. The problem is with diagonal rows. I don't know how to count duplicates in diagonal rows
diagonal_vectors = (-10 .. 10).flat_map do |x|
i = x < 0 ? 0 : x
j = x < 0 ? -x : 0
d = 15 - x.abs
[
d.times.map { |k|
gomoku[i + k, j + k]
},
d.times.map { |k|
gomoku[i + k, 14 - j - k]
}
]
end
With this, you can apply the same test sawa gave you.
EDIT: What this does
When looking at diagonals, there's two kinds: going down-left, and going down-right. Let's focus on down-right ones for now. In a 15x15 matrix, there are 29 down-right diagonals: one starting at each element of the first row, one starting at each element of the first column, but taking care not to count the one starting at [0, 0] twice. But some diagonals are too short, so we want to only take those that start on the first eleven rows and columns (because others will be shorter than 5 elements). This is what the first three lines do: [i, j] will be [10, 0], [9, 0] ... [0, 0], [0, 1], ... [0, 10]. d is the length of a diagonal starting at that position. Then, d.times.map { |k| gomoku[i + k, j + k] } collects all the elements in that diagonal. Say we're working on [10, 0]: d is 5, so we have [10, 0], [11, 1], [12, 2], [13, 3], [14, 4]; and we collect values at those coordinates in a list. Simultaneously, we'll also work on a down-left diagonal; that's the other map's job, which flips one coordinate. Thus, the inner block will return a two-element array, which is two diagonals, one down-left, one down-right. flat_map will take care of iterating while squishing the two-element arrays so that we get one big array of diagonals, not array of two-element arrays of diagonals.

What is the proper syntax for multiple comparisons?

Is there a proper syntax in Ruby for comparing multiple values against the same variable? For example:
#!/usr/bin/ruby -w
y = 15
p 'success' if y == 1 || y == 5 || y == -2 || y == 15132 || y == 3.14159265 || y == 15
Can that be written as something along the lines of:
y = 15
p 'success' if y == 1,5,-2,15132,3.14159265,15
And, if so, would that also apply to while loops?
y = 15
while y != 1,5,-2,15132,3.14159265,15
y = rand(50)
p y
end
Based on my search I'm tending to believe this is either not possible, or it's too obscure for my searches.
I hope it's the second case.
I have already considered an array itteration solution, but it's not as pretty or simple as I'd like.
You're looking for include?
p 'success' if [1,5,-2,15132,3.14159265,15].include? y
p 'success' if [1, 5, -2, 15132, 3.14159265, 15].include? y
case y
when 1, 5, -2, 15132, 3.14159265, 15 then p "success"
end
For a more general case you can use the any? method with a comparison block; this has the advantage of being usable with operators apart from ==:
p 'success' if [1, 5, -2, 15132, 3.14159265, 15].any? { |i| i == y }
From the Array#index :
Returns the index of the first object in ary such that the object is == to obj.Returns nil if no match is found.
p 'success' if [1,5,-2,15132,3.14159265,15].index(y)

Resources