Tricky trig

Blitz3D Forums/Blitz3D Programming/Tricky trig

big10p(Posted 2007) [#1]
OK, this is probably going to be kind of hard to explain, but I'll give it a shot: :)

I've written a MoveEntity function for my 2d vector entity lib, but I'd like to check with someone (Floyd? :) who has a better math brain than me that I'm not doing things the long/slow way.

I thought this function was going to be fairly straightforward to implement... which it was, until you consider moving a child entity whose parent has a non-unifrom scale.

In this case, the child's movement vector is in terms of the parent's scaled coordinate system - it's X units might be twice the size of it's Y units, for example.

Because of this, I've been struggling to scale the movement vector correctly to fit the parent's coordinate system scale. The movement vector needs to be transformed into the parent's coord system becasue this is where the child entity 'lives' - i.e. it's position is relative to it's parent.

I think I've finally found a solution to this, but it requires 2 Sqr calls, and the whole process seems a bit 'mechanical' to me. Maybe it's the only way, but I was wondering if there's a more elegant way to achieve the same results?

Here's the (semi-pseudo) code I'm using:
; Child's movement vector. We want to move the child 1 unit along it's positve X axis
; regardless of it's current rotation!
vx# = 1.0
vy# = 0.0


; Rotate movement vector to match child's global rotation.
cos_rot# = Cos(child_global_rot#)
sin_rot# = Sin(child_global_rot#)

global_vx# = (vx * cos_rot) - (vy * sin_rot)
global_vy# = (vy * cos_rot) + (vx * sin_rot)


; Rotate global movement vector into parent's coord system.
cos_rot# = Cos(-parent_global_rot#)
sin_rot# = Sin(-parent_global_rot#)

parent_vx# = (global_vx * cos_rot) - (global_vy * sin_rot)
parent_vy# = (global_vy * cos_rot) + (global_vx * sin_rot)

parent_vx = parent_vx / parent_global_scale_y#
parent_vy = parent_vy / parent_global_scale_x#


; At this point, our parent vector has the correct direction, but it's
; magnitude/length needs to be scaled to match the parent's coord system scale...


; Find magnitute/length of the orignal movement vector
; and the magnitude of the equivalent vector in the parent's coord system.
global_mag# = Sqr(vx * vx + vy * vy)
parent_mag# = Sqr(parent_vx * parent_vx + parent_vy * parent_vy)


; Scale the parent's vector to match the magnitude of the original movement vector,
; but specified in terms of the parent's coord system scale.
move_vx = (parent_vx / parent_mag) * global_mag
move_vy = (parent_vy / parent_mag) * global_mag


; Finally, move the child.
child_x = child_x + move_vx
child_y = child_y + move_vy

P.S. I did infact find an alternative method without using the 2 Sqr calls, but it required 2 ATan2 calls, a Sin and a Cos. I figured the Sqr method was better and maybe (slightly) faster?

Thanks for any knowledgeable opinions! :)


Rob Farley(Posted 2007) [#2]
Wouldn't it be easier to deal with it's movement in it's own space then transform it to it's parents space afterwards?

ie, position entity at (childxpos * parentxscale) + parentxposition, (childypos * parentyscale) + parentyposition

Or am I getting the wrong end of the stick with you problem?


big10p(Posted 2007) [#3]
Rob, the trouble is that the (non-uniformly scaled) parent's rotation affects by how much the child actually moves.

Given that this is in 2D so +X is to the right, +Y is down, and 0 degrees is at 3 o'clock postition, imagine:
  ##                            #
  |                             |
parent:                        child:
x_scale=2                      rot=0
y_scale=1
rot=0

Now, if I wanted to move the child down 1 unit I could simply do childypos = childypos + (yunits * parentyscale). However, if you imagine that the parent in the above diagram is rotated by -90 degrees (pointing straight up), the formula fails (because the parent's +ve Y axis is now pointing to the right, not down). Things get even more complicated when the parent/child are rotated at arbitrary (non-axis aligned) angles. This is why I believe the use of vectors is required.

Remember that when you position/rotate/scale an entity, the entity itself isn't actually positioned/rotated/scaled, it's coordinate system is! E.g. no matter where you position an entity, it's always at the origin (0,0) of it's own coordinate system.


Rob Farley(Posted 2007) [#4]
I'm not talking about moving the child in the world space, just move it in it's own space. Then transform it's position each time based on the parent...

I think I need to write some code to explain this better...

Hold on...


Rob Farley(Posted 2007) [#5]
Move the mouse about and use left and right buttons to rotate it.

Graphics 640,480,32,2

SetBuffer BackBuffer()

parentrot = 180

childx = 10
childy = -30

t = 1

Repeat
	Cls
	parentx = MouseX()
	parenty = MouseY()
	
	If MouseDown(2) Then parentrot = parentrot + 1
	If MouseDown(1) Then parentrot = parentrot - 1
	
	; parent
	Color 0,255,0
	Line parentx,parenty, parentx + (Sin(parentrot)*20), parenty + (Cos(parentrot)*20)
	Oval parentx-4,parenty-4,9,9
	
	; child
	Color 255,0,0
	childxworld = parentx + (Sin(parentrot) * childy) + (Cos(parentrot)*childx)
	childyworld = parenty + (Cos(parentrot) * childy) - (Sin(parentrot) * childx)
	Oval childxworld  - 3 , childyworld  - 3,7,7
	
	; move child
	childx = Sin(t) * 20
	t=t+5
	
	Color 255,255,255
	Text 0,0,"Child X:" + childx
	Text 0,10,"Child Y:" + childy
	Text 0,20,"Child X World:" + childxworld 
	Text 0,30,"Child X World:" + childyworld 
		
	Flip

Until KeyHit(1)


I've not messed with scale and stuff but you get the general idea, you just move the child around in it's own space then display it based on what's going on with the parent.

Also as you may have noticed the sin bit on the moving the child is just to give it movement nothing to do with any of the other calculations.

As you can tell from this the child is only moving in it's X direction, however, because the parent is moving around and rotating the world position is adjusted accordingly.

Oh and I think I've got a few of the things a bit arse about tit so it's reversed, but the idea is still sound!


big10p(Posted 2007) [#6]
I've not messed with scale and stuff but you get the general idea
But scale is what's causes the difficulty! As I said in my OP, things are simple when non-uniform scaling of the parent isn't involved.

Your code is basically doing the same as mine, in as much as it rotates the the childs local position vector into the parent's coord system.

I knew this would be tricky to explain. :)


Rob Farley(Posted 2007) [#7]
Scale added:
Graphics 640,480,32,2

SetBuffer BackBuffer()

parentrot# = 180

parentxscale# = 2
parentyscale# = 3

childx# = 10
childy# = -30

t = 1

Repeat
	Cls
	parentx# = MouseX()
	parenty# = MouseY()
	
	If MouseDown(2) Then parentrot = parentrot + 1
	If MouseDown(1) Then parentrot = parentrot - 1
	
	; parent
	Color 0,255,0
	Line parentx,parenty, parentx + (Sin(parentrot)*20), parenty + (Cos(parentrot)*20)
	Oval parentx-4,parenty-4,9,9
	
	; child
	Color 255,0,0
	childxworld = parentx + ((Sin(parentrot) * childy) * parentyscale) + ((Cos(parentrot) * childx) * parentxscale)
	childyworld = parenty + ((Cos(parentrot) * childy) * parentyscale) - ((Sin(parentrot) * childx) * parentxscale)
	Oval childxworld  - 3 , childyworld  - 3,7,7
	
	; move child
	childx = Sin(t) * 20
	childy = t/5 Mod 10
	t=t+5
	
	Color 255,255,255
	Text 0,0,"Child X:" + childx
	Text 0,10,"Child Y:" + childy
	Text 0,20,"Child X World:" + childxworld 
	Text 0,30,"Child X World:" + childyworld 
				
	
	Flip

Until KeyHit(1)



Ross C(Posted 2007) [#8]
Couldn't you unparent it, move it, then re-parent it. I mind i had to do this for something in my world editor. Worked for me :o) Probably not ideal in terms of speed??


big10p(Posted 2007) [#9]
Rob, the problem still doesn't manifest because the child and parent axes are aligned. Interesting to see you've included the scaling as part of the rotation formula - I hadn't considered this before, so maybe I can use this approach in some way, not sure.

Anyway, this amendment may help show the problem where the child now has it's own rotation (see comments for when spacebar is hit):
Graphics 640,480,32,2

SetBuffer BackBuffer()

parentrot# = 180

parentxscale# = 2
parentyscale# = 3

childx# = 10
childy# = -30
childrot# = 45

t = 1

Repeat
	Cls
	parentx# = MouseX()
	parenty# = MouseY()
	
	If MouseDown(2) Then parentrot = parentrot + 1
	If MouseDown(1) Then parentrot = parentrot - 1
	
	; parent
	Color 0,255,0
	Line parentx,parenty, parentx + (Cos(parentrot)*20), parenty + (Sin(parentrot)*20)
	Oval parentx-4,parenty-4,9,9
	
	; child
	Color 255,0,0
	childxworld = parentx + ((Cos(parentrot) * childx) * parentxscale) - ((Sin(parentrot) * childy) * parentyscale)
	childyworld = parenty + ((Cos(parentrot) * childy) * parentyscale) + ((Sin(parentrot) * childx) * parentxscale)
	childworldrot# = parentrot + childrot
	Line childxworld,childyworld, childxworld + (Cos(childworldrot)*20), childyworld + (Sin(childworldrot)*20)
	Oval childxworld  - 3 , childyworld  - 3,7,7
	
	; move child 5 'parent units' along it's x axis (shown by the red line).
	; (doesn't work)
	If KeyHit(57)
		childx = childx + 5
	EndIf
	
	Color 255,255,255
	Text 0,0,"Child X:" + childx
	Text 0,10,"Child Y:" + childy
	Text 0,20,"Child X World:" + childxworld 
	Text 0,30,"Child X World:" + childyworld 
				
	
	Flip

Until KeyHit(1)
End


Hey Ross. ;) I can't really do that because the child needs to move in correspondence to it parent's coord system scale. I could probably use this approach by using a couple of vector tforms aswell, but it'd probably end up messier than the code I already have. :P


Floyd(Posted 2007) [#10]
The only thing that jumps off the page when I read this is that the rotation is far too complicated.

2D rotation is much simpler than 3D since it can be represented by a single number, an angle. If, say, the child has a rotation of 20 and the parent 50 then the child vector can be rotated by 30 ( = 50 - 20 ) to give it the parent's rotation.

In the notation of the example code the child movement vector can be rotated just once.
The angle is ( parent_global_rot - child_global_rot ).


big10p(Posted 2007) [#11]
Ah, yes - I should've spotted that. I copy/pasted the initial code from another similar function and didn't think to question the double rotation. The other function actually requires it.

To get things working as you suggest, it seems I actually have to use the formula angle = -parent_global_rot + child_global_rot, though.

Thanks all.