TFormNormal not working?

Blitz3D Forums/Blitz3D Programming/TFormNormal not working?

big10p(Posted 2007) [#1]
TFormNormal is supposed to work in exactly the same way as TFormVector, except the resultant vector is normalized, right? This demo shows TFormNormal returning a completely different vector from TFormVector:

	Graphics3D 640,480,32,2
	SetBuffer BackBuffer()

	WireFrame True

	cam = CreateCamera()
	PositionEntity cam,0,0,-8
	CameraProjMode cam,2
	CameraZoom cam,0.1
	
	; Create source entity of tform.
	source_cone = CreateCone()
	ScaleEntity source_cone,.5,1,.5
	PositionEntity source_cone,3,3,0
	TurnEntity source_cone,0,0,90

	; Create destination entity of tform.
	dest_cone = CreateCone()
	ScaleEntity dest_cone,2,1,2
	PositionEntity dest_cone,-3,-3,0
	TurnEntity dest_cone,0,0,45

	; Create marker blob.
	blob = CreateSphere(16,dest_cone)
	ScaleEntity blob,.2,.2,.2,True
	
	While Not KeyHit(1)

		If KeyDown(2)
			TFormVector(1,1,0, source_cone,dest_cone)
			PositionEntity blob,TFormedX(),TFormedY(),TFormedZ()
		ElseIf KeyDown(3)
			TFormNormal(1,1,0, source_cone,dest_cone)
			PositionEntity blob,TFormedX(),TFormedY(),TFormedZ()
		Else
			PositionEntity blob,0,0,0
		EndIf

		RenderWorld
		
		Text 10,10,"Press 1 to move blob from centre of cone, along the tformed vector"
		Text 10,30,"Press 2 to move blob from centre of cone, along the tformed normal"
	
		Flip True

	Wend

	End



Floyd(Posted 2007) [#2]
The documentation is wrong.

The Normal in TFormNormal refers to perpendicularity. Suppose two vectors v1 and v2 are perpendicular, i.e. their dot product is zero. Transform v1 with TFormVector and v2 with TFormNormal. The resulting vectors are again perpendicular.

The transformed v2 is also scaled to length one, another meaning for the word normalize.

The real source of confusion is that the flawed description in the documentation is correct if there is no scaling.

I dredged up this example from my stockpile of old code:




big10p(Posted 2007) [#3]
The documentation is wrong.
Ah, I see. Mods: please move this thread into the programming forum, as it's obviously not a bug. :)

Anyway, I think I've got the hang of what TFormNormal does, but I'm struggling to work out how I'm going to implement this into my own 2D vector entity lib (I've done TFormPoint and TFormVector). Any ideas? :P

It's not simply a case of scaling the normal in the opposite direction to that of TFormVector, is it?! Sorry, that probably makes no sense.

Anyway, thanks for your help, Floyd!


big10p(Posted 2007) [#4]
I think I've sussed it. :)

When scaling the vector, I simply scale the X component by the entity's Y scale, and the Y component by the X scale. This finally came to me after managing to visualize what TFormNormal is doing. :P

Phew, I thought it was going to be tougher than that.


Matty(Posted 2007) [#5]
big10p - the only difference between TFormNormal and TFormVector is that the length of the vector returned by TFormNormal is '1'.

Don't know if you already know this but this is how it works (beginning at the beginning)

A vector quantity represents a magnitude (or size) and a direction. A scalar quantity represents just a magnitude (or size).

TFormVector returns a vector.
TFormNormal returns a normalised vector, or a vector which has had its length scaled to '1' (also known as a unit vector). This is useful as it allows us to consider only the 'directional' quality of the vector.
To go from a vector to a normalised vector all you have to do is divide the components (x,y,z) by the length of the vector. In practice this looks something like this:

;components of a vector x,y,z
x#=123.0
y#=456.0
z#=789.0

;length of vector
magnitude#=sqr(x*x+y*y+z*z)
;if magnitude is zero then it does not make sense to talk of a direction for the vector, so should check that before performing division below.
;components of unit vector/normalised vector 
unit_x#=x/magnitude
unit_y#=y/magnitude
unit_z#=z/magnitude





Hope that helps somewhat.


big10p(Posted 2007) [#6]
big10p - the only difference between TFormNormal and TFormVector is that the length of the vector returned by TFormNormal is '1'.
Wrong - read the rest of this thread. :) As Floyd pointed out, the docs for TFormNormal are incorrect.

Hope that helps somewhat.
Actually, I already know how to normalize a vector etc., but thanks for trying to help, anyway. ;)


Floyd(Posted 2007) [#7]
And if you are wondering about those scale factors here's a way to think about vectors and normals.

Suppose two vectors (x,y,z) and (u,v,w) are perpendicular. The dot product is zero.

x*u + y*v + z*w = 0

If the first vector is scaled by A,B, and C and the second by 1/A, 1/B and 1/C the dot product is still zero.

(A*x)*(u/A) + (B*y)*(v/B) + (C*z)*(w/C) = 0

All the scale factors cancel out. Since we care only about direction we could also multiply the second vector by A*B*C and it would still be perpendicular.

( u/A, v/B, w/C ) *A*B*C = ( B*C*u, A*C*v, A*B*w )

And finally scale to length 1. This is the 3D version of the 2D "cross multiplication".

This also clarifies why none of this is needed in the unscaled case. With A = B = C = 1 all the multiplying and dividing changes nothing.


big10p(Posted 2007) [#8]
Thanks for the info, Floyd. My math skills aren't too great, so can I confirm with you that my TFormNormal function is doing things right? The results I'm getting seem to match those of B3D's TFormNormal, but I'd just like to check. :)

Here's what I do to tform the 2D normal X,Y from ent1 space, into ent2 space:

1 - Scale (muliply) X by ent1's (source entity) global Y scale. Scale Y by ent1's global X scale.
2 - Rotate the resultant vector to match ent1's global rotation.

This converts the vector from ent1 space, into global space. Then I:

3 - Rotate the vector into ent2's (destination entity) coord system.
4 - Scale (divide) X by ent2's global Y scale. Scale Y by ent2's global X scale.


Floyd(Posted 2007) [#9]
It's hard to be sure with everything in words without real mathematical notation. But that's the nature of forums.

This looks right to me. You clearly have the correct idea. Going from entity1 to entity2 can be done in two steps with entity1 to world to entity2.

entity1 to world is done via scale then rotate. Then world to entity2 is just the inverse of entity2 to world.
This means first rotate ( opposite direction ) and then scale, also in opposite sense, i.e. divide instead of multiply.


Matty(Posted 2007) [#10]
Sorry to be a pain but I think the docs are perfectly fine as they are. As an example see this snippet of code:
Graphics3D 800,600,0,2
cube=CreateCube()
sphere=CreateSphere()

a#=123.0
b#=456.0
c#=789.0

Repeat
Cls

If KeyDown(57) Then TurnEntity cube,1,2,3:TurnEntity sphere,-3,2,-1
TFormVector a,b,c,cube,sphere
Text 0,0,"TForm Vector - TFormedX:"+TFormedX()+" TFormedY:"+TFormedY()+" TFormedZ:"+TFormedZ()

VecLength#=Sqr(TFormedX()*TFormedX()+TFormedY()*TFormedY()+TFormedZ()*TFormedZ())
If VecLength>0 Then 
Text 0,20,"TForm Vector Normalised - TFormedX:"+TFormedX()/VecLength+" TFormedY:"+TFormedY()/VecLength+" TFormedZ:"+TFormedZ()/VecLength
EndIf 

TFormNormal a,b,c,cube,sphere
Text 0,40,"TFormNormal - TFormedX:"+TFormedX()+" TFormedY:"+TFormedY()+" TFormedZ:"+TFormedZ()
Text 0,60,"TFormNormal Vector Length:"+Sqr(TFormedX()*TFormedX()+TFormedY()*TFormedY()+TFormedZ()*TFormedZ())



Flip


Until KeyDown(1)
End



Note there is no renderworld or updateworld as we're not viewing anything here.

Change a,b,c which are the components of the vector in cube space to any values you like (other than all zeroes). The first tformvector command transforms this vector into 'sphere' space ie the other entity (could be any entity - a pivot even I just chose a sphere and a cube) without scaling the resulting vector.

If you compare the tformnormal results with the values of the tformvector command after normalising the tformvector output you get identical results (ie tformvector transforms a vector from one coordinate system to another without scaling the resultant vector components in tformedx(),tformedy(),tformedz() whereas tformnormal transforms a vector from one coordinate system to another and then scales it to be a unit vector (of length 1)).

Pressing space rotates the two entities.

EDIT - the reason your code in the first post causes it to go to different positions is because you have scaled the cones. If you remove the scaling from the cones then it works as it should. There must be something wrong with TFormNormal when an entity is scaled?perhaps?

EDIT 2 - Ignore me I think you've covered this all above Sorry about that. My mistake


big10p(Posted 2007) [#11]
It's hard to be sure with everything in words without real mathematical notation. But that's the nature of forums.

This looks right to me. You clearly have the correct idea. Going from entity1 to entity2 can be done in two steps with entity1 to world to entity2.

entity1 to world is done via scale then rotate. Then world to entity2 is just the inverse of entity2 to world.
This means first rotate ( opposite direction ) and then scale, also in opposite sense, i.e. divide instead of multiply.
Great, it seems I am doing things correctly. Thanks, Floyd.

Matty: Yeah, TFormNormal works differently to TFormVector when the entities are scaled. I thought it was a bug, but it's not. :)


big10p(Posted 2007) [#12]
Just wondering how Blitz3D handles entities scaled to 0 with this stuff, as it would lead to 'division by zero' errors with these calculations...