Vector Class
Monkey Forums/Monkey Code/Vector Class
| ||
Sharing my Vector2D class. ´ I use this in almost every single little thing I do in Monkey. Very tested, very useful. Feel free to use it for free for whatever you want - use,share,improve,extend,bundle,and so on. It's even OK if you want to bundle this with a framework. Strict ' A vector class with a lot of handy functionality for game-programming! ' In simplicy a vector is an Arrow. It has an length ' and a direction. You can use these properties to ' make the vector model a Position, a Velocity, a ' Line, a Distance and many other things that can be ' measured using a length and a direction or anything ' that uses two float pair-values! ' Wikipedia: A vector is what is needed to "carry" the ' point A to the point B; the Latin word vector means "one who carries" Class Vector Public Field X:Float Field Y:Float Const Zero:Vector = New Vector(0,0) Method New(x:Float=0,y:Float=0) X = x Y = y End ' S E T '---------------------------------------------- Method Set:Vector( vector:Vector ) X = vector.X Y = vector.Y Return Self End Method Set:Vector(x:Float,y:Float ) X = x Y = y Return Self End ' A D D '---------------------------------------------- Method Add:Vector( vector:Vector ) X += vector.X Y += vector.Y Return Self End ' S U B T R A C T '---------------------------------------------- Method Subtract:Vector( vector:Vector ) X = X - vector.X Y = Y - vector.Y Return Self End ' D O T P R O D U C T '--------------------------------------------------- ' Dot Product which is X*X2 + Y*Y2 ' '--------------------------------------------------- Method Dot:Float( Vector:Vector ) Return ( X * Vector.X + Y * Vector.Y) End ' M U L T I P L Y V E C T O R '---------------------------------------------- Method Multiply:Vector( Value:Float ) X*=Value Y*=Value Return Self End ' M I R R O R '---------------------------------------------------------- ' if the mirrorImage vector is a unit-vector this will ' make this vector flip 180 degrees around it ' So that this vector now points in the exact opppisite ' direction To the mirrorImage vector ' Returns Self, which will be opposite to mirrorImage. Method Mirror:Vector( mirrorImage:Vector ) Local Dotprod# = -X * mirrorImage.X - Y * mirrorImage.Y X=X+2 * mirrorImage.X * dotprod Y=Y+2 * mirrorImage.Y * dotprod Return Self End ' Set the vector to a direction and a length x,y ' returns Self, which is now pointin the directio ' provided, with the length provided Method MakeField:Vector( direction:Float, length:Float ) X = Cos( -direction )*length Y = Sin( -direction )*length Return Self End '---------------------------------------------- ' Create a copy --> depending on situation you might ' want to use the VectorPool to do this instead for ' when extrenme performance is required '----------------------------------------------- Method Copy:Vector() Property Local vector:Vector = New Vector vector.X = X vector.Y = Y Return vector End ' C R E A T E L E F T N O R M A L '---------------------------------------------- ' Make this an Perpendicular Vector ' As if you would rotate it 90 degrees Counter Clockwise ' return ( Y, -X ) Method LeftNormal:Vector( ) Local tempX:Float = Y Y = -X X = tempX Return Self End ' C R E A T E R I G H T N O R M A L '---------------------------------------------- ' Make this a Perpendicular Vector ' Same as rotating it 90 degrees ClockWise ' return ( -Y, X ) Method RightNormal:Vector( ) Local tempY:Float = Y Y = X X = -tempY Return Self End ' N O R M A L I Z E '---------------------------------------------- ' Purpose: Sets Vector length To ONE but keeps it's direction ' Returns Self as a UnitVector Method Normalize:Vector() Local Length:Float = Self.Length() If Length = 0 Return Self ' Don't divide by zero, 'we do not normalize a zero-vector 'since this vector's direction is in ' mathematical terms all directions! Endif Set( X / Length, Y / Length ) 'Make length = 1 Return Self End ' G E T L E N G T H '----------------------------- ' Get current Length of vector, uses a single sqr operation Method Length:Float() Property Return Sqrt( X*X + Y*Y )' End ' S E T L E N G T H '------------------------------------- Method Length:Void( length:Float ) Property 'If we want to set vector to zero If length = 0 X=0 Y=0 Return Endif If X = 0 And Y = 0 X = length Endif Normalize() Multiply( length ) End Method ReduceLength:Vector( amount:Float ) Local currentAngle:Float = Direction Local currentLength:Float = Length Local newLength:Float = currentLength - amount If newLength > 0 MakeField( currentAngle, currentLength - amount ) Else Set 0,0 Endif Return Self End ' G E T D I R E C T I O N '---------------------------------------------------- ' Calculates the current direction in degrees ' in the 0 To < 360 range Method Direction:Float() Property Local angle:Float = ATan2(-Y , X) 'If angle < 0 Then angle =+ 360 Return angle End ' S E T D I R E C T I O N '---------------------------------------------- ' ' Set the angle of this vector without changing the length, ' has no effect if vector length is 0 ' uses a single sqr operation Method Direction:Void( direction:Float ) Property MakeField( direction, Length ) End Method ' D I S T A N C E '---------------------------------------------- ' The Distance between the two points ' This is NOT related to the vectors Length# Method DistanceTo:Float(Other:Vector) Return DistanceTo(Other.X, Other.Y) End Method DistanceTo:Float(x:Float, y:Float) Local dx:Float = x - X Local dy:Float = y - Y Return Sqrt( dx*dx + dy*dy ) End ' G E T A N G L E B E T W E E N '---------------------------------------------- ' If you have two vectors that start at the same position ' the angle-distance between two vectors Result is from 0 ' to 180 degrees, because two vectors can not be more than 180 ' degrees apart, check AngleClockwise & AngleAntiClockwise ' to get a 0-360 result instead ' even tough it is counted as they are on the same position, ' that is not a requirement at all for this to be correct Method AngleTo:Float( target:Vector ) Local dot:Float = Self.Dot( target ) Local combinedLength:Float = Self.Length()*target.Length() If combinedLength = 0 Then Return 0 Local quaski:Float = dot/combinedLength Local angle:Float = ACos( quaski ) Return angle End ' If you have two vectors so they both start at the same position ' returns the angle from this vector to target vector (in degrees) ' if you where to go Clockwise to it, result is 0 to 360 Method AngleToClockwise:Float( target:Vector ) Local angle:Float = ATan2(-Y , X) - ATan2(-target.Y , target.X) If angle < 0 Then angle += 360 If angle >= 360 Then angle -= 360 Return angle End ' If you have two vectors so they both start at the same position ' returns the angle fromt this vector to target vector (in degrees) ' if you where to go AntiClockwise to it, result is 0 to 360 Method AngleToAntiClockwise:Float( target:Vector ) Local angle:Float = AngleToClockwise(target)-360 Return -angle End ' V E C T O R B E T W E E N '---------------------------------------------- ' Change the vector into a vector from Position1 to Position2 ' Return Self, as a vector that goes from first ' parameter's position to the second Method MakeBetween:Vector( PositionFrom:Vector , PositionToo:Vector) If PositionFrom = Null Or PositionToo = Null Then Return Self X = ( PositionToo.X - PositionFrom.X ) Y = ( PositionToo.Y - PositionFrom.Y ) Return Self End End Short use example can be found here: Vector Class |
| ||
Great stuff! :D But possibly move it to the code archives for better archiving? |
| ||
Yep great! Thanks for that. |
| ||
ive used the angle functions of a vector class before its very useful, thanks |
| ||
Many thanks! I reckon this would be a good time to mention the Wolfire vector articles again. |
| ||
How many of you guys are familiar with Vectors since before? My old Vector Tutorial for newbies, should brush it up for Monkey and this Vector class, but it might be somewhat helpful anyway since Blitzmax is quite similar and the focus is on understanding :) Basic Vectors and Physics in Blitzmax |
| ||
Here's some vector stuff I do for my chipmunk port at the moment. It's still not optimized and need to be done more within the vector class (so methods instead of functions), but I work on that right now (at the moment it's more a 1:1 conversion from the original C code)' chipmonkey - A Monkey Port of Scott Lembcke's chipmunk 2d physics engine ' ' chipmonkey homepage: http://code.google.com/p/chipmonkey/ ' chipmunk homepage: http://code.google.com/p/chipmunk-physics/ ' ' This port is maintained by Martin Leidel @ http://www.monkeycoder.de ' ' This software is provided 'as-is', without any express or implied ' warranty. In no event will the authors be held liable for any damages ' arising from the use of this software. ' ' Permission is granted to anyone to use this software for any purpose, ' including commercial applications, and to alter it and redistribute it ' freely, subject to the following restrictions: ' ' 1. The origin of this software must not be misrepresented; you must not ' claim that you wrote the original software. If you use this software ' in a product, an acknowledgment in the product documentation would be ' appreciated but is not required. ' 2. Altered source versions must be plainly marked as such, and must not be ' misrepresented as being the original software. ' 3. This notice may not be removed or altered from any source distribution. ' Module chipmonkey.vect Class cpVect Public Const zero:cpVect = New cpVect( 0.0, 0.0 ) Method New( x:Float, y:Float ) Self.x = x Self.y = y End Method Private Field x:Float Field y:Float End Class ' Returns the length of v. Function cpvlength:Float( v:cpVect ) Return Sqrt( cpvdot( v, v ) ) End Function ' Spherical linearly interpolate between v1 and v2. Function cpvslerp:cpVect( v1:cpVect, v2:cpVect, t:Float ) Local omega:Float = ACos( cpvdot( v1, v2 ) ) If( omega <> 0.0 ) Local denom:Float = 1.0 / Sin( omega ) Return cpvadd( cpvmult( v1, Sin( ( 1.0 - t ) * omega ) * denom ), cpvmult( v2, Sin( t * omega ) * denom ) ) Else Return v1 End If End Function ' Spherical linearly interpolate between v1 towards v2 by no more than angle a radians Function cpvslerpconst:cpVect( v1:cpVect, v2:cpVect, a:Float ) Local angle:Float = ACos( cpvdot( v1, v2 ) ) Return cpvslerp( v1, v2, Min( a, angle ) / angle ) End Function ' Returns the unit length vector for the given angle (in radians). Function cpvforangle:cpVect( a:Float ) Return New cpVect( Cos( a ), Sin( a ) ) End Function ' Returns the angular direction v is pointing in (in radians). Function cpvtoangle:Float( v:cpVect ) Return ATan2( v.y, v.x ) End Function ' Returns a string representation of v. Intended mostly for debugging purposes and not production use. Function cpvstr:String( v:cpVect ) Return String( "( " + v.x + ", " + v.y + " )" ) End Function ' Check if two vectors are equal. (Be careful when comparing floating point numbers!) Function cpveql:Bool( v1:cpVect, v2:cpVect ) Return ( v1.x = v2.x And v1.y = v2.y ) End Function ' Add two vectors Function cpvadd:cpVect( v1:cpVect, v2:cpVect ) Return New cpVect( v1.x + v2.x, v1.y + v2.y ) End Function ' Negate a vector. Function cpvneg:cpVect( v:cpVect ) Return New cpVect( -v.x, -v.y ) End Function ' Subtract two vectors. Function cpvsub:cpVect( v1:cpVect, v2:cpVect ) Return New cpVect( v1.x - v2.x, v1.y - v2.y ) End Function ' Scalar multiplication. Function cpvmult:cpVect( v:cpVect, s:Float ) Return New cpVect( v.x * s, v.y * s ) End Function ' Vector dot product. Function cpvdot:Float( v1:cpVect, v2:cpVect ) Return v1.x * v2.x + v1.y * v2.y End Function ' 2D vector cross product analog. ' The cross product of 2D vectors results in a 3D vector with only a z component. ' This function returns the magnitude of the z value. Function cpvcross:Float( v1:cpVect, v2:cpVect ) Return v1.x * v2.y - v1.y * v2.x End Function ' Returns a perpendicular vector. (90 degree rotation) Function cpvperp:cpVect( v:cpVect ) Return New cpVect( -v.y, v.x ) End Function ' Returns a perpendicular vector. (-90 degree rotation) Function cpvrperp:cpVect( v:cpVect ) Return New cpVect( v.y, -v.x ) End Function ' Returns the vector projection of v1 onto v2. Function cpvproject:cpVect( v1:cpVect, v2:cpVect ) Return cpvmult( v2, cpvdot( v1, v2 ) / cpvdot( v2, v2 ) ) End Function ' Uses complex number multiplication to rotate v1 by v2. Scaling will occur if v1 is not a unit vector. Function cpvrotate:cpVect( v1:cpVect, v2:cpVect ) Return New cpVect( v1.x * v2.x - v1.y * v2.y, v1.x * v2.y + v1.y * v2.x ) End Function ' Inverse of cpvrotate(). Function cpvunrotate:cpVect( v1:cpVect, v2:cpVect ) Return New cpVect( v1.x * v2.x + v1.y * v2.y, v1.y * v2.x - v1.x * v2.y ) End Function ' Returns the squared length of v. Faster than cpvlength() when you only need to compare lengths. Function cpvlengthsq:Float( v:cpVect ) Return cpvdot( v, v ) End Function ' Linearly interpolate between v1 and v2. Function cpvlerp:cpVect( v1:cpVect, v2:cpVect, t:Float ) Return cpvadd( cpvmult( v1, 1.0 - t ), cpvmult( v2, t ) ) End Function ' Returns a normalized copy of v. Function cpvnormalize:cpVect( v:cpVect ) Return cpvmult( v, 1.0 / cpvlength( v ) ) End Function ' Returns a normalized copy of v or vzero if v was already vzero. Protects against divide by zero errors. Function cpvnormalize_safe:cpVect( v:cpVect ) If( v.x = 0.0 And v.y = 0.0 ) Return New cpVect() Else Return cpvnormalize( v ) End If End Function ' Clamp v to length len. Function cpvclamp:cpVect( v:cpVect, len:Float ) If( cpvdot( v, v ) > len * len ) Return cpvmult( cpvnormalize( v ), len ) Else Return v End If End Function ' Linearly interpolate between v1 towards v2 by distance d. Function cpvlerpconst:cpVect( v1:cpVect, v2:cpVect, d:Float ) Return cpvadd( v1, cpvclamp( cpvsub( v2, v1 ), d ) ) End Function ' Returns the distance between v1 and v2. Function cpvdist:Float( v1:cpVect, v2:cpVect ) Return cpvlength( cpvsub( v1, v2 ) ) End Function ' Returns the squared distance between v1 and v2. Faster than vdist() when you only need to compare distances. Function cpvdistsq:Float( v1:cpVect, v2:cpVect ) Return cpvlengthsq( cpvsub( v1, v2 ) ) End Function ' Returns true if the distance between v1 and v2 is less than dist. Function cpvnear:Bool( v1:cpVect, v2:cpVect, dist:Float ) Return cpvdistsq( v1, v2 ) < dist * dist End Function |
| ||
I have been trying to figure out how to really use vectors and in the process I ran in to these tutorials: http://tonypa.pri.ee/vectors/start.html they are in flash but are really good for beginners and for learning how to do stuff. such as ball to ball collision, ball to line collision, ball to arc collision and how to apply friction and gravity. I got most of it except how to do ball to arc collision. here is the ball to wall collision in monkey: |
| ||
@Xaron, cool, looking forward to see the physics library btw, feel free to use my Vector class in the port, might be simpler and more powerful to use, however obviously the names of the methods won't be the same. |
| ||
All, great stuff. :) L8r, |
| ||
indeed, very interesting, thanks! |
| ||
Because I spotted it in the above code: What is the advantage of using Property after a Method? Is it only for readability? |
| ||
Yes, it makes the code cleaner. You can go myVector.Direction = 300 Instead of myVector.SetDirection 300 Not much of a difference, but it's nice still :) |
| ||
Tibit made this simple mario movement exampleStrict Import mojo.app Import mojo.graphics Import mojo.input Import classes.vector Function Main:Int() New MarioApp End Class Mario Field Position:Vector = New Vector( 200,200 ) Field Velocity:Vector = New Vector Field Force:Vector = New Vector Field MaximumVelocity:Float = 5 Field jumpCount:Int = 0 ' Just for fun, showing how to add double-jumping Field jumpForce:Float = 5 'How high we can jump Method Update:Void() Local groundHeight:Float = DeviceHeight() - 15 If KeyDown( KEY_LEFT ) Force.Add( New Vector( -0.1, 0) ) End If KeyDown( KEY_RIGHT ) Force.Add( New Vector(0.1, 0) ) End If KeyHit( KEY_SPACE ) ' We can only jump when out feet touch the ground If Abs(Position.Y - groundHeight) < 0.1 Force.Add( New Vector(0, -jumpForce) ) 'Jump up = Add force to Y-axis jumpCount+=1 Else If Abs(Position.Y - groundHeight) > 30 And jumpCount > 0 And jumpCount < 2 And Velocity.Y > 0 jumpCount+=1 Force.Add( New Vector(0, -jumpForce*0.8) ) 'Double jump ability End End 'We are always affected by gravity Force.Add( New Vector(0, 0.1) ) ' This affects how fast we will fall Velocity.Add( Force ) Force.Set 0,0 'Reset force, sicne we converted it all into velocity ' If Velocity.Length > MaximumVelocity ' Velocity.Length = MaximumVelocity ' Endif Position.Add Velocity Velocity.Multiply( 0.99 ) 'Friction 'Here I Reduce velocity by 5% each Update ' Collision against ground (Against the whole X-Axis actually) If Position.Y > groundHeight 'Print "Crash" Position.Y = groundHeight Velocity.Y = 0 jumpCount = 0 End End Method Draw:Void() SetColor 0,0,0 DrawOval Position.X-15, Position.Y-15, 30, 30 End End Class MarioApp Extends App Field Mario:Mario = New Mario Method OnCreate:Int() SetUpdateRate 60 Return 0 End Method OnUpdate:Int() Mario.Update() Return 0 End Method OnRender:Int() Cls 200,200,200 Mario.Draw Return 0 End End |