i have an image with size 1162 x 16 which i want to rotate on the touch "moved phase" event ,
the problem is that when the image rotates it gets scrambled "pixelated", although it is not getting scaled ,
i tried with an image of size 128 x 128 but the image was not pixelated,
could it be due to the large size of the image !!
does the rotation affect the image structure???
anyone has an idea why that happens???
or if anyone has a workaround ,, would you please help me with that .
here is the updated code after making it square :
local bck = display.newRect (0,0,display.contentWidth,display.contentHeight)
bck.x = display.contentWidth * 0.5
bck.y = display.contentHeight * 0.5
bck:setFillColor (255,255,255)
local img = display.newImageRect ("laser1.png",1170,1170)
img.x = display.contentWidth * 0.5
img.y = display.contentHeight * 0.5
local function getRotation(PointX1,PointY1,PointX2,PointY2)
--display.getCurrentStage():setFocus ( Bug )
local atan2 = math.atan2
local pi = 3.14159265358
local deltax = PointX2 - PointX1
local deltay = PointY2 - PointY1
local currentAngle = ((atan2(deltay,deltax) )* 180.0) / pi
local rotationDigree = currentAngle - img.previousAngle;
img.previousAngle = currentAngle
return rotationDigree;
end
local function handleTouch ( event )
img.previousAngle = 1
if( event.phase == "moved" ) then
img.rotation = getRotation ( img.x , img.y , event.x , event.y )
end
end
Runtime:addEventListener ("touch",handleTouch)
Your image is not square. This means that when you rotate by 90 degrees (say) you are trying to fit a 16 x 1162 image into a space that's 1162 x 16.
This means that the image is being squashed in one dimension and stretched in the other.
You need to make both the source and target images 1162 x 1162 adding a border, either transparent or some known colour not in the original image so you can remove it once the rotation is complete.
The test image of 128 x 128 works because it's square.
Related
I have a 2d matrix created from position, scale and rotation (no skew). I would like to be able to decompose this matrix back to the original components and have managed to do so with the following pseudo code:
posX = matrix.tx
posY = matrix.ty
scaleX = Sqrt( matrix.a * matrix.a + matrix.b * matrix.b )
scaleY = Sqrt( matrix.c * matrix.c + matrix.d * matrix.d )
rotation = ATan2( -matrix.c / scaleY, matrix.a / scaleX )
However this obviously only works with positive scale values and I am unsure how to calculate the correct negative scales. I have attempted various suggestions found using google but so far none have worked correctly.
I have tried the accepted answer from here and the decomposition explained here, whilst they produce correct transformations, the components of scale and rotation do not match my original values.
I have tried taking the sign of the diagonal matrix.a * matrix.d which appears to work for the scale on the x axis but unsure if this is the correct approach and can't figure out how to handle the y axis.
Is this even possible? Will I have to accept that I will not get back the exact components and the best I can hope for is values that produce the same transformation?
Any help or pointers would be greatly appreciated.
Original
Translation = 204, 159
Rotation = -3.0168146900000044
Scale = -3, -2
Matrix = [ 2.976675975304773, 0.37336327891663146, -0.24890885261108764, 1.984450650203182, 204, 159 ]
Decomposition
Translation = 204, 159
Rotation = 0.1247779635897889
Scale = 3, 2
Matrix = [ 2.976675975304773, 0.3733632789166315, -0.24890885261108767, 1.984450650203182, 204, 159 ]
That was using the following decomposition code:
posX = matrix.tx
posY = matrix.ty
scaleX = Sgn( a ) * Sqrt( matrix.a * matrix.a + matrix.b * matrix.b )
scaleY = Sgn( d ) * Sqrt( matrix.c * matrix.c + matrix.d * matrix.d )
rotation = ATan2( -matrix.c / scaleY, matrix.a / scaleX )
Sometimes you can't tell flip (negative scale) from rotation, e.g. an image flipped horizontally and vertically is identical to the image rotated by 180 degrees.
So what you have to do is know whether the transformation matrix contains flips. With that knowledge, you can cancel out the flip first, decompose it as usual, and put the flip back into the decomposed scale factors.
Pseudo code:
Matrix m
hFlip = true
vFlip = true
if hFlip: m = compose(m, scale(-1, 1))
if vFlip: m = compose(m, scale(1, -1))
translation, rotation, scale = decompose(m)
if hFlip: scale.x = -scale.x
if vFlip: scale.y = -scale.y
I've got a project where I'm designing an image viewer for tiled images. Every image tile is 256x256 pixels. For each level of scaling, I'm increasing the size of each image by 5%. I represent the placement of the tiles by dividing the screen into tiles the same size as each image. An offset is used to precicely place each image where needed. When the scaling reaches a certain point(1.5), I switch over to a new layer of images that altogether has a greater resolution than the previous images. The zooming method itself looks like this:
def zoomer(self, mouse_pos, zoom_in): #(tuple, bool)
x, y = mouse_pos
x_tile, y_tile = x / self.tile_size, y / self.tile_size
old_scale = self.scale
if self.scale > 0.75 and self.scale < 1.5:
if zoom_in:
self.scale += SCALE_STEP # SCALE_STEP = 5% = 0.05
ratio = (SCALE_STEP + 1)
else:
self.scale -= SCALE_STEP
ratio = 1 / (SCALE_STEP + 1)
else:
if zoom_in:
self.zoom += 1
self.scale = 0.8
ratio = (SCALE_STEP + 1)
else:
self.zoom -= 1
self.scale = 1.45
ratio = 1 / (SCALE_STEP + 1)
# Results in x/y lengths of the relevant full image
x_len = self.size_list[self.levels][0] / self.power()
y_len = self.size_list[self.levels][1] / self.power()
# Removing extra pixel if present
x_len = x_len - (x_len % 2)
y_len = y_len - (y_len % 2)
# The tile's picture coordinates
tile_x = self.origo_tile[0] + x_tile
tile_y = self.origo_tile[1] + y_tile
# The mouse's picture pixel address
x_pic_pos = (tile_x * self.tile_size) -
self.img_x_offset + (x % self.tile_size)
y_pic_pos = (tile_y * self.tile_size) -
self.img_y_offset + (y % self.tile_size)
# Mouse percentile placement within the image
mouse_x_percent = (x_pic_pos / old_scale) / x_len
mouse_y_percent = (y_pic_pos / old_scale) / y_len
# The mouse's new picture pixel address
new_x = (x_len * self.scale) * mouse_x_percent
new_y = (y_len * self.scale) * mouse_y_percent
# Scaling tile size
self.tile_size = int(TILE_SIZE * self.scale)
# New mouse screen tile position
new_mouse_x_tile = x / self.tile_size
new_mouse_y_tile = y / self.tile_size
# The mouse's new tile address
new_tile_x = new_x / self.tile_size
new_tile_y = new_y / self.tile_size
# New tile offsets
self.img_x_offset = (x % self.tile_size) - int(new_x % self.tile_size)
self.img_y_offset = (y % self.tile_size) - int(new_y % self.tile_size)
# New origo tile
self.origo_tile = (int(new_tile_x) - new_mouse_x_tile,
int(new_tile_y) - new_mouse_y_tile)
Now, the issue arising from this is that the mouse_.._percent variables never seem to match up with the real position. For testing purposes, I feed the method with a mouse position centered in the middle of the screen and the picture centered in the middle too. As such, the resulting mouse_.._percent variable should, in a perfect world, always equal 50%. For the first level, it does, but quickly wanders off when scaling. By the time I reach the first zoom breakpoint (self.scale == 1.5), the position has drifted to x = 48%, y = 42%.
The self.origo_tile is a tuple containing the x/y coordinate for the tile to be drawn on screen tile (0, 0)
I've been staring at this for hours, but can't seen to find a remedy for it...
How the program works:
I apologize that I didn't have enough time to apply this to your code, but I wrote the following zooming simulator. The program allows you to zoom the same "image" multiple times, and it outputs the point of the image that would appear in the center of the screen, along with how much of the image is being shown.
The code:
from __future__ import division #double underscores, defense against the sinister integer division
width=256 #original image size
height=256
posx=128 #original display center, relative to the image
posy=128
while 1:
print "Display width: ", width
print "Display height: ", height
print "Center X: ", posx
print "Center Y: ", posy
anchx = int(raw_input("Anchor X: "))
anchy = int(raw_input("Anchor Y: "))
zmag = int(raw_input("Zoom Percent (0-inf): "))
zmag /= 100 #convert from percent to decimal
zmag = 1/zmag
width *= zmag
height *= zmag
posx = ((anchx-posx)*zmag)+posx
posy = ((anchy-posy)*zmag)+posy
Sample output:
If this program outputs the following:
Display width: 32.0
Display height: 32.0
Center X: 72.0
Center Y: 72.0
Explanation:
This means the zoomed-in screen shows only a part of the image, that part being 32x32 pixels, and the center of that part being at the coordinates (72,72). This means on both axes it is displaying pixels 56 - 88 of the image in this specific example.
Solution/Conclusion:
Play around with that program a bit, and see if you can implement it into your own code. Keep in mind that different programs move the Center X and Y differently, change the program I gave if you do not like how it works already (though you probably will, it's a common way of doing it). Happy Coding!
I'm writing a function in matlab to zoom or shrink an image using bicubic interpolation. However, my function resizes an image along both rows and columns. What if I want to enlarge the image along the rows only, or along the columns only? This is my code so far
function pic_new = zoom_image(pic, zoom_value)
actualSize = size(pic);
newSize = max(floor(zoom_value.*actualSize(1:2)),1);
newX = ((1:newSize(2))-0.5)./zoom_value+0.5; %# New image pixel X coordinates
newY = ((1:newSize(1))-0.5)./zoom_value+0.5;
oldClass = class(pic); %# Original image type
pic = double(pic); %# Convert image to double precision for interpolation
if numel(actualSize) == 2
pic_new = interp2(pic,newX,newY(:),'cubic');
end
pic_new = cast(pic_new,oldClass);
end
Updated: I was able to resize the image both along rows and columns. However, it doesn't work right
This is the original image: https://imagizer.imageshack.us/v2/895x383q90/r/903/4jM76I.png
This is the image after being enlarge 2.5 along rows and shrunk 1.3 along columns: https://imagizer.imageshack.us/v2/323x465q90/r/673/EHIaoB.png
Why is there such a black box in the result image?
Updated 2: This is how I did: in the command window type
>> img = imread('pic.pgm');
>> newImage = zoom_image(img, 2.5, 1/1.3);
>> imshow(newImage)
Using imresize it can be easily achieved
pic_new = imresize( pic, newSize, 'bicubic' );
Where newSize is the new size of the image (height and width). The new aspect ratio can be arbitrary and does not have to be the same as the aspect ratio of the new image.
For example, shrinking an image by 1/2 along the rows and leaving number of columns unchanged:
[nCol nRows] = size( pic(:,:,1) );
pic_new = imresize( pic, [nCols round( nRows/2 ) ], 'bicubic' );
Try editing your function to have a separate zoom for rows and columns, a la
function pic_new = zoomfunc(pic, zoom_valueX, zoom_valueY)
actualSize = size(pic);
newSize = max(floor([zoom_valueY zoom_valueX].*actualSize(1:2)),1);
newX = ((1:newSize(2))-0.5)./zoom_valueX+0.5; %# New image pixel X coordinates
newY = ((1:newSize(1))-0.5)./zoom_valueY+0.5;
oldClass = class(pic); %# Original image type
pic = double(pic); %# Convert image to double precision for interpolation
if numel(actualSize) == 2
pic_new = interp2(pic,newX,newY(:),'cubic');
end
pic_new = cast(pic_new,oldClass);
end
I am trying to have a number of boxes rotate like a ferris wheel based on touch input
so imagine a ferris wheel on the screen and dragging one of the carts down would rotate the wheel one way and dragging up would rotate the wheel the other way
each box or 'cart' on the ferris wheel doesnt rotate they will only move in a circular motion, exactly like a ferris wheel
i pretty much have this working now, however my boxes are snapping away from the initial grab point
so when i touch one of the boxes it will quickly appear elsewhere but then rotate as normal, i want it to continue to rotate smoothly from initial grab point
here is my current code below
local squares = display.newGroup()
local square = display.newRect(0,0,200,200)
square.x, square.y = 320, 320
square:setFillColor(100,255,55)
squares:insert(square)
local square2 = display.newRect(0,0,200,200)
square2.x, square2.y = 320, 320
square2:setFillColor(999,255,55)
squares:insert(square2)
local function onTouch( event )
local t = event.target
local phase = event.phase
if "began" == phase then
local parent = t.parent
parent:insert( t )
display.getCurrentStage():setFocus( t )
t.isFocus = true
-- Store initial position
t.x0 = event.x - t.x
t.y0 = event.y - t.y
elseif t.isFocus then
if "moved" == phase then
local degrees = event.y
local rads = degrees * (math.pi / 360.0)
square.x = 300 * math.cos(rads) + 500
square.y = 300 * math.sin(rads)+ 500
degrees = degrees + 100
print (square.x, square.y)
local rads2 = degrees * (math.pi / 360.0)
square2.x = 300 * math.cos(rads2)+ 500
square2.y = 300 * math.sin(rads2)+ 500
degrees = degrees - 100
print (square.x, square.y)
elseif "ended" == phase or "cancelled" == phase then
display.getCurrentStage():setFocus( nil )
t.isFocus = false
end
end
return true
end
squares:addEventListener( "touch", onTouch )
please feel free to point out any silly mistakes I have made and also
if you could show me how to get the same affect but around another object rather than a point i would be really greatful, thanks
This is a complete working example.
local squares = display.newGroup()
local wheelX = display.contentCenterX
local wheelY = display.contentCenterY
local radius = 220
local degrees = -180
local squareH = 150
local square = display.newRect(0,0,squareH,squareH)
square:setFillColor(255,255,255)
square.degStart = 100
squares:insert(square)
local square2 = display.newRect(0,0,squareH,squareH)
square2:setFillColor(999,255,55)
square2.degStart = -10
squares:insert(square2)
local function drawRects(degrees)
local rads = (square.degStart + degrees) * (math.pi / 180.0)
square.x = radius * math.cos(rads) + wheelX
square.y = radius * math.sin(rads) + wheelY
local rads2 = (square2.degStart + degrees) * (math.pi / 180.0)
square2.x = radius * math.cos(rads2) + wheelX
square2.y = radius * math.sin(rads2) + wheelY
end
local function getDegrees(square)
local x = square.x
local y = square.y
local degrees = math.atan2((y - wheelY) , (x - wheelX)) * (180 / math.pi)
return degrees
end
local function onTouch( event )
local t = event.target
local phase = event.phase
local parent = t.parent
if "began" == phase then
print("began")
parent:insert( t )
display.getCurrentStage():setFocus( t )
t.isFocus = true
square.degStart = getDegrees(square)
square2.degStart = getDegrees(square2)
elseif t.isFocus then
if "moved" == phase then
degrees = math.atan2((event.yStart - wheelY) , (event.xStart - wheelX)) * (180 / math.pi)
degrees2 = math.atan2((event.y - wheelY) , (event.x - wheelX)) * (180 / math.pi)
diffDegrees = degrees2 - degrees
drawRects(diffDegrees)
print("diffDegrees: " .. diffDegrees)
elseif "ended" == phase or "cancelled" == phase then
display.getCurrentStage():setFocus( nil )
t.isFocus = false
end
end
return true
end
squares:addEventListener( "touch", onTouch )
drawRects(degrees)
It took me a lot of time.
You should try to get event.y and event.x and then retrieve the degrees with formula:
degrees = Math.atan(event.y / event.x)
So I've managed myself to write the first part (algorithm) to calculate each tile's position where should it be placed while drawing this map (see bellow). However I need to be able to convert mouse location to the appropriate cell and I've been almost pulling my hair off because I can't figure out a way how to get the cell from mouse location. My concern is that it involves some pretty high math or something i'm just something easy i'm not capable to notice.
For example if the mouse position is 112;35 how do i calculate/transform it to to get that the cell is 2;3 at that position?
Maybe there is some really good math-thinking programmer here who would help me on this or someone who knows how to do it or can give some information?
var cord:Point = new Point();
cord.x = (x - 1) * 28 + (y - 1) * 28;
cord.y = (y - 1) * 14 + (x - 1) * (- 14);
Speaking of the map, each cell (transparent tile 56x28 pixels) is placed in the center of the previous cell (or at zero position for the cell 1;1), above is the code I use for converting cell-to-position. I tried lot of things and calculations for position-to-cell but each of them failed.
Edit:
After reading lot of information it seems that using off screen color map (where colors are mapped to tiles) is the fastest and most efficient solution?
I know this is an old post, but I want to update this since some people might still look for answers to this issue, just like I was earlier today. However, I figured this out myself. There is also a much better way to render this so you don't get tile overlapping issues.
The code is as simple as this:
mouse_grid_x = floor((mouse_y / tile_height) + (mouse_x / tile_width));
mouse_grid_y = floor((-mouse_x / tile_width) + (mouse_y / tile_height));
mouse_x and mouse_y are mouse screen coordinates.
tile_height and tile_width are actual tile size, not the image itself. As you see on my example picture I've added dirt under my tile, this is just for easier rendering, actual size is 24 x 12. The coordinates are also "floored" to keep the result grid x and y rounded down.
Also notice that I render these tiles from the y=0 and x=tile_with / 2 (red dot). This means my 0,0 actually starts at the top corner of the tile (tilted) and not out in open air. See these tiles as rotated squares, you still want to start from the 0,0 pixel.
Tiles will be rendered beginning with the Y = 0 and X = 0 to map size. After first row is rendered you skip a few pixels down and to the left. This will make the next line of tiles overlap the first one, which is a great way to keep the layers overlapping coorectly. You should render tiles, then whatever in on that tile before moving on to the next.
I'll add a render example too:
for (yy = 0; yy < map_height; yy++)
{
for (xx = 0; xx < map_width; xx++)
{
draw tiles here with tile coordinates:
tile_x = (xx * 12) - (yy * 12) - (tile_width / 2)
tile_y = (yy * 6) + (xx * 6)
also draw whatever is on this tile here before moving on
}
}
(1) x` = 28x -28 + 28y -28 = 28x + 28y -56
(2) y` = -14x +14 +14y -14 = -14x + 14y
Transformation table:
[x] [28 28 -56 ] = [x`]
[y] [-14 14 0 ] [y`]
[1] [0 0 1 ] [1 ]
[28 28 -56 ] ^ -1
[-14 14 0 ]
[0 0 1 ]
Calculate that with a plotter ( I like wims )
[1/56 -1/28 1 ]
[1/56 1/28 1 ]
[0 0 1 ]
x = 1/56*x` - 1/28y` + 1
y = 1/56*x` + 1/28y` + 1
I rendered the tiles like above.
the sollution is VERY simple!
first thing:
my Tile width and height are both = 32
this means that in isometric view,
the width = 32 and height = 16!
Mapheight in this case is 5 (max. Y value)
y_iso & x_iso == 0 when y_mouse=MapHeight/tilewidth/2 and x_mouse = 0
when x_mouse +=1, y_iso -=1
so first of all I calculate the "per-pixel transformation"
TileY = ((y_mouse*2)-((MapHeight*tilewidth)/2)+x_mouse)/2;
TileX = x_mouse-TileY;
to find the tile coordinates I just devide both by tilewidth
TileY = TileY/32;
TileX = TileX/32;
DONE!!
never had any problems!
I've found algorithm on this site http://www.tonypa.pri.ee/tbw/tut18.html. I couldn't get it to work for me properly, but I change it by trial and error to this form and it works for me now.
int x = mouse.x + offset.x - tile[0;0].x; //tile[0;0].x is the value of x form witch map was drawn
int y = mouse.y + offset.y;
double _x =((2 * y + x) / 2);
double _y= ((2 * y - x) / 2);
double tileX = Math.round(_x / (tile.height - 1)) - 1;
double tileY = Math.round(_y / (tile.height - 1));
This is my map generation
for(int x=0;x<max_X;x++)
for(int y=0;y<max_Y;y++)
map.drawImage(image, ((max_X - 1) * tile.width / 2) - ((tile.width - 1) / 2 * (y - x)), ((tile.height - 1) / 2) * (y + x));
One way would be to rotate it back to a square projection:
First translate y so that the dimensions are relative to the origin:
x0 = x_mouse;
y0 = y_mouse-14
Then scale by your tile size:
x1 = x/28; //or maybe 56?
y1 = y/28
Then rotate by the projection angle
a = atan(2/1);
x_tile = x1 * cos(a) - y1 * sin(a);
y_tile = y1 * cos(a) + x1 * sin(a);
I may be missing a minus sign, but that's the general idea.
Although you didn't mention it in your original question, in comments I think you said you're programming this in Flash. In which case Flash comes with Matrix transformation functions. The most robust way to convert between coordinate systems (eg. to isometric coordinates) is using Matrix transformations:
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/geom/Matrix.html
You would want to rotate and scale the matrix in the inverse of how you rotated and scaled the graphics.