Last active
July 23, 2020 12:09
-
-
Save HoraceBury/9544932 to your computer and use it in GitHub Desktop.
Arrow projectile with air drag applied to feathered tail.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- arrow test | |
-- http://www.iforce2d.net/b2dtut/sticky-projectiles | |
-- video: http://screencast.com/t/vcPLSJR6xkIq | |
require("mathlib") | |
local sWidth, sHeight = display.actualContentWidth, display.actualContentHeight | |
local centerX, centerY = sWidth/2, sHeight/2 | |
local arrows = display.newGroup() | |
-- original arrow image: http://content.screencast.com/users/HoraceBury/folders/Default/media/1d553487-b39f-440c-a89c-3d4e818cf20a/arrow.png?downloadOnly=true | |
-- if you don't have/can't get this image, comment out this line... | |
local outline = graphics.newOutline( 4, "arrow.png" ) | |
-- if you don't have the image, use this instead... | |
--outline = {157,0,223,13,153,24,0,12,156,0} | |
local launchPad = display.newCircle( 500, sHeight, 50 ) | |
local maxDamping = 10 | |
local multi = 10 | |
local x, y = nil, nil | |
local zero = {x=0,y=0} | |
local one = {x=1,y=0} | |
local dragConstant = .25 | |
local trim = 300 | |
--[[ -- This was some working out while trying to directly convert (yet again) then "sticky projectiles" code into Lua... (didn't work) | |
local dc = .5 -- dragConstant | |
local pointingDirection = math.rotateTo( one, 45, zero ) | |
local flightDirection = {x=12,y=0} -- arrow:getLinearVelocity() | |
local flightSpeed = math.normalise( flightDirection ) -- normalises flightDirection and returns the length | |
local dot = math.dotProduct( flightDirection, pointingDirection ) | |
local mass = math.polygonArea( outline ) * 0.1 -- area * density | |
local dragForceMagnitude = (1 - math.abs(dot)) * flightSpeed * flightSpeed * dc * mass | |
print(mass) | |
print(dot) | |
print( math.dotProduct( -10,10 , 10,0 ) ) | |
print(dragForceMagnitude) -- this is always wayyyyy too high | |
]]-- | |
-- apply drag to tail of each arrow | |
Runtime:addEventListener( "enterFrame", function() | |
for i=1, arrows.numChildren do | |
-- get the arrow | |
local arrow = arrows[i] | |
-- get direction the arrow is facing | |
x, y = arrow:localToContent( 1, 0 ) | |
local pointing = { x=x-arrow.x, y=y-arrow.y } | |
-- get direction of flight | |
x, y = arrow:getLinearVelocity() | |
local velocityVector = { x=x, y=y } | |
-- get the ballistic velocity | |
local velocity = math.lengthOf( x, y ) | |
-- get drag direction | |
local dragDirection = { x=-x, y=-y } | |
-- get angle between the two | |
local angle = math.angleOf( {x=0,y=0}, pointing, velocityVector ) | |
-- value of angle determines how much of the drag is applied (0 - 180) | |
local fraction = math.fractionOf( 180, math.abs(angle) ) | |
-- drag force | |
local dragForce = { x=dragConstant * dragDirection.x, y=dragConstant * dragDirection.y } | |
-- feathers location | |
x, y = arrow:localToContent( -100, 0 ) | |
local feathers = { x=x, y=y } | |
-- apply drag if it is above a certain amount (an arrow dropping to the ground should just flop down after nose diving) | |
if (velocity > trim) then | |
-- choose the version which you prefer... | |
-- drag is relative to the angle between the direction of travel and facing direction | |
--arrow:applyForce( fraction*dragForce.x, fraction*dragForce.y, feathers.x, feathers.y ) | |
-- drag is relative to speed | |
arrow:applyForce( dragForce.x, dragForce.y, feathers.x, feathers.y ) | |
end | |
end | |
end ) | |
-- tap to fire arrow - location and distance from launchPad will indicate direction and speed of flight | |
Runtime:addEventListener( "tap", function(e) | |
local angle = math.angleOf( launchPad, e ) | |
local arrow = display.newImage( arrows, "arrow.png" ) | |
arrow.x, arrow.y = e.x, e.y | |
arrow.rotation = angle | |
physics.addBody( arrow, "dynamic", { outline=outline, friction=1, density=1, bounce=.1, } ) | |
arrow.angularDamping = 1 | |
arrow:setLinearVelocity( (e.x-launchPad.x)*multi, (e.y-sHeight)*multi ) | |
end ) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
settings = { | |
orientation = | |
{ | |
default = "landscapeLeft", | |
}, | |
iphone = | |
{ | |
plist= | |
{ | |
UIStatusBarHidden=true, | |
CFBundleIconFile = "Icon.png", | |
CFBundleIconFiles = { | |
"Icon.png", | |
"Icon@2x.png", | |
"Icon-72.png", | |
}, | |
}, | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
application = | |
{ | |
content = | |
{ | |
width = 1024*3, | |
height = 768*3, | |
scale = "letterbox", | |
xAlign = "left", | |
yAlign = "top", | |
antialias = true, | |
imageSuffix = | |
{ | |
["-x2"] = 2, | |
["-x4"] = 4, | |
}, | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- arrow test | |
-- http://www.iforce2d.net/b2dtut/sticky-projectiles | |
display.setStatusBar( display.HiddenStatusBar ) | |
require("physics") | |
physics.start() | |
physics.setDrawMode("hybrid") | |
physics.setGravity( 0, 9.8 ) | |
local sWidth, sHeight = display.actualContentWidth, display.actualContentHeight | |
local centerX, centerY = sWidth/2, sHeight/2 | |
physics.addBody( display.newRect( centerX, 0, sWidth, 20 ), "static" ) | |
physics.addBody( display.newRect( centerX, sHeight, sWidth, 20 ), "static" ) | |
physics.addBody( display.newRect( 0, centerY, 20, sHeight ), "static" ) | |
physics.addBody( display.newRect( sWidth, centerY, 20, sHeight ), "static" ) | |
require("arrowlib") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- mathlib.lua | |
--[[ | |
Maths extension library for use in Corona SDK by Matthew Webster. | |
All work derived from referenced sources. | |
Many of these functions are useful for trigonometry and geometry because they have developed for use within graphical user interfaces. | |
Much of these are useful when building physics games | |
twitter: @horacebury | |
blog: http://springboardpillow.blogspot.co.uk/2012/04/sample-code.html | |
code exchange: http://code.coronalabs.com/search/node/HoraceBury | |
github: https://gist.github.com/HoraceBury | |
]]-- | |
--[[ | |
References: | |
http://stackoverflow.com/questions/385305/efficient-maths-algorithm-to-calculate-intersections | |
http://stackoverflow.com/questions/4543506/algorithm-for-intersection-of-2-lines | |
http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=geometry2#reflection | |
http://gmc.yoyogames.com/index.php?showtopic=433577 | |
http://local.wasp.uwa.edu.au/~pbourke/geometry/ | |
http://alienryderflex.com/polygon/ | |
http://alienryderflex.com/polygon_fill/ | |
http://www.amazon.com/dp/1558607323/?tag=stackoverfl08-20 | |
http://www.amazon.co.uk/s/ref=nb_sb_noss_1?url=search-alias%3Daps&field-keywords=Real-Time+Collision+Detection | |
http://en.wikipedia.org/wiki/Line-line_intersection | |
http://developer.coronalabs.com/forum/2010/11/17/math-helper-functions-distancebetween-and-anglebetween | |
http://www.mathsisfun.com/algebra/vectors-dot-product.html | |
http://www.mathsisfun.com/algebra/vector-calculator.html | |
http://lua-users.org/wiki/PointAndComplex | |
http://www.math.ntnu.no/~stacey/documents/Codea/Library/Vec3.lua | |
http://www.iforce2d.net/forums/viewtopic.php?f=4&t=79&sid=b9ecd62533361594e321de04b3929d4f | |
http://rosettacode.org/wiki/Dot_product#Lua | |
http://chipmunk-physics.net/forum/viewtopic.php?f=1&t=2215 | |
http://www.fundza.com/vectors/normalize/index.html | |
http://www.mathopenref.com/coordpolygonarea2.html | |
http://stackoverflow.com/questions/2705542/returning-the-nearest-multiple-value-of-a-number | |
http://members.tripod.com/c_carleton/dotprod.html/ | |
http://www.1728.org/density.htm | |
http://www.wikihow.com/Find-the-Angle-Between-Two-Vectors | |
]]-- | |
--[[ | |
Deprecated functions (see revisions for code): | |
rad = convertDegreesToRadians( degrees ) | |
deg = convertRadiansToDegrees( radians ) | |
polygonFill( points, closed, perPixel, width, height, col ) | |
]]-- | |
--[[ | |
Multiplication & Fractions Functions: | |
]]-- | |
--[[ | |
Point Functions: | |
]]-- | |
--[[ | |
Angle Functions: | |
]]-- | |
--[[ | |
Line Functions: | |
]]-- | |
--[[ | |
Polygon Functions: | |
]]-- | |
--[[ | |
Point Functions: | |
]]-- | |
-- rounds up to the nearest multiple of the number | |
local function nearest( number, multiple ) | |
return math.round( (number / multiple) ) * multiple | |
end | |
math.nearest = nearest | |
-- Returns b represented as a fraction of a. | |
-- Eg: If a is 1000 and b is 900 the returned value is 0.9 | |
-- Often the returned value would be used in a multiplication of another value, usually a distance value. | |
local function fractionOf( a, b ) | |
return b / a | |
end | |
math.fractionOf = fractionOf | |
-- Returns b represented as a percentage of a. | |
-- Eg: If a is 1000 and b is 900 the returned value is 90 | |
-- Use: This is useful in determining how far something should be moved to complete a certain distance. | |
-- Often the returned value would be used in a division of another value, usually a distance value. | |
local function percentageOf( a, b ) | |
return fractionOf(a, b) * 100 | |
end | |
math.percentageOf = percentageOf | |
-- return a value clamped between a range | |
local function clamp( val, low, high ) | |
if (val < low) then return low end | |
if (val > high) then return high end | |
return val | |
end | |
math.clamp = clamp | |
-- rotates point around the centre by degrees | |
-- rounds the returned coordinates using math.round() if round == true | |
-- returns new coordinates object | |
local function rotateAboutPoint( point, degrees, centre ) | |
local pt = { x=point.x - centre.x, y=point.y - centre.y } | |
pt = math.rotateTo( pt, degrees ) | |
pt.x, pt.y = pt.x + centre.x, pt.y + centre.y | |
return pt | |
end | |
math.rotateAboutPoint = rotateAboutPoint | |
-- rotates a point around the (0,0) point by degrees | |
-- returns new point object | |
-- center: optional | |
local function rotateTo( point, degrees, center ) | |
if (center ~= nil) then | |
return rotateAboutPoint( point, degrees, center ) | |
else | |
local x, y = point.x, point.y | |
local theta = math.rad( degrees ) | |
local pt = { | |
x = x * math.cos(theta) - y * math.sin(theta), | |
y = x * math.sin(theta) + y * math.cos(theta) | |
} | |
return pt | |
end | |
end | |
math.rotateTo = rotateTo | |
--[[ Support values for angles ]]-- | |
local PI = (4*math.atan(1)) | |
local quickPI = 180 / PI | |
math.PI, math.quickPI = PI, quickPI | |
--[[ | |
Returns the angle. | |
Params: | |
a : Returns the angle of the point at a relative to (0,0) (east is the virtual base) | |
a, b Params: Returns the angle of b relative to a | |
a, b, c Params: Returns the angle found at a for between b and c | |
]]-- | |
local function angleOf( ... ) | |
local a, b, c = arg[1], arg[2], arg[3] | |
if (#arg == 1) then | |
-- angle of a relative to (0,0) | |
return math.atan2( a.y, a.x ) * quickPI -- 180 / PI -- math.pi | |
elseif (#arg == 2) then | |
-- angle of b relative to a | |
return math.atan2( b.y - a.y, b.x - a.x ) * quickPI -- 180 / PI -- math.pi | |
elseif (#arg == 3) then | |
-- angle between b and c found at a | |
local deg = angleOf( a, b ) - angleOf( a, c ) -- target - source | |
if (deg > 180) then | |
deg = deg - 360 | |
elseif (deg < -180) then | |
deg = deg + 360 | |
end | |
return deg | |
end | |
-- wrong set of parameters | |
return nil | |
end | |
math.angleOf = angleOf | |
-- Brent Sorrentino | |
-- Returns the angle between the objects | |
local function angleBetween( srcObj, dstObj ) | |
local xDist = dstObj.x - srcObj.x | |
local yDist = dstObj.y - srcObj.y | |
local angleBetween = math.deg( math.atan( yDist / xDist ) ) | |
if ( srcObj.x < dstObj.x ) then | |
angleBetween = angleBetween + 90 | |
else | |
angleBetween = angleBetween - 90 | |
end | |
return angleBetween | |
end | |
math.angleBetween = angleBetween | |
--[[ | |
Calculate the angle between two lines. | |
Params: | |
lineA - The first line { a={x,y}, b={x,y} } | |
lineA - The first line { a={x,y}, b={x,y} } | |
]]-- | |
local function angleBetweenLines( lineA, lineB ) | |
local angle1 = math.atan2( lineA.a.y - lineA.b.y, lineA.a.x - lineA.b.x ) | |
local angle2 = math.atan2( lineB.a.y - lineB.b.y, lineB.a.x - lineB.b.x ) | |
return math.deg( angle1 - angle2 ) | |
end | |
math.angleBetweenLines = angleBetweenLines | |
-- returns the smallest angle between the two angles | |
-- ie: the difference between the two angles via the shortest distance | |
-- returned value is signed: clockwise is negative, anticlockwise is positve | |
-- returned value wraps at +/-180 | |
-- Example code to rotate a display object by touch: | |
--[[ | |
-- called in the "moved" phase of touch event handler | |
local a = mathlib.angleBetweenPoints( target, target.prevevent ) | |
local b = mathlib.angleBetweenPoints( target, event ) | |
local d = mathlib.smallestAngleDiff( a, b ) | |
target.prev = event | |
target.rotation = target.rotation - d | |
]]-- | |
local function smallestAngleDiff( target, source ) | |
local a = target - source | |
if (a > 180) then | |
a = a - 360 | |
elseif (a < -180) then | |
a = a + 360 | |
end | |
return a | |
end | |
math.smallestAngleDiff = smallestAngleDiff | |
-- Returns the angle in degrees between the first and second points, measured at the centre | |
-- Always a positive value | |
local function angleAt( centre, first, second ) | |
local a, b, c = centre, first, second | |
local ab = math.lengthOf( a, b ) | |
local bc = math.lengthOf( b, c ) | |
local ac = math.lengthOf( a, c ) | |
local angle = math.deg( math.acos( (ab*ab + ac*ac - bc*bc) / (2 * ab * ac) ) ) | |
return angle | |
end | |
math.angleAt = angleAt | |
-- Returns true if the point is within the angle at centre measured between first and second | |
local function isPointInAngle( centre, first, second, point ) | |
local range = math.angleAt( centre, first, second ) | |
local a = math.angleAt( centre, first, point ) | |
local b = math.angleAt( centre, second, point ) | |
-- print(range,a+b) | |
return math.round(range) >= math.round(a + b) | |
end | |
math.isPointInAngle = isPointInAngle | |
-- Forces to apply based on total force and desired angle | |
-- http://developer.anscamobile.com/code/virtual-dpadjoystick-template | |
local function forcesByAngle(totalForce, angle) | |
local forces = {} | |
local radians = -math.rad(angle) | |
forces.x = math.cos(radians) * totalForce | |
forces.y = math.sin(radians) * totalForce | |
return forces | |
end | |
math.forcesByAngle = forcesByAngle | |
-- returns the distance between points a and b | |
-- b is optional. assumes distance from 0,0 if b is nil | |
local function lengthOf( a, b ) | |
if (type(a) == "number") then | |
a = {x=a,y=b} | |
b = nil | |
end | |
if (b == nil) then | |
b = {x=0,y=0} | |
end | |
local width, height = b.x-a.x, b.y-a.y | |
return (width*width + height*height)^0.5 -- math.sqrt(width*width + height*height) | |
-- nothing wrong with math.sqrt, but I believe the ^.5 is faster | |
end | |
math.lengthOf = lengthOf | |
--[[ | |
Description: | |
Extends the point away from or towards the origin to the length of len. | |
Params: | |
max = | |
If param max is nil then the lenOrMin value is the distance to calculate the point's location | |
If param max is not nil then the lenOrMin value is the minimum clamping distance to extrude to | |
lenOrMin = the length or the minimum length to extrude the point's distance to | |
max = the maximum length to extrude to | |
Returns: | |
{x,y} = extruded point | |
]]-- | |
local function extrudeToLen( origin, point, lenOrMin, max ) | |
local length = lengthOf( origin, point ) | |
if (length == 0) then | |
return origin.x, origin.y | |
end | |
local len = lenOrMin | |
if (max ~= nil) then | |
if (length < lenOrMin) then | |
len = lenOrMin | |
elseif (length > max) then | |
len = max | |
else -- the point is within the min/max clamping range | |
return point.x, point.y | |
end | |
end | |
local factor = len / length | |
local x, y = (point.x - origin.x) * factor, (point.y - origin.y) * factor | |
return x + origin.x, y + origin.y, x, y | |
end | |
math.extrudeToLen = extrudeToLen | |
--[[ | |
Performs unit normalisation of a vector. | |
Description: | |
Unit normalising is basically converting the length of a line to be a fraction of 1.0 | |
This function modified the vector value passed in and returns the length as returned by lengthOf() | |
Note: | |
Can also be performed like this: | |
function Normalise(vector) | |
local x,y = x/(x^2 + y^2)^(1/2), y/(x^2 + y^2)^(1/2) | |
local unitVector = {x=x,y=y} | |
return unitVector | |
end | |
Ref: | |
http://www.fundza.com/vectors/normalize/index.html | |
]]-- | |
local function normalise( vector ) | |
local len = math.lengthOf( vector ) | |
vector.x = vector.x / len | |
vector.y = vector.y / len | |
return len | |
end | |
math.normalise = normalise | |
-- calculates the area of a polygon | |
-- will not calculate area for self-intersecting polygons (where vertices cross each other) | |
-- points: table of {x,y} points | |
-- ref: http://www.mathopenref.com/coordpolygonarea2.html | |
local function polygonArea( points ) | |
if (type(points[1]) == "number") then | |
points = math.tableToPoints( points ) | |
end | |
local count = #points | |
if (points.numChildren) then | |
count = points.numChildren | |
end | |
local area = 0 -- Accumulates area in the loop | |
local j = count -- The last vertex is the 'previous' one to the first | |
for i=1, count do | |
area = area + (points[j].x + points[i].x) * (points[j].y - points[i].y) | |
j = i -- j is previous vertex to i | |
end | |
return math.abs(area/2) | |
end | |
math.polygonArea = polygonArea | |
-- Returns true if the dot { x,y } is within the polygon defined by points table { {x,y},{x,y},{x,y},... } | |
local function pointInPolygon( points, dot ) | |
local i, j = #points, #points | |
local oddNodes = false | |
for i=1, #points do | |
if ((points[i].y < dot.y and points[j].y>=dot.y | |
or points[j].y< dot.y and points[i].y>=dot.y) and (points[i].x<=dot.x | |
or points[j].x<=dot.x)) then | |
if (points[i].x+(dot.y-points[i].y)/(points[j].y-points[i].y)*(points[j].x-points[i].x)<dot.x) then | |
oddNodes = not oddNodes | |
end | |
end | |
j = i | |
end | |
return oddNodes | |
end | |
math.pointInPolygon = pointInPolygon | |
-- Return true if the dot { x,y } is within any of the polygons in the list | |
local function isPointInPolygons( polygons, dot ) | |
for i=1, #polygons do | |
if (pointInPolygon( polygons[i], dot )) then | |
return true | |
end | |
end | |
return false | |
end | |
math.isPointInPolygons = isPointInPolygons | |
-- Returns true if the points in the polygon wind clockwise | |
-- Does not consider that the vertices may intersect (lines between points might cross over) | |
local function isPolygonClockwise( pointList ) | |
local area = 0 | |
if (type(pointList[1]) == "number") then | |
pointList = math.pointsToTable( pointList ) | |
print("#pointList",#pointList) | |
end | |
for i = 1, #pointList-1 do | |
local pointStart = { x=pointList[i].x - pointList[1].x, y=pointList[i].y - pointList[1].y } | |
local pointEnd = { x=pointList[i + 1].x - pointList[1].x, y=pointList[i + 1].y - pointList[1].y } | |
area = area + (pointStart.x * -pointEnd.y) - (pointEnd.x * -pointStart.y) | |
end | |
return (area < 0) | |
end | |
math.isPolygonClockwise = isPolyClockwise | |
-- returns true if the middle point is concave when viewed as part of a polygon | |
-- a, b, c are {x,y} points | |
local function isPointConcave(a,b,c) | |
local small = smallestAngleDiff( math.angleOf(b,a), math.angleOf(b,c) ) | |
if (small < 0) then | |
return false | |
else | |
return true | |
end | |
end | |
math.isPointConcave = isPointConcave | |
-- returns true if the polygon is concave | |
-- assumes points are {x,y} tables | |
-- returns nil if there are not enough points ( < 3 ) | |
-- can accept a display group | |
local function isPolygonConcave( points ) | |
local count = points.numChildren | |
if (count == nil) then | |
count = #points | |
end | |
if (count < 3) then | |
return nil | |
end | |
local isConcave = true | |
for i=1, count do | |
if (i == 1) then | |
isConcave = isPointConcave( points[count],points[1],points[2] ) | |
elseif (i == count) then | |
isConcave = isPointConcave( points[count-1],points[count],points[1] ) | |
else | |
isConcave = isPointConcave( points[i-1], points[i], points[i+1] ) | |
end | |
if (not isConcave) then | |
return false | |
end | |
end | |
return true | |
end | |
math.isPolygonConcave = isPolygonConcave | |
-- returns list of points where a polygon intersects with the line a,b | |
-- assumes polygon is standard display format: { x,y,x,y,x,y,x,y, ... } | |
-- returns collection of intersection points with the polygon line's index {x,y,lineIndex} | |
-- sort: true to sort the points into order from a to b | |
local function polygonLineIntersection( polygon, a, b, sort ) | |
local points = {} | |
for i=1, #polygon-3, 2 do | |
local success, pt = math.doLinesIntersect( a, b, { x=polygon[i], y=polygon[i+1] }, { x=polygon[i+2], y=polygon[i+3] } ) | |
if (success) then | |
pt.lineIndex = i | |
points[ #points+1 ] = pt | |
end | |
end | |
if (sort) then | |
table.sort( points, function(a,b) return math.lengthOf(e,a) > math.lengthOf(e,b) end ) | |
end | |
return points | |
end | |
math.polygonLineIntersection = polygonLineIntersection | |
--[[ | |
Products | |
]]-- | |
--[[ | |
Calculates the dot product of two lines. | |
This function implements the simple form of the dot product calculation: a · b = ax × bx + ay × by | |
The lines can be provided in 3 forms: | |
Parameters: | |
a: {x,y} | |
b: {x,y} | |
Example: | |
print( dotProduct( {x=10,y=10}, {x=-10,y=10} ) ) | |
Parameters: | |
a: {a,b} | |
b: {a,b} | |
Example: | |
print( dotProduct( | |
{ a={x=10,y=10}, b={x=101,y=5} }, | |
{ a={x=10,y=-10}, b={x=51,y=10} } | |
)) | |
Params: | |
lenA: Length A | |
lenB: Length B | |
deg: Angle between points A and B in degrees | |
Example: | |
print( dotProduct( 23, 10, 90 ) ) | |
Ref: | |
http://www.mathsisfun.com/algebra/vectors-dot-product.html | |
http://members.tripod.com/c_carleton/dotprod.html/ | |
http://www.mathsisfun.com/algebra/vector-calculator.html | |
]]-- | |
local function dotProduct( ... ) | |
local ax, ax, bx, by | |
if (#arg == 2 and arg[1].a == nil) then | |
-- two vectors - get the vectors | |
ax, ay = arg[1].x, arg[1].y | |
bx, by = arg[2].x, arg[2].y | |
elseif (#arg == 2 and a.x == nil) then | |
-- two lines - calculate the vectors | |
ax = arg[1].b.x - arg[1].a.x | |
ay = arg[1].b.y - arg[1].a.y | |
bx = arg[2].b.x - arg[2].a.x | |
by = arg[2].b.y - arg[2].a.y | |
elseif (#arg == 3 and type(arg[1]) == "number") then | |
-- two lengths and an angle: lenA * lenB * math.cos( deg ) | |
return arg[1] * arg[2] * math.cos( arg[3] ) | |
elseif (#arg == 4 and type(arg[1]) == "number") then | |
-- two lines, params are (x,y,x,y) - get the vectors | |
ax, ay = arg[1], arg[2] | |
bx, by = arg[3], arg[4] | |
end | |
-- multiply the x's, multiply the y's, then add | |
local dot = ax * bx + ay * by | |
return dot | |
end | |
math.dotProduct = dotProduct | |
--[[ | |
Description: | |
Calculates the cross product of a vector. | |
Ref: | |
http://www.math.ntnu.no/~stacey/documents/Codea/Library/Vec3.lua | |
]]-- | |
local function crossProduct( a, b ) | |
local x, y, z | |
x = a.y * (b.z or 0) - (a.z or 0) * b.y | |
y = (a.z or 0) * b.x - a.x * (b.z or 0) | |
z = a.x * b.y - a.y * b.x | |
return { x=x, y=y, z=z } | |
end | |
math.crossProduct = crossProduct | |
--[[ | |
Description: | |
Perform the cross product on two vectors. In 2D this produces a scalar. | |
Params: | |
a: {x,y} | |
b: {x,y} | |
Ref: | |
http://www.iforce2d.net/forums/viewtopic.php?f=4&t=79&sid=b9ecd62533361594e321de04b3929d4f | |
]]-- | |
local function b2CrossVectVect( a, b ) | |
return a.x * b.y - a.y * b.x; | |
end | |
math.b2CrossVectVect = b2CrossVectVect | |
--[[ | |
Description: | |
Perform the cross product on a vector and a scalar. In 2D this produces a vector. | |
Params: | |
a: {x,y} | |
b: float | |
Ref: | |
http://www.iforce2d.net/forums/viewtopic.php?f=4&t=79&sid=b9ecd62533361594e321de04b3929d4f | |
]]-- | |
local function b2CrossVectFloat( a, s ) | |
return { x = s * a.y, y = -s * a.x } | |
end | |
math.b2CrossVectFloat = b2CrossVectFloat | |
--[[ | |
Description: | |
Perform the cross product on a scalar and a vector. In 2D this produces a vector. | |
Params: | |
a: float | |
b: {x,y} | |
Ref: | |
http://www.iforce2d.net/forums/viewtopic.php?f=4&t=79&sid=b9ecd62533361594e321de04b3929d4f | |
]]-- | |
local function b2CrossFloatVect( s, a ) | |
return { x = -s * a.y, y = s * a.x } | |
end | |
math.b2CrossFloatVect = b2CrossFloatVect | |
--[[ | |
Polygons | |
]]-- | |
--[[ | |
Description: | |
Calculates the average of all the x's and all the y's and returns the average centre of all points. | |
Works with a display group or table proceeding { {x,y}, {x,y}, ... } | |
Params: | |
pts = list of {x,y} points to get the average middle point from | |
Returns: | |
{x,y} = average centre location of all the points | |
]]-- | |
local function midPoint( ... ) | |
local pts = arg | |
local x, y, c = 0, 0, #pts | |
if (pts.numChildren and pts.numChildren > 0) then c = pts.numChildren end | |
for i=1, c do | |
x = x + pts[i].x | |
y = y + pts[i].y | |
end | |
return { x=x/c, y=y/c } | |
end | |
math.midPoint = midPoint | |
--[[ | |
Description: | |
Calculates the average of all the x's and all the y's and returns the average centre of all points. | |
Works with a table proceeding {x,y,x,y,...} as used with display.newLine or physics.addBody | |
Params: | |
pts = table of x,y values in sequence | |
Returns: | |
x, y = average centre location of all points | |
]]-- | |
local function midPointOfShape( pts ) | |
local x, y, c, t = 0, 0, #pts, #pts/2 | |
for i=1, c-1, 2 do | |
x = x + pts[i] | |
y = y + pts[i+1] | |
end | |
return x/t, y/t | |
end | |
math.midPointOfShape = midPointOfShape | |
-- returns true when the point is on the right of the line formed by the north/south points | |
local function isOnRight( north, south, point ) | |
local a, b, c = north, south, point | |
local factor = (b.x - a.x)*(c.y - a.y) - (b.y - a.y)*(c.x - a.x) | |
return factor > 0, factor | |
end | |
math.isOnRight = isOnRight | |
-- reflect point across line from north to south | |
local function reflect( north, south, point ) | |
local x1, y1, x2, y2 = north.x, north.y, south.x, south.y | |
local x3, y3 = point.x, point.y | |
local x4, y4 = 0, 0 -- reflected point | |
local dx, dy, t, d | |
dx = y2 - y1 | |
dy = x1 - x2 | |
t = dx * (x3 - x1) + dy * (y3 - y1) | |
t = t / (dx * dx + dy * dy) | |
x = x3 - 2 * dx * t | |
y = y3 - 2 * dy * t | |
return { x=x, y=y } | |
end | |
math.reflect = reflect | |
-- This is based off an explanation and expanded math presented by Paul Bourke: | |
-- It takes two lines as inputs and returns true if they intersect, false if they don't. | |
-- If they do, ptIntersection returns the point where the two lines intersect. | |
-- params a, b = first line | |
-- params c, d = second line | |
-- param ptIntersection: The point where both lines intersect (if they do) | |
-- http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/ | |
-- http://paulbourke.net/geometry/pointlineplane/ | |
local function doLinesIntersect( a, b, c, d ) | |
-- parameter conversion | |
local L1 = {X1=a.x,Y1=a.y,X2=b.x,Y2=b.y} | |
local L2 = {X1=c.x,Y1=c.y,X2=d.x,Y2=d.y} | |
-- Denominator for ua and ub are the same, so store this calculation | |
local d = (L2.Y2 - L2.Y1) * (L1.X2 - L1.X1) - (L2.X2 - L2.X1) * (L1.Y2 - L1.Y1) | |
-- Make sure there is not a division by zero - this also indicates that the lines are parallel. | |
-- If n_a and n_b were both equal to zero the lines would be on top of each | |
-- other (coincidental). This check is not done because it is not | |
-- necessary for this implementation (the parallel check accounts for this). | |
if (d == 0) then | |
return false | |
end | |
-- n_a and n_b are calculated as seperate values for readability | |
local n_a = (L2.X2 - L2.X1) * (L1.Y1 - L2.Y1) - (L2.Y2 - L2.Y1) * (L1.X1 - L2.X1) | |
local n_b = (L1.X2 - L1.X1) * (L1.Y1 - L2.Y1) - (L1.Y2 - L1.Y1) * (L1.X1 - L2.X1) | |
-- Calculate the intermediate fractional point that the lines potentially intersect. | |
local ua = n_a / d | |
local ub = n_b / d | |
-- The fractional point will be between 0 and 1 inclusive if the lines | |
-- intersect. If the fractional calculation is larger than 1 or smaller | |
-- than 0 the lines would need to be longer to intersect. | |
if (ua >= 0 and ua <= 1 and ub >= 0 and ub <= 1) then | |
local x = L1.X1 + (ua * (L1.X2 - L1.X1)) | |
local y = L1.Y1 + (ua * (L1.Y2 - L1.Y1)) | |
return true, {x=x, y=y} | |
end | |
return false | |
end | |
math.doLinesIntersect = doLinesIntersect | |
-- returns the closest point on the line between A and B from point P | |
local function GetClosestPoint( A, B, P, segmentClamp ) | |
local AP = { x=P.x - A.x, y=P.y - A.y } | |
local AB = { x=B.x - A.x, y=B.y - A.y } | |
local ab2 = AB.x*AB.x + AB.y*AB.y | |
local ap_ab = AP.x*AB.x + AP.y*AB.y | |
local t = ap_ab / ab2 | |
if (segmentClamp or true) then | |
if (t < 0.0) then | |
t = 0.0 | |
elseif (t > 1.0) then | |
t = 1.0 | |
end | |
end | |
local Closest = { x=A.x + AB.x * t, y=A.y + AB.y * t } | |
return Closest | |
end | |
math.GetClosestPoint = GetClosestPoint | |
-- converts a table of {x,y,x,y,...} to points {x,y} | |
local function tableToPoints( tbl ) | |
local pts = {} | |
for i=1, #tbl-1, 2 do | |
pts[#pts+1] = { x=tbl[i], y=tbl[i+1] } | |
end | |
return pts | |
end | |
math.tableToPoints = tableToPoints | |
-- converts a list of points {x,y} to a table of coords {x,y,x,y,...} | |
local function pointsToTable( pts ) | |
local tbl = {} | |
for i=1, #pts do | |
tbl[#tbl+1] = pts[i].x | |
tbl[#tbl+1] = pts[i].y | |
end | |
return tbl | |
end | |
math.pointsToTable = pointsToTable |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment