Skip to content

Instantly share code, notes, and snippets.

@kirubz
Forked from xeoncross/mathlib.lua
Created August 27, 2018 13:33
Show Gist options
  • Select an option

  • Save kirubz/a46d28b466d148041b6208503271816c to your computer and use it in GitHub Desktop.

Select an option

Save kirubz/a46d28b466d148041b6208503271816c to your computer and use it in GitHub Desktop.

Revisions

  1. @HoraceBury HoraceBury revised this gist Mar 12, 2014. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions mathlib.lua
    Original file line number Diff line number Diff line change
    @@ -332,7 +332,7 @@ end
    Ref:
    http://www.mathsisfun.com/algebra/vectors-dot-product.html
    ]]--
    math.dotProductByLenAngle( lenA, lenB, deg )
    math.dotProductByLenAngle = function( lenA, lenB, deg )
    return lenA * lenB * math.cos(deg)
    end

    @@ -651,7 +651,7 @@ end
    math.tableToPoints = function( tbl )
    local pts = {}

    for i=1, #coords-1, 2 do
    for i=1, #tbl-1, 2 do
    pts[#pts+1] = { x=tbl[i], y=tbl[i+1] }
    end

  2. @HoraceBury HoraceBury revised this gist Mar 12, 2014. 1 changed file with 18 additions and 6 deletions.
    24 changes: 18 additions & 6 deletions mathlib.lua
    Original file line number Diff line number Diff line change
    @@ -609,9 +609,9 @@ end

    -- calculates the area of a polygon
    -- will not calculate area for self-intersecting polygons (where vertices cross each other)
    -- can accept a table of {x,y} or a display group
    -- points: table of {x,y} points
    -- ref: http://www.mathopenref.com/coordpolygonarea2.html
    function polygonArea( points )
    math.polygonArea = function( points )
    local count = #points
    if (points.numChildren) then
    count = points.numChildren
    @@ -647,12 +647,24 @@ function pointInPolygon( points, dot )
    return oddNodes
    end

    -- converts a table of {x,y,x,y,x,y,x,y,...} to { {x,y}, {x,y}, {x,y}, {x,y}, }
    math.convertCoordsToTable = function( coords )
    local tbl = {}
    -- converts a table of {x,y,x,y,...} to points {x,y}
    math.tableToPoints = function( tbl )
    local pts = {}

    for i=1, #coords-1, 2 do
    tbl[#tbl+1] = { x=coords[i], y=coords[i+1] }
    pts[#pts+1] = { x=tbl[i], y=tbl[i+1] }
    end

    return pts
    end

    -- converts a list of points {x,y} to a table of coords {x,y,x,y,...}
    math.pointsToTable = function( pts )
    local tbl = {}

    for i=1, #pts do
    tbl[#tbl+1] = pts[i].x
    tbl[#tbl+1] = pts[i].y
    end

    return tbl
  3. @HoraceBury HoraceBury revised this gist Mar 12, 2014. 1 changed file with 134 additions and 222 deletions.
    356 changes: 134 additions & 222 deletions mathlib.lua
    Original file line number Diff line number Diff line change
    @@ -1,69 +1,89 @@
    -- mathlib
    -- mathlib.lua

    --[[
    Maths extension library for use in Corona SDK by Matthew Webster.
    All work derived from referenced sources.
    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


    -- Functions:
    -- nearestMultiple = math.nearest( number, multiple )
    -- len = lengthOf( a, b )
    -- rad = convertDegreesToRadians( degrees ) DEPRECATED
    -- deg = convertRadiansToDegrees( radians ) DEPRECATED
    -- {x,y} = rotateTo( point, degrees )
    -- {x,y} = rotateAboutPoint( point, centre, degrees, round )
    -- angleOfPoint( pt )
    -- angleBetweenPoints( a, b )
    -- angleOf( a, b )
    -- angle = angleBetween( srcObj, dstObj )
    -- dot = dotProduct( ax, ay, bx, by )
    -- extrudeToLen( origin, point, lenOrMin, max )
    -- smallestAngleDiff( target, source )
    -- angleAt( centre, first, second )
    -- isPointInAngle( centre, first, second, point )
    -- len = Normalise(vector)
    -- dot = dotProduct(a,b)
    -- dot = dotProductByDimensions( a, b )
    -- dot = dotProductByCos( a, b )
    -- cross = crossProduct( a, b )
    -- cross = b2CrossVectVect( a, b )
    -- {x,y} = b2CrossVectFloat( a, s )
    -- {x,y} = b2CrossFloatVect( s, a )
    -- fractionOf( a, b )
    -- percentageOf( a, b )
    -- midPoint( pts )
    -- midPointOfShape( pts )
    -- isOnRight( north, south, point )
    -- reflect( north, south, point )
    -- math.doLinesIntersect( a, b, c, d )
    -- GetClosestPoint( A, B, P, segmentClamp )
    -- area = polygonArea( points )
    -- pointInPolygon( points, dot )
    -- pointInPolygons( polygons, dot )
    -- isPolyClockwise( pointList )
    -- polygonFill( points, closed, perPixel, width, height, col )
    -- pixelFill( points, closed, perPixel, width, height )
    -- math.clamp( val, low, high )
    -- forcesByAngle(totalForce, angle)
    --[[
    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
    ]]--

    --[[
    Functions:
    nearestMultiple = math.nearest( number, multiple )
    len = lengthOf( a, b )
    {x,y} = rotateTo( point, degrees )
    {x,y} = rotateAboutPoint( point, centre, degrees, round )
    angleOfPoint( pt )
    angleBetweenPoints( a, b )
    angleOf( a, b )
    angle = angleBetween( srcObj, dstObj )
    dot = dotProduct( ax, ay, bx, by )
    extrudeToLen( origin, point, lenOrMin, max )
    smallestAngleDiff( target, source )
    angleAt( centre, first, second )
    isPointInAngle( centre, first, second, point )
    len = normalise(vector)
    dot = dotProduct(a,b)
    dot = dotProductByDimensions( a, b )
    dot = dotProductByCos( a, b )
    cross = crossProduct( a, b )
    cross = b2CrossVectVect( a, b )
    {x,y} = b2CrossVectFloat( a, s )
    {x,y} = b2CrossFloatVect( s, a )
    fractionOf( a, b )
    percentageOf( a, b )
    midPoint( pts )
    midPointOfShape( pts )
    isOnRight( north, south, point )
    reflect( north, south, point )
    math.doLinesIntersect( a, b, c, d )
    GetClosestPoint( A, B, P, segmentClamp )
    area = polygonArea( points )
    pointInPolygon( points, dot )
    pointInPolygons( polygons, dot )
    isPolyClockwise( pointList )
    polygonFill( points, closed, perPixel, width, height, col )
    pixelFill( points, closed, perPixel, width, height )
    math.clamp( val, low, high )
    forcesByAngle(totalForce, angle)
    ]]--

    --[[
    Deprecated (see revisions for code):
    rad = convertDegreesToRadians( degrees )
    deg = convertRadiansToDegrees( radians )
    polygonFill( points, closed, perPixel, width, height, col )
    ]]--

    -- rounds up to the nearest multiple of the number
    math.nearest = function( number, multiple )
    @@ -81,20 +101,6 @@ math.lengthOf = function( a, b )
    -- nothing wrong with math.sqrt, but I believe the ^.5 is faster
    end

    --[[ DEPRECATED
    -- converts degree value to radian value, useful for angle calculations
    function convertDegreesToRadians( degrees )
    -- return (math.pi * degrees) / 180
    return math.rad(degrees)
    end
    ]]--

    --[[ DEPRECATED
    function convertRadiansToDegrees( radians )
    return math.deg(radians)
    end
    ]]--

    -- rotates point around the centre by degrees
    -- rounds the returned coordinates using math.round() if round == true
    -- returns new coordinates object
    @@ -163,33 +169,6 @@ local function angleBetween ( srcObj, dstObj )
    return angleBetween
    end

    --[[
    Calculates the dot product of two lines. The lines are provided as {a,b}
    Each end point of the line objects are of the format {x,y}
    Params:
    a - first line, format: {a,b}
    b - second line, format: {a,b}
    Example:
    print(dotProduct(
    {a={x=0,y=0},b={x=101,y=5}},
    {a={x=0,y=0},b={x=51,y=10}}
    ))
    Ref:
    http://www.mathsisfun.com/algebra/vector-calculator.html
    ]]--
    function dotProduct( a, b )
    -- get dimensions
    local ax, ay = a.b.x-a.a.x, a.b.y-a.a.y
    local bx, by = b.b.x-b.a.x, b.b.y-b.a.y

    -- multiply the x's, multiply the y's, then add
    local dot = ax * bx + ay * by
    return dot
    end

    --[[
    Description:
    Extends the point away from or towards the origin to the length of len.
    @@ -294,6 +273,33 @@ math.normalise = function( vector )
    return len
    end

    --[[
    Calculates the dot product of two lines. The lines are provided as {a,b}
    Each end point of the line objects are of the format {x,y}
    Params:
    a - first line, format: {a,b}
    b - second line, format: {a,b}
    Example:
    print(dotProduct(
    {a={x=0,y=0},b={x=101,y=5}},
    {a={x=0,y=0},b={x=51,y=10}}
    ))
    Ref:
    http://www.mathsisfun.com/algebra/vector-calculator.html
    ]]--
    function dotProduct( a, b )
    -- get dimensions
    local ax, ay = a.b.x-a.a.x, a.b.y-a.a.y
    local bx, by = b.b.x-b.a.x, b.b.y-b.a.y

    -- multiply the x's, multiply the y's, then add
    local dot = ax * bx + ay * by
    return dot
    end

    --[[
    Calculates the dot product of a pair of vectors.
    @@ -311,11 +317,29 @@ function dotProduct(a, b)
    return ret]]--
    return a.x*b.x + a.y*b.y + (a.z or 0)*(b.z or 0)
    end


    --[[
    Dot product of two lengths.
    Params:
    lenA - Length A
    lenB - Length B
    deg - Angle between points A and B in degrees
    Returns:
    Dot product.
    Ref:
    http://www.mathsisfun.com/algebra/vectors-dot-product.html
    ]]--
    math.dotProductByLenAngle( lenA, lenB, deg )
    return lenA * lenB * math.cos(deg)
    end

    --[[
    Calculates the dot product of two lines. The lines are provided as {a,b}
    Each end point of the line objects are of the format {x,y}
    This function implements the simple form of the dot product calculation: a · b = ax × bx + ay × by
    This function implements the simple form of the dot product calculation: a · b = ax × bx + ay × by
    Params:
    a - first line, format: {a,b}
    @@ -331,7 +355,7 @@ end
    http://www.mathsisfun.com/algebra/vectors-dot-product.html
    http://www.mathsisfun.com/algebra/vector-calculator.html
    ]]--
    function dotProductByDimensions( a, b )
    math.dotProductByDimensions = function( a, b )
    -- get dimensions
    local ax, ay = a.b.x-a.a.x, a.b.y-a.a.y
    local bx, by = b.b.x-b.a.x, b.b.y-b.a.y
    @@ -344,7 +368,7 @@ end
    --[[
    Calculates the dot product of two vectors. The vectors are provided as {{x,y},{x,y}}
    Each end point of the line objects are of the format {x,y}
    This function implements the COS form of the dot product calculation: a · b = |a| × |b| × cos(?)
    This function implements the COS form of the dot product calculation: a · b = |a| × |b| × cos(?)
    Params:
    a - first vector, format: {x,y}
    @@ -663,118 +687,6 @@ function isPolyClockwise( pointList )
    return (area < 0)
    end

    --[[
    -- returns a display group with the pixels of the polygon filled in
    -- values in the points table need to be {x,y} tables
    -- polygon does not need to be anti/clockwise winding
    -- param points: the polygon points
    -- param closed: true to cut the polygon out of the screen, false to fill it in
    -- param perPixel: true to fill each pixel individually
    -- param width, height: width and height of each pixel or height of each row
    -- param col: table containing {r,g,b} to set the polygon colour and override the default random colours
    function polygonFill( points, closed, perPixel, width, height, col )
    local fill = display.newGroup()
    local nodes, nodeX, pixelX, pixelY, i, j, swap = 0, {}, 0, 0, 0, 0, 0
    local IMAGE_BOT, IMAGE_TOP, IMAGE_LEFT, IMAGE_RIGHT = 0, display.contentHeight, display.contentWidth, 0
    local closestPoint, shortestDist, closestIndex = {x=0,y=0}, 5000, 0
    -- if the polygon is closed then encase it in a rect the size of the screen
    -- the starting point needs to be closest to the closestPoint
    if (closed) then
    for i=1, #points do
    if (closed) then
    local len = lengthOf( points[i], closestPoint )
    if (len < shortestDist) then
    closestIndex = i
    shortestDist = len
    end
    end
    end
    -- insert bounding box
    table.insert(points,closestIndex+0,{x=points[closestIndex].x,y=points[closestIndex].y})
    table.insert(points,closestIndex+1,closestPoint)
    table.insert(points,closestIndex+2,{x=display.contentWidth,y=0})
    table.insert(points,closestIndex+3,{x=display.contentWidth,y=display.contentHeight})
    table.insert(points,closestIndex+4,{x=0,y=display.contentHeight})
    table.insert(points,closestIndex+5,closestPoint)
    end
    -- find highest, lowest, left-most and right-most points on screen (and their indices)
    for i=1, #points do
    if (points[i].y > IMAGE_BOT) then IMAGE_BOT = points[i].y end
    if (points[i].y < IMAGE_TOP) then IMAGE_TOP = points[i].y end
    if (points[i].x > IMAGE_RIGHT) then IMAGE_RIGHT = points[i].x end
    if (points[i].x < IMAGE_LEFT) then IMAGE_LEFT = points[i].x end
    end
    -- Loop through the rows of the image.
    for pixelY=IMAGE_TOP, IMAGE_BOT, height do
    -- Build a list of nodes.
    nodes = 1
    j=#points
    for i=1, #points do
    if (points[i].y<pixelY and points[j].y>=pixelY or points[j].y<pixelY and points[i].y>=pixelY) then
    nodeX[nodes] = (points[i].x+(pixelY-points[i].y)/(points[j].y-points[i].y)*(points[j].x-points[i].x))
    nodes = nodes + 1
    end
    j = i
    end
    -- Sort the nodes, via a simple “Bubble sort.
    i = 1
    while (i < nodes-1) do
    if (nodeX[i]>nodeX[i+1]) then
    swap=nodeX[i]
    nodeX[i]=nodeX[i+1]
    nodeX[i+1]=swap
    if (i > 1) then
    i = i - 1
    end
    else
    i = i + 1
    end
    end
    -- Fill the pixels between node pairs.
    for i=1, nodes-1, 2 do
    if (nodeX[i ] >= IMAGE_RIGHT) then
    break
    end
    if (nodeX[i+1] > IMAGE_LEFT) then
    if (nodeX[i ]< IMAGE_LEFT ) then nodeX[i ] = IMAGE_LEFT end
    if (nodeX[i+1]> IMAGE_RIGHT) then nodeX[i+1] = IMAGE_RIGHT end
    if (perPixel or false) then
    for j=nodeX[i], nodeX[i+1], width do
    local rect = display.newRect( fill, 0, 0, width, height )
    rect.x, rect.y = j, pixelY
    if (col) then
    rect:setFillColor( col.r, col.g, col.b )
    else
    rect:setFillColor( math.random( 100,255 ), math.random( 100,255 ), math.random( 100,255 ) )
    end
    end
    else
    local rwidth = nodeX[i+1]-nodeX[i]
    local rect = display.newRect( fill, 0, 0, rwidth, height )
    rect.x, rect.y = nodeX[i]+rwidth/2, pixelY
    if (col) then
    rect:setFillColor( col.r, col.g, col.b )
    else
    rect:setFillColor( math.random( 100,255 ), math.random( 100,255 ), math.random( 100,255 ) )
    end
    end
    end
    end
    end
    -- return the filled polygon
    return fill
    end
    ]]--

    -- return a value clamped between a range
    math.clamp = function( val, low, high )
    if (val < low) then return low end
  4. @HoraceBury HoraceBury revised this gist Mar 12, 2014. 1 changed file with 6 additions and 2 deletions.
    8 changes: 6 additions & 2 deletions mathlib.lua
    Original file line number Diff line number Diff line change
    @@ -71,7 +71,11 @@ math.nearest = function( number, multiple )
    end

    -- returns the distance between points a and b
    -- b is optional. assumes distance from 0,0 if b is nil
    math.lengthOf = function( a, b )
    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
    @@ -283,8 +287,8 @@ end
    Ref:
    http://www.fundza.com/vectors/normalize/index.html
    ]]--
    function Normalise(vector)
    local len = lengthOf( {x=0,y=0}, vector )
    math.normalise = function( vector )
    local len = math.lengthOf( vector )
    vector.x = vector.x / len
    vector.y = vector.y / len
    return len
  5. @HoraceBury HoraceBury revised this gist Mar 10, 2014. 1 changed file with 34 additions and 9 deletions.
    43 changes: 34 additions & 9 deletions mathlib.lua
    Original file line number Diff line number Diff line change
    @@ -34,7 +34,7 @@
    -- angleOfPoint( pt )
    -- angleBetweenPoints( a, b )
    -- angleOf( a, b )
    -- angle = angleBetween ( srcObj, dstObj )
    -- angle = angleBetween( srcObj, dstObj )
    -- dot = dotProduct( ax, ay, bx, by )
    -- extrudeToLen( origin, point, lenOrMin, max )
    -- smallestAngleDiff( target, source )
    @@ -54,7 +54,7 @@
    -- midPointOfShape( pts )
    -- isOnRight( north, south, point )
    -- reflect( north, south, point )
    -- doLinesIntersect( a, b, c, d )
    -- math.doLinesIntersect( a, b, c, d )
    -- GetClosestPoint( A, B, P, segmentClamp )
    -- area = polygonArea( points )
    -- pointInPolygon( points, dot )
    @@ -311,7 +311,7 @@ end
    --[[
    Calculates the dot product of two lines. The lines are provided as {a,b}
    Each end point of the line objects are of the format {x,y}
    This function implements the simple form of the dot product calculation: a b = ax bx + ay by
    This function implements the simple form of the dot product calculation: a · b = ax × bx + ay × by
    Params:
    a - first line, format: {a,b}
    @@ -340,7 +340,7 @@ end
    --[[
    Calculates the dot product of two vectors. The vectors are provided as {{x,y},{x,y}}
    Each end point of the line objects are of the format {x,y}
    This function implements the COS form of the dot product calculation: a b = |a| |b| cos(?)
    This function implements the COS form of the dot product calculation: a · b = |a| × |b| × cos(?)
    Params:
    a - first vector, format: {x,y}
    @@ -522,7 +522,7 @@ end
    -- 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/
    function doLinesIntersect( a, b, c, d )
    math.doLinesIntersect = function( 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}
    @@ -580,7 +580,7 @@ function GetClosestPoint( A, B, P, segmentClamp )
    end

    -- calculates the area of a polygon
    -- will not calculate area for intersecting polygons (where vertices cross each other)
    -- will not calculate area for self-intersecting polygons (where vertices cross each other)
    -- can accept a table of {x,y} or a display group
    -- ref: http://www.mathopenref.com/coordpolygonarea2.html
    function polygonArea( points )
    @@ -658,7 +658,8 @@ function isPolyClockwise( pointList )

    return (area < 0)
    end


    --[[
    -- returns a display group with the pixels of the polygon filled in
    -- values in the points table need to be {x,y} tables
    -- polygon does not need to be anti/clockwise winding
    @@ -716,7 +717,7 @@ function polygonFill( points, closed, perPixel, width, height, col )
    j = i
    end
    -- Sort the nodes, via a simple ìBubble sort.
    -- Sort the nodes, via a simple “Bubble sort.
    i = 1
    while (i < nodes-1) do
    if (nodeX[i]>nodeX[i+1]) then
    @@ -768,7 +769,8 @@ function polygonFill( points, closed, perPixel, width, height, col )
    -- return the filled polygon
    return fill
    end

    ]]--

    -- return a value clamped between a range
    math.clamp = function( val, low, high )
    if (val < low) then return low end
    @@ -832,3 +834,26 @@ function isPolygonConcave( points )

    return true
    end

    -- 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
    math.polygonLineIntersection = function( 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
  6. @HoraceBury HoraceBury revised this gist Mar 9, 2014. 1 changed file with 20 additions and 3 deletions.
    23 changes: 20 additions & 3 deletions mathlib.lua
    Original file line number Diff line number Diff line change
    @@ -22,7 +22,6 @@
    -- 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://stackoverflow.com/questions/471962/how-do-determine-if-a-polygon-is-complex-convex-nonconvex


    -- Functions:
    @@ -60,8 +59,10 @@
    -- area = polygonArea( points )
    -- pointInPolygon( points, dot )
    -- pointInPolygons( polygons, dot )
    -- isPolyClockwise( pointList )
    -- polygonFill( points, closed, perPixel, width, height, col )
    -- pixelFill( points, closed, perPixel, width, height )
    -- clamp( val, low, high )
    -- math.clamp( val, low, high )
    -- forcesByAngle(totalForce, angle)

    -- rounds up to the nearest multiple of the number
    @@ -617,7 +618,18 @@ function pointInPolygon( points, dot )

    return oddNodes
    end


    -- converts a table of {x,y,x,y,x,y,x,y,...} to { {x,y}, {x,y}, {x,y}, {x,y}, }
    math.convertCoordsToTable = function( coords )
    local tbl = {}

    for i=1, #coords-1, 2 do
    tbl[#tbl+1] = { x=coords[i], y=coords[i+1] }
    end

    return tbl
    end

    -- Return true if the dot { x,y } is within any of the polygons in the list
    function pointInPolygons( polygons, dot )
    for i=1, #polygons do
    @@ -633,6 +645,11 @@ end
    function isPolyClockwise( pointList )
    local area = 0

    if (type(pointList[1]) == "number") then
    pointList = math.convertCoordsToTable( 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 }
  7. @HoraceBury HoraceBury revised this gist Mar 8, 2014. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions mathlib.lua
    Original file line number Diff line number Diff line change
    @@ -22,6 +22,7 @@
    -- 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://stackoverflow.com/questions/471962/how-do-determine-if-a-polygon-is-complex-convex-nonconvex


    -- Functions:
  8. @HoraceBury HoraceBury revised this gist Mar 8, 2014. 1 changed file with 45 additions and 0 deletions.
    45 changes: 45 additions & 0 deletions mathlib.lua
    Original file line number Diff line number Diff line change
    @@ -769,3 +769,48 @@ function forcesByAngle(totalForce, angle)

    return forces
    end

    -- returns true if the middle point is concave when viewed as part of a polygon
    -- a, b, c are {x,y} points
    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

    -- 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
    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
  9. @HoraceBury HoraceBury created this gist Mar 8, 2014.
    771 changes: 771 additions & 0 deletions mathlib.lua
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,771 @@
    -- mathlib

    -- 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


    -- Functions:
    -- nearestMultiple = math.nearest( number, multiple )
    -- len = lengthOf( a, b )
    -- rad = convertDegreesToRadians( degrees ) DEPRECATED
    -- deg = convertRadiansToDegrees( radians ) DEPRECATED
    -- {x,y} = rotateTo( point, degrees )
    -- {x,y} = rotateAboutPoint( point, centre, degrees, round )
    -- angleOfPoint( pt )
    -- angleBetweenPoints( a, b )
    -- angleOf( a, b )
    -- angle = angleBetween ( srcObj, dstObj )
    -- dot = dotProduct( ax, ay, bx, by )
    -- extrudeToLen( origin, point, lenOrMin, max )
    -- smallestAngleDiff( target, source )
    -- angleAt( centre, first, second )
    -- isPointInAngle( centre, first, second, point )
    -- len = Normalise(vector)
    -- dot = dotProduct(a,b)
    -- dot = dotProductByDimensions( a, b )
    -- dot = dotProductByCos( a, b )
    -- cross = crossProduct( a, b )
    -- cross = b2CrossVectVect( a, b )
    -- {x,y} = b2CrossVectFloat( a, s )
    -- {x,y} = b2CrossFloatVect( s, a )
    -- fractionOf( a, b )
    -- percentageOf( a, b )
    -- midPoint( pts )
    -- midPointOfShape( pts )
    -- isOnRight( north, south, point )
    -- reflect( north, south, point )
    -- doLinesIntersect( a, b, c, d )
    -- GetClosestPoint( A, B, P, segmentClamp )
    -- area = polygonArea( points )
    -- pointInPolygon( points, dot )
    -- pointInPolygons( polygons, dot )
    -- pixelFill( points, closed, perPixel, width, height )
    -- clamp( val, low, high )
    -- forcesByAngle(totalForce, angle)

    -- rounds up to the nearest multiple of the number
    math.nearest = function( number, multiple )
    return math.round( (number / multiple) ) * multiple
    end

    -- returns the distance between points a and b
    math.lengthOf = function( a, b )
    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

    --[[ DEPRECATED
    -- converts degree value to radian value, useful for angle calculations
    function convertDegreesToRadians( degrees )
    -- return (math.pi * degrees) / 180
    return math.rad(degrees)
    end
    ]]--

    --[[ DEPRECATED
    function convertRadiansToDegrees( radians )
    return math.deg(radians)
    end
    ]]--

    -- 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, center )
    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

    -- rotates a point around the (0,0) point by degrees
    -- returns new point object
    -- center: optional
    math.rotateTo = function( 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

    local PI = (4*math.atan(1))
    local quickPI = 180 / PI

    -- returns the degrees between two points
    -- note: 0 degrees is 'east'
    local function angleBetweenPoints( a, b )
    local x, y = b.x - a.x, b.y - a.y
    return math.angleOf( { x=x, y=y } )
    end

    -- returns the degrees between (0,0) and pt
    -- note: 0 degrees is 'east'
    -- center: optional
    math.angleOf = function( center, pt )
    if (pt == nil) then
    pt = center
    local angle = math.atan2( pt.y, pt.x ) * quickPI -- 180 / PI -- math.pi
    if angle < 0 then angle = 360 + angle end
    return angle
    else
    return angleBetweenPoints( center, pt )
    end
    end

    -- 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

    --[[
    Calculates the dot product of two lines. The lines are provided as {a,b}
    Each end point of the line objects are of the format {x,y}
    Params:
    a - first line, format: {a,b}
    b - second line, format: {a,b}
    Example:
    print(dotProduct(
    {a={x=0,y=0},b={x=101,y=5}},
    {a={x=0,y=0},b={x=51,y=10}}
    ))
    Ref:
    http://www.mathsisfun.com/algebra/vector-calculator.html
    ]]--
    function dotProduct( a, b )
    -- get dimensions
    local ax, ay = a.b.x-a.a.x, a.b.y-a.a.y
    local bx, by = b.b.x-b.a.x, b.b.y-b.a.y

    -- multiply the x's, multiply the y's, then add
    local dot = ax * bx + ay * by
    return dot
    end

    --[[
    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
    ]]--
    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

    -- 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
    ]]--
    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

    -- Returns the angle in degrees between the first and second points, measured at the centre
    -- Always a positive value
    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

    -- Returns true if the point is within the angle at centre measured between first and second
    function isPointInAngle( centre, first, second, point )
    local range = angleAt( centre, first, second )
    local a = angleAt( centre, first, point )
    local b = angleAt( centre, second, point )
    -- print(range,a+b)
    return math.round(range) >= math.round(a + b)
    end

    --[[
    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
    ]]--
    function Normalise(vector)
    local len = lengthOf( {x=0,y=0}, vector )
    vector.x = vector.x / len
    vector.y = vector.y / len
    return len
    end

    --[[
    Calculates the dot product of a pair of vectors.
    Example:
    local dot = dotProduct( {x=10,y=5}, {x=5,y=10} )
    Ref:
    http://rosettacode.org/wiki/Dot_product#Lua
    ]]--
    function dotProduct(a, b)
    --[[local ret = 0
    for i = 1, #a do
    ret = ret + a[i] * b[i]
    end
    return ret]]--
    return a.x*b.x + a.y*b.y + (a.z or 0)*(b.z or 0)
    end

    --[[
    Calculates the dot product of two lines. The lines are provided as {a,b}
    Each end point of the line objects are of the format {x,y}
    This function implements the simple form of the dot product calculation: a ∑ b = ax ◊ bx + ay ◊ by
    Params:
    a - first line, format: {a,b}
    b - second line, format: {a,b}
    Example:
    print(dotProductByDimensions(
    {a={x=0,y=0},b={x=101,y=5}},
    {a={x=0,y=0},b={x=51,y=10}}
    ))
    Ref:
    http://www.mathsisfun.com/algebra/vectors-dot-product.html
    http://www.mathsisfun.com/algebra/vector-calculator.html
    ]]--
    function dotProductByDimensions( a, b )
    -- get dimensions
    local ax, ay = a.b.x-a.a.x, a.b.y-a.a.y
    local bx, by = b.b.x-b.a.x, b.b.y-b.a.y

    -- multiply the x's, multiply the y's, then add
    local dot = ax * bx + ay * by
    return dot
    end

    --[[
    Calculates the dot product of two vectors. The vectors are provided as {{x,y},{x,y}}
    Each end point of the line objects are of the format {x,y}
    This function implements the COS form of the dot product calculation: a ∑ b = |a| ◊ |b| ◊ cos(?)
    Params:
    a - first vector, format: {x,y}
    b - second vector, format: {x,y}
    Example:
    print(dotProductByCos(
    {a={x=10,y=5},b={x=5,y=10}}
    ))
    Ref:
    http://www.mathsisfun.com/algebra/vectors-dot-product.html
    http://www.mathsisfun.com/algebra/vector-calculator.html
    ]]--
    function dotProductByCos( a, b )
    -- define centre point
    local centre = {x=0,y=0}

    -- get angle at intersection
    local angle = angleAt( centre, a, b )

    -- get vectors (lengths from 0,0)
    local lena = lengthOf( centre, a )
    local lenb = lengthOf( centre, b )

    -- multiply the x's, multiply the y's, then add
    local dot = lena * lenb * math.cos( angle )
    return dot
    end

    --[[
    Description:
    Calculates the cross product of a vector.
    Ref:
    http://www.math.ntnu.no/~stacey/documents/Codea/Library/Vec3.lua
    ]]--
    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

    --[[
    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
    ]]--
    function b2CrossVectVect( a, b )
    return a.x * b.y - a.y * b.x;
    end

    --[[
    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
    ]]--
    function b2CrossVectFloat( a, s )
    return { x = s * a.y, y = -s * a.x }
    end

    --[[
    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
    ]]--
    function b2CrossFloatVect( s, a )
    return { x = -s * a.y, y = s * a.x }
    end

    -- 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.
    math.fractionOf = function( a, b )
    return b / a
    end

    -- 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.
    math.percentageOf = function( a, b )
    return fractionOf(a, b) * 100
    end

    --[[
    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
    ]]--
    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

    --[[
    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
    ]]--
    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

    -- returns true when the point is on the right of the line formed by the north/south points
    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

    -- reflect point across line from north to south
    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

    -- 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/
    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

    -- returns the closest point on the line between A and B from point P
    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

    -- calculates the area of a polygon
    -- will not calculate area for intersecting polygons (where vertices cross each other)
    -- can accept a table of {x,y} or a display group
    -- ref: http://www.mathopenref.com/coordpolygonarea2.html
    function polygonArea( points )
    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

    -- Returns true if the dot { x,y } is within the polygon defined by points table { {x,y},{x,y},{x,y},... }
    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

    -- Return true if the dot { x,y } is within any of the polygons in the list
    function pointInPolygons( polygons, dot )
    for i=1, #polygons do
    if (pointInPolygon( polygons[i], dot )) then
    return true
    end
    end
    return false
    end

    -- Returns true if the points in the polygon wind clockwise
    -- Does not consider that the vertices may intersect (lines between points might cross over)
    function isPolyClockwise( pointList )
    local area = 0

    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

    -- returns a display group with the pixels of the polygon filled in
    -- values in the points table need to be {x,y} tables
    -- polygon does not need to be anti/clockwise winding
    -- param points: the polygon points
    -- param closed: true to cut the polygon out of the screen, false to fill it in
    -- param perPixel: true to fill each pixel individually
    -- param width, height: width and height of each pixel or height of each row
    -- param col: table containing {r,g,b} to set the polygon colour and override the default random colours
    function polygonFill( points, closed, perPixel, width, height, col )
    local fill = display.newGroup()
    local nodes, nodeX, pixelX, pixelY, i, j, swap = 0, {}, 0, 0, 0, 0, 0
    local IMAGE_BOT, IMAGE_TOP, IMAGE_LEFT, IMAGE_RIGHT = 0, display.contentHeight, display.contentWidth, 0
    local closestPoint, shortestDist, closestIndex = {x=0,y=0}, 5000, 0

    -- if the polygon is closed then encase it in a rect the size of the screen
    -- the starting point needs to be closest to the closestPoint
    if (closed) then
    for i=1, #points do
    if (closed) then
    local len = lengthOf( points[i], closestPoint )
    if (len < shortestDist) then
    closestIndex = i
    shortestDist = len
    end
    end
    end
    -- insert bounding box
    table.insert(points,closestIndex+0,{x=points[closestIndex].x,y=points[closestIndex].y})
    table.insert(points,closestIndex+1,closestPoint)
    table.insert(points,closestIndex+2,{x=display.contentWidth,y=0})
    table.insert(points,closestIndex+3,{x=display.contentWidth,y=display.contentHeight})
    table.insert(points,closestIndex+4,{x=0,y=display.contentHeight})
    table.insert(points,closestIndex+5,closestPoint)
    end

    -- find highest, lowest, left-most and right-most points on screen (and their indices)
    for i=1, #points do
    if (points[i].y > IMAGE_BOT) then IMAGE_BOT = points[i].y end
    if (points[i].y < IMAGE_TOP) then IMAGE_TOP = points[i].y end
    if (points[i].x > IMAGE_RIGHT) then IMAGE_RIGHT = points[i].x end
    if (points[i].x < IMAGE_LEFT) then IMAGE_LEFT = points[i].x end
    end

    -- Loop through the rows of the image.
    for pixelY=IMAGE_TOP, IMAGE_BOT, height do
    -- Build a list of nodes.
    nodes = 1
    j=#points

    for i=1, #points do
    if (points[i].y<pixelY and points[j].y>=pixelY or points[j].y<pixelY and points[i].y>=pixelY) then
    nodeX[nodes] = (points[i].x+(pixelY-points[i].y)/(points[j].y-points[i].y)*(points[j].x-points[i].x))
    nodes = nodes + 1
    end
    j = i
    end

    -- Sort the nodes, via a simple ìBubble sort.
    i = 1
    while (i < nodes-1) do
    if (nodeX[i]>nodeX[i+1]) then
    swap=nodeX[i]
    nodeX[i]=nodeX[i+1]
    nodeX[i+1]=swap

    if (i > 1) then
    i = i - 1
    end
    else
    i = i + 1
    end
    end

    -- Fill the pixels between node pairs.
    for i=1, nodes-1, 2 do
    if (nodeX[i ] >= IMAGE_RIGHT) then
    break
    end
    if (nodeX[i+1] > IMAGE_LEFT) then
    if (nodeX[i ]< IMAGE_LEFT ) then nodeX[i ] = IMAGE_LEFT end
    if (nodeX[i+1]> IMAGE_RIGHT) then nodeX[i+1] = IMAGE_RIGHT end

    if (perPixel or false) then
    for j=nodeX[i], nodeX[i+1], width do
    local rect = display.newRect( fill, 0, 0, width, height )
    rect.x, rect.y = j, pixelY
    if (col) then
    rect:setFillColor( col.r, col.g, col.b )
    else
    rect:setFillColor( math.random( 100,255 ), math.random( 100,255 ), math.random( 100,255 ) )
    end
    end
    else
    local rwidth = nodeX[i+1]-nodeX[i]
    local rect = display.newRect( fill, 0, 0, rwidth, height )
    rect.x, rect.y = nodeX[i]+rwidth/2, pixelY
    if (col) then
    rect:setFillColor( col.r, col.g, col.b )
    else
    rect:setFillColor( math.random( 100,255 ), math.random( 100,255 ), math.random( 100,255 ) )
    end
    end
    end
    end
    end

    -- return the filled polygon
    return fill
    end

    -- return a value clamped between a range
    math.clamp = function( val, low, high )
    if (val < low) then return low end
    if (val > high) then return high end
    return val
    end

    -- Forces to apply based on total force and desired angle
    -- http://developer.anscamobile.com/code/virtual-dpadjoystick-template
    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