Selective Ruby array slicing - ruby

I've been slowly learning Ruby (at this point, maybe the first language I've invested any amount of time in actually learning) so this is probably going to be a very simple question for many of you.
My toy project for my studies is, basically, a roguelike. Currently, I have a Map class that contains an array of Tile objects representing, of course, each tile in the entire map. I'm attempting to create a method that will return a smaller array (most likely example would be to display the currently viewable area of the map).
My problem comes down to this. Since the array containing all these tiles is single-dimension, I can't seem to think of a clean way to slice pieces of this array out based on two x,y coordinates that the method takes to determine what to return. In other words, I can't seem to find a clean way to translate between the two coordinate pairs without some fairly ugly looking code and I get the idea that there's a very simple way to do this that is just not 'clicking'.
Ideas anyone? I'm open to some pretty crazy suggestions!

If your array is single dimention you can map x and y coords to an array index much like you do pixels on a vga buffer.
offset = y * buffer_width + x
If your map tile width is 100 tiles and you wanted to get tile 5,5 then 5 * 100 + 5 = array index 505 would be the corresponding tile in your 1 dimensional array.
You can use this to piece together a viewable region on screen. For instance a 10x10 viewable tiles: Start at your offset and grab the next 10 items, add buffer_width to drop a row and add the next 10, and so on until you have all 10 rows for your 10x10 viewable area.
Here is an example on a smaller tile set with a tile buffer width of 5 and selecting a viewable 3x3 area:
buffer = ['0,0', '1,0', '2,0', '3,0', '4,0',
'0,1', '1,1', '2,1', '3,1', '4,1',
'0,2', '1,2', '2,2', '3,2', '4,2',
'0,3', '1,3', '2,3', '3,3', '4,3',
'0,4', '1,4', '2,4', '3,4', '4,4']
buffer_width = 5
buffer_height = 5
# now lets say we want to grab a 3x3 slice from right
# in the middle of the array from 1,1->3,3
x1,y1 = 1,1
x2,y2 = 3,3
view_width = x2 - x1
view_height = y2 - y1
(0..view_height).each do |row|
offset = (y1 + row) * buffer_width + x1
puts buffer[offset..offset+view_width].inspect
end
Our result will be an output like this:
["1,1", "2,1", "3,1"]
["1,2", "2,2", "3,2"]
["1,3", "2,3", "3,3"]
Which you can string together in a new single dimensional or multi dimensional, which ever you see fit.
Hope this helps.

You'll need to use use map on a slice of the array and map each row to a slice itself.
a = [['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']]
a[1..2].map { |row| row[0..1] }
=> [["d", "e"], ["g", "h"]]

Related

Automatically choose x locations of scatter plot in front of bar graph

I'd like an algorithm to organize a 2D cloud of points in front of a bar graph so that a viewer could easily see the spread of the data. The y location of the point needs to be equal/scaled/proportional to the value of the data, but the x location doesn't matter and would be determined by the algorithm. I imagine a good strategy would be to minimize overlap among the points and center the points.
Here is an example of such a plot without organizing the points:
I generate my bar graphs with points in front of it with MATLAB, but I'm interested just in the best way to choose the x location values of the points.
I have been organizing the points by hand afterwards in Adobe Illustrator, which is time-consuming. Any recommendations? Is this a sub-problem of an already solved problem? What is this kind of plot called?
For high sample sizes, I imagine something like the following would be better than a cloud of points.
I think, mathematically, starting with some array of y-values, it would maximize the sum of the difference between every element from every other element, inversely scaled by the distance between the elements, by rearranging the order of the elements in the array.
Here is the MATLAB code I used to generate the graph:
y = zeros(20,6);
yMean = zeros(1,6);
for i=1:6
y(:,i) = 5 + (8-5).*rand(20,1);
yMean(i) = mean(y(:,i));
end
figure
hold on
bar(yMean,0.5)
for i=1:6
x = linspace(i-0.3,i+0.3,20);
plot(x,y(:,i),'ro')
end
axis([0,7,0,10])
Here is one way that determines x-locations based on grouping into (histogram) bins. The result is similar to e.g. the plot in https://stackoverflow.com/a/1934882/4720018, but retains the original y-values. For convenience the points are sorted, but they could be displayed in order of appearance using the bin_index. Whether this is "the best way" of choosing the x-coordinates depends on what you are trying to achieve.
% Create some dummy data
dummy_data_y = 1+0.1*randn(10,3);
% Create bar plot (assuming you are interested in the mean)
bar_obj = bar(mean(dummy_data_y));
% Obtain data size info
n = size(dummy_data_y, 2);
% Algorithm that creates an x vector for each data column
sorted_data_y = sort(dummy_data_y, 'ascend'); % for convenience
number_of_bins = 5;
for j=1:n
% Get histogram information
[bin_count, ~, bin_index] = histcounts(sorted_data_y(:, j), number_of_bins);
% Create x-location data for current column
xj = [];
for k = 1:number_of_bins
xj = [xj 0:bin_count(k)-1];
end
% Collect x locations per column, scale and translate
sorted_data_x(:, j) = j + (xj-(bin_count(bin_index)-1)/2)'/...
max(bin_count)*bar_obj.BarWidth;
end
% Plot the individual data points
line(sorted_data_x, sorted_data_y, 'linestyle', 'none', 'marker', '.', 'color', 'r')
Whether this is a good way to display your data remains open to discussion.

MATLAB: Create movie from cell array of uint8 images

I have 20 grayscale images of type uint8 stored in a 1x20 cell array named flow8. I want to generate a movie from them. My current approach is:
% Generate images.
for i = 1:20
flow8{i} = round(rand(100, 100)*255+1);
end
% Get into 4-D shape.
n = size(flow8,2);
matSize = size(flow8,1);
imageStack = reshape(cell2mat(flow8),matSize,[],n);
imageStack = permute(imageStack, [1 2 4 3]);
% Create movie.
mov = immovie(imageStack, gray)
implay(mov)
Here, I have added an image generation loop to make the code compilable.
With this code, the generated movie consists of only one horizontal line.
What do I need to do to get a proper movie? Or is there a better way to make a movie from my images?
I am using MATLAB R2015b academic on Windows 7.
If you look closely at your code, flow8 is 1 x 20. When you do your reshaping, you compute matSize with:
matSize = size(flow8, 1)
Well, that value is 1, because as we said the shape of the cell array is 1 x 20.
Instead, you likely wanted the size of each image. In which case, you'll want to index into the cell array to get the value and then take the size of that.
matSize = size(flow8{1});
Potentially another (much shorter) way to do this though, it so use cat to concatenate along the 4th dimension. Then you avoid all of the reshape and permute manipulations.
imageStack = cat(4, flow8{:});

How to resize an image by adding extra pixels using matlab

I would like to resize a 512X512 image into 363X762 image which will be larger than the original image(of size 512X512). Those extra pixel values must be different values in the range of 0-255.
I tried the following code:
I=imread('photo.jpg'); %photo.jpg is a 512X512 image
B=zeros(363,726);
sizeOfMatrixB=size(B);
display(sizeOfMatrixB);
B(1:262144)=I(1:262144);
imshow(B);
B(262155:263538)=0;
But I think this is a lengthy one and the output is also not as desired. Could anyone suggest me with a better piece of code to perform this. Thank you in advance.
I think that the code you have is actually pretty close to ideal except that you have a lot of hard-coded values in there. Those should really be computed on the fly. We can do that using numel to count the number of elements in B.
B = zeros(363, 726);
%// Assign the first 262144 elements of B to the values in I
%// all of the rest will remain as 0
B(1:numel(I)) = I;
This flexibility is important and the importance is actually demonstrated via the typo in your last line:
B(262155:263538)=0;
%// Should be
B(262144:263538)=0;
Also, you don't need these extra assignments to zero at the end because you initialize the matrix to be all zeros in the first place.
A Side Note
It looks like you are spreading the original image data for each column across multiple columns. I'm guessing this isn't what you want. You probably only want to grab the first 363 rows of I to be placed into B. You can do that this way:
B = zeros(363, 726);
B(1:size(B, 1), 1:size(I, 2)) = I(1:size(B, 1), :);
Update
If you want the other values to be something other than zero, you can initialize your matrix to be that value instead.
value = 2;
B = zeros(363, 726) + value;
B(1:numel(I)) = I;
If you want them to be random integers between 0 and 255, use randi to initialize the matrix.
B = randi([0 255], 363, 726);
B(1:numel(I)) = I;

shuffle image using matlab

For instance, I have a screen size of 1024x768. And, each of my image is 150 x 250, where I have 4 images in total. How can i randomize these images to appear at different positions on the screen?
What do you want exactly?
Is it to:
keep 4 predefined empty places and assign each of the 4 image to one empty place randomly,
or simply place them randomly on the screen?
The idea is to display the images and keep the handle of each figure object (H1, H2, H3, H4). The ideal would be to store them in a handle list H.
For the first idea, store the top-left corner position of each empty place in a 4 entries list POS.
Make a connectivity list LC where LC[i]=j returns the index in POS where to find the position of the handle H[i].
For example, if LC = [1 2 3 4] handle H1 is assigned to position POS1, H2 to POS[2], etc...
Then use randperm() (mathworks.com/help/techdoc/ref/randperm.html) as Ashish pointed out on the LC list. This will randomly "mix" the connectivity list, and hence shuffle the positions.
Finally, set the position of each handle:
set(H[i], 'Position', [POS(LC[i],1) POS(LC[i],2) SizeX SizeY]);
Where SizeX and SizeY are the size ratios between the figure object and the screen.

Displaying a subset of longitude/latitude points?

I have an array of coordinates (latitude and longitude) maded in this way:
[0] = "45.01234,9.12345"
[1] = "46.11111,9.12345"
[2] = "47.22222,9.98765"
[...] etc
In a loop, convert these coordinates in meters (UTM northing / UTM easting) and after that I convert these coords in pixel (X / Y) on screen (the output device is an iPhone) to draw a route line on a custom map.
[0] = "512335.00000,502333.666666"
[...] etc
The returning pixel are passed to a method that draw a line on screen (simulating a route calculation).
[0] = "20,30"
[1] = "21,31"
[2] = "25,40"
[...] etc
As coordinate (lat/lon) are too many, I need to truncate lat/lon array eliminating the values that doesn't fill in the map bound (the visible part of map on screen).
Map bounds are 2 couple of coords lat/lon, upper left, and lower right.
Now, what is the best way to loop on this array (NOT SORTED) and check if a value is or not in bound and after remove the value that is outside?
To return a clean array that contains only the coords visible on screen?
Note: the coords array is a very big array. 4000/5000 Couple of items.
This is a method that should be looped every drag or zoom.
How can I optimize search and controls in this array?
I'd suggest breaking this into several steps:
Convert each longitude/latitude pair into a pair of meters in your new coordinate system.
Create a kd-tree data structure holding all the points in the set. This allows you to efficiently query which points lie in a given rectangular range very efficiently.
Whenever the viewport changes, find all points in the kd-tree that will be displayed in that rectangle.
To display the points, iterate over the set of points that will be displayed and display each of them.

Resources