[Solved] Checking rotated 2D camera - world boundary

General discussion about LÖVE, Lua, game development, puns, and unicorns.
Post Reply
User avatar
zorg
Party member
Posts: 3470
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

[Solved] Checking rotated 2D camera - world boundary

Post by zorg »

In the past weeks, i've been working on a camera system, and it's almost ready in its most basic form; the problem is, i've yet to figure out the following:
In its update function, given the world's and the viewport's dimensions, and the camera's next calculated position, orientation and scale, check whether the viewport is wholly inside of the world, and if not, set it so it stays inside of the world, and does not show anything (the void) outside.

Here's the relevant code that i've been trying to append with the orientation detection:

Code: Select all

-- cam.update(cam,dt) -- uses cam instead of implicit self
-- Limit the camera to the world's boundary, if checks are enabled

	-- cam.x and cam.y are the position of the camera, that is, the middle of the viewport; cam.o is its orientation, and cam.s is the scale.
	if cam.checkBounds then

		-- boundary checking helpers
		local topLeft = {}

		topLeft.x = cam.x - (viewport.width  / 2) / cam.s
		topLeft.y = cam.y - (viewport.height / 2) / cam.s

		-- check if coords are inside the world boundary
		if topLeft.x < 0 then
			cam.x = (viewport.width  / 2) / cam.s
		end
		if topLeft.x > world.width - (viewport.width / cam.s)  then
			cam.x = world.width - (viewport.width  / 2) / cam.s
		end
		if topLeft.y < 0 then
			cam.y = (viewport.height / 2) / cam.s
		end
		if topLeft.y > world.height - (viewport.height / cam.s) then
			cam.y = world.height - (viewport.height / 2) / cam.s
		end

	end
Any help would be appreciated :3
Last edited by zorg on Thu Feb 12, 2015 12:02 pm, edited 1 time in total.
Me and my stuff :3True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
User avatar
micha
Inner party member
Posts: 1083
Joined: Wed Sep 26, 2012 5:13 pm

Re: Checking rotated 2D camera - world boundary

Post by micha »

If I understand correctly you want to know the formula for the position of the screen corners, if the camera is rotated by the angle o.
Here you go:

Code: Select all

local dx = viewport.width/2/cam.s
local dy = viewport.height/2/cam.s
topLeft.x = cam.x - dx * math.cos(cam.o) - dy * math.sin(cam.o)
topLeft.y = cam.y + dx * math.sin(cam.o) - dy * math.cos(cam.o)
Usually I screw up the signs (+/-) before the dx and dy. If the above formula is incorrect, then try replacing some + with - or vice versa.
User avatar
zorg
Party member
Posts: 3470
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

Re: Checking rotated 2D camera - world boundary

Post by zorg »

Thanks micha, that worked! Though i still needed to modify the boundary checks to account for all corners; hopefully the following code is correct; i did test it, and it worked flawlessly for me, so i'm hopeful that it has not any bugs :3

Code: Select all

if cam.checkBounds then

		-- boundary checking helpers
		local topLeft = {}
		local bottomRight = {}

		local hx = viewport.width  / 2
		local hy = viewport.height / 2

		local dx = hx / cam.s
		local dy = hy / cam.s

		local ax = -dx * math.cos(cam.o) - dy * math.sin(cam.o)
		local ay =  dx * math.sin(cam.o) - dy * math.cos(cam.o)

		topLeft.x = cam.x + ax
		topLeft.y = cam.y + ay

		bottomRight.x = cam.x - ax
		bottomRight.y = cam.y - ay

		-- check if the viewport is wholly inside the world boundary, and move it back, if not

		if topLeft.x < 0 then cam.x = 0 - ax end
		if topLeft.y < 0 then cam.y = 0 - ay end

		if bottomRight.x < 0 then cam.x = 0 + ax end
		if bottomRight.y < 0 then cam.y = 0 + ay end

		if bottomRight.x > world.width  + dx + ax then cam.x = world.width  + ax end
		if bottomRight.y > world.height + dy + ay then cam.y = world.height + ay end

		if topLeft.x > world.width  + dx - ax then cam.x = world.width  - ax end
		if topLeft.y > world.height + dy - ay then cam.y = world.height - ay end

	end
Edit: nope, there're still a few cases where it shows the outside of the world in some corners, i'll try to fix it. Seems to be the top-right and bottom-left that are problematic; probably will need 8 more if-s.
Me and my stuff :3True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
User avatar
micha
Inner party member
Posts: 1083
Joined: Wed Sep 26, 2012 5:13 pm

Re: Checking rotated 2D camera - world boundary

Post by micha »

Yes, you need to check for all four corners, if the camera is rotated.

You can reduce the number of if-statements a little bit: First calculate the x coordinate of all four corners. Then find the corner with the largest x-coordinate

Code: Select all

largestX = math.max(topleft.x,topright.x,bottomleft.x,bottomright.x)
And then only compare this largest x to the world boundary:

Code: Select all

if largestX > world.width then
  cam.x = cam.x - (largestX - world.width)
end
Repeat for smallest x and largest and smallest y.

The quantity (largestX-world.width) is the amount of overlap.
User avatar
s-ol
Party member
Posts: 1077
Joined: Mon Sep 15, 2014 7:41 pm
Location: Cologne, Germany
Contact:

Re: Checking rotated 2D camera - world boundary

Post by s-ol »

micha wrote:Yes, you need to check for all four corners, if the camera is rotated.

You can reduce the number of if-statements a little bit: First calculate the x coordinate of all four corners. Then find the corner with the largest x-coordinate

Code: Select all

largestX = math.max(topleft.x,topright.x,bottomleft.x,bottomright.x)
And then only compare this largest x to the world boundary:

Code: Select all

if largestX > world.width then
  cam.x = cam.x - (largestX - world.width)
end
Repeat for smallest x and largest and smallest y.

The quantity (largestX-world.width) is the amount of overlap.

Code: Select all

function minmax( min, max, va, vb )
  return math.min(min, va), math.max(max, vb)
end
local maxx, maxy = math.huge, math.huge
local minx, miny = -math.huge, -math.huge
for i,v in ipairs( rects ) do -- or whatever floats your boat
  minx, maxx = minmax( minx, maxx, v.x, v.x+v.w )
  miny, maxy = minmax( miny, maxy, v.y, v.y+v.h )
---- or ----
  minx = math.min( minx, v.x )
  miny = math.min ( miny, v.y )
  maxx = math.max( maxx, v.x+v.w )
  maxy = math.max( maxy, v.y+v.h )
end

s-ol.nu /blog  -  p.s-ol.be /st8.lua  -  g.s-ol.be /gtglg /curcur

Code: Select all

print( type(love) )
if false then
  baby:hurt(me)
end
User avatar
kikito
Inner party member
Posts: 3153
Joined: Sat Oct 03, 2009 5:22 pm
Location: Madrid, Spain
Contact:

Re: Checking rotated 2D camera - world boundary

Post by kikito »

Hi there,

I have fought with this problem in my camera library, gamera.

Here's the (private) function which calculates the visible area (width and height) based on the angle, zoom factor, and original width and height of the viewport:

Code: Select all

local function getVisibleArea(self, scale)
  scale = scale or self.scale
  local sin, cos = abs(self.sin), abs(self.cos)
  local w,h = self.w / scale, self.h / scale
  w,h = cos*w + sin*h, sin*w + cos*h
  return min(w,self.ww), min(h, self.wh)
end
You can use that to adjust the position of the camera so that it does not go beyond the limits of the world.

Code: Select all

local function adjustPosition(self)
  local wl,wt,ww,wh = self.wl, self.wt, self.ww, self.wh
  local w,h = getVisibleArea(self)
  local w2,h2 = w*0.5, h*0.5

  local left, right  = wl + w2, wl + ww - w2
  local top,  bottom = wt + h2, wt + wh - h2

  self.x, self.y = clamp(self.x, left, right), clamp(self.y, top, bottom)
end
When I write def I mean function.
User avatar
zorg
Party member
Posts: 3470
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

Re: Checking rotated 2D camera - world boundary

Post by zorg »

S0lll0s wrote:(...)
kikito wrote:(...)
Thanks to both of you for helping!

kikito, I tried to somehow integrate your solution into my code, that apparently handled the cam position a bit differently, and i couldn't believe it, it's finally working! One question though, in this case, that you gave two functions, and i modified them to my needs, would i need to paste your license into this, or would an attribution line suffice (like --this part adapted from kikito's gamera <link>-- or something), or how does this work? I'm a bit new to this specific thing. :huh:

For posterity, the final code (sans the clamp function):

Code: Select all

if cam.checkBounds then

	-- scale the viewport
	local dx = viewport.width  / cam.s
	local dy = viewport.height / cam.s

	-- calculate half of that
	local hx = dx / 2
	local hy = dy / 2

	-- this would be your w,h = cos*w + sin*h, sin*w+cos*h code from getVisibleArea(), along with the line below it, and
	-- the line local w2,h2 = w*0.5, h*0.5 from adjustPosition()
	local ax = math.min((math.abs(math.cos(cam.o))*dx + math.abs(math.sin(cam.o))*dy),world.width ) / 2
	local ay = math.min((math.abs(math.sin(cam.o))*dx + math.abs(math.cos(cam.o))*dy),world.height) / 2

	-- from adjustPosition()
	local left, right = 0 + ax, 0 + world.width  - ax
	local top, bottom = 0 + ay, 0 + world.height - ay

	-- quick and dirty inline, for test purposes only
	local clamp = function(n,min,max)
		if min>max then min, max = max, min end
		return math.min(math.max(n, min), max)
	end

	cam.x = clamp(cam.x,left,right)
	cam.y = clamp(cam.y,top,bottom)

end
Thanks again everyone, for all the help. I'll release this as soon as possible.
Me and my stuff :3True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
User avatar
kikito
Inner party member
Posts: 3153
Joined: Sat Oct 03, 2009 5:22 pm
Location: Madrid, Spain
Contact:

Re: Checking rotated 2D camera - world boundary

Post by kikito »

Hi, I'm glad you got it working!

Your code is different enough from mine so no you don't need to include my license at all. A reference/link somewhere in your readme (a "credits" section, maybe?) or similar would be nice, but it's totally ok if you don't do it.
When I write def I mean function.
Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot] and 6 guests