diff --git a/Changes.txt b/Changes.txt index 0b27b9d..78897e1 100644 --- a/Changes.txt +++ b/Changes.txt @@ -1,4 +1,39 @@ -MLib changelog- accurate since 0.3.0.0 (previously known as 3.0.0), short description from before then. +MLib changelog- accurate since 3.0.0 (now known as 0.3.0.0), short description from before then. + +1.0.0.2 +==== +Added: +---- + +Removed: +---- +- Ability to use a direction for Math.GetAngle's 5th argument instead of having a third point. See Fixed for more. + +Changed: +---- +- Changed README.md for clarity and consistency. +- Updated spec.lua +- See Fixed for more. + +Fixed: +---- +- Circle.IsSegmentSecant now properly accounts for chords actually being chords, and not secants. +- Circle.CircleIntersects now can return 'Colinear' or 'Equal' if the circles have same x and y but different radii (Colinear) or are exactly the same (Equal). +- Statistics.GetMode now returns a table with the modes, and the second argument as the number of times they appear. +- Math.GetRoot now returns the negative number as a second argument. +- Math.GetPercentOfChange now works for 0 to 0 (previously false). +- Math.GetAngle now takes only three points and no direction option. +- Typos in Shape.CheckCollisions and Shape.Remove. +- Fixed nil problems in Shape.CheckCollisions. +- Improved readablility and DRYness of Shape.CheckCollisions. +- Bugs in Shape.Remove and Shape.CheckCollisions regarding passing tables as arguments. + +TODO: +- Add: + - Frequency + - Binomial Probability + - Standard Deviation + - Conditional Probability 1.0.0.1 ==== diff --git a/README.md b/README.md index 62a3d5a..b54dd24 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ git clone git://github.com/davisdude/mlib.git ```` ###Download -The __latest release__ can be found here, and previous releases here. +The __latest release__ can be found here, and previous releases here. ##Usage of MLib Download the file called `mlib.lua` and put it somewhere in the file for the process you want it in. Use the *require* function to import the module into the library. @@ -123,7 +123,7 @@ Deals with linear aspects, such as slope. Also handles line segments. - The distance between the two points. - Example: - Gets the length from ( 1, 1 ) to ( 2, 1 ), which is 1. - - `length = MLib.Line.GetLength( 1, 1, 2, 1 )` + - `Length = MLib.Line.GetLength( 1, 1, 2, 1 )` #####MLib.Line.GetMidpoint - Gets the midpoint of a two points. @@ -194,7 +194,7 @@ Deals with linear aspects, such as slope. Also handles line segments. - Returns: - The perpendicular slope and the midpoint of the line. - Example: - - Gets the perpendicular biisector of ( 1, 1 ) and ( 3, 3 ), which will be -1, 2, 2. + - Gets the perpendicular biisector of ( 1, 1 ) and ( 3, 3 ), which will be 2, 2, -1. - `x, y, PerpendicularSlope = MLib.Line.GetPerpendicularBisector( 1, 1, 3, 3 )` #####MLib.Line.GetIntercept @@ -227,6 +227,7 @@ Deals with linear aspects, such as slope. Also handles line segments. - Synopsis: - `MLib.Line.GetIntersection( Slope, Intercept, x1, y1, x2, y2 )` - `MLib.Line.GetIntersection( Slope1, Intercept1, Slope2, Intercept2 )` + - `MLib.Line.GetIntersection( x1, y1, x2, y2, x3, y3, x4, y4 )` - Arguments: - `MLib.Line.GetIntersection( Slope, Intercept, x1, y1, x2, y2 )` - `Slope`: Number. The slope of the first line. @@ -240,6 +241,15 @@ Deals with linear aspects, such as slope. Also handles line segments. - `Intercept1`: Number. The y-intercept of the first line. - `Slope2`: Number. The slope of the second line. - `Intercept2`: Number. The y-intercept of the second line. + - `MLib.Line.GetIntersection( x1, y1, x2, y2, x3, y3, x4, y4 )` + - `x1`: Number. The one x-coordinate of the first line. + - `y1`: Number. The one y-coordinate of the first line. + - `x2`: Number. The the other x-coordinate of the second line. + - `y2`: Number. The the other y-coordinate of the second line. + - `x3`: Number. The one x-coordinate of the third line. + - `y3`: Number. The one y-coordinate of the third line. + - `x4`: Number. The the other x-coordinate of the fourth line. + - `y4`: Number. The the other y-coordinate of the fourth line. - Returns: - Where the lines intersect. - __IMPORTANT__: If the lines are parallel, it will return false. @@ -381,15 +391,15 @@ Handles polygon-related functions. - `Base`: Number. Length of the base of the triangle. - `Area`: Number. Area of the triangle. - Returns: - - The height of the triangle. + - The height and area of the triangle. - Example: - - Gives the height of a triangle at ( 0, 0 ), ( 0, 4 ) and ( 3, 0 ) and a base of 3. The height is 4. - - `Height = MLib.Polygon.GetTriangleHeight( 3, 0, 0, 0, 4, 3, 0 )` - - Gives the height of a triangle with a base of 3 and a and area of 6. The height is 4. - - `Height = MLib.Polygon.GetTriangleHeight( 3, 6 ) + - Gives the height of a triangle at ( 0, 0 ), ( 0, 4 ) and ( 3, 0 ) and a base of 3. The height is 4, area is 6. + - `Height, Area = MLib.Polygon.GetTriangleHeight( 3, 0, 0, 0, 4, 3, 0 )` + - Gives the height of a triangle with a base of 3 and an area of 6. The height is 4. + - `Height, Area = MLib.Polygon.GetTriangleHeight( 3, 6 )` #####MLib.Polygon.GetSignedArea -- Gives the area of any simple polygon. +- Gives the signed area of any simple polygon. - Synopsis: - `MLib.Polygon.GetArea( Verticies )` - Arguments: @@ -434,10 +444,10 @@ Handles polygon-related functions. - The centroid x and y of the polygon. - Example: - Gives the centroid of the polygon with points at ( 0, 0 ), ( 0, 6 ), ( 3, 0 ), which is at ( 1, 2 ) - - Vverticies = { 0, 0, 0, 6, 3, 0 }` + - `Verticies = { 0, 0, 0, 6, 3, 0 }` - `CentroidX, CentroidY = MLib.Polygon.GetCentroid( Verticies )` - Gives centroid of a polygon with points at ( 0, 0 ), ( 0, 4 ), ( 4, 4 ), ( 4, 0 ) - - `CentroidX, a = MLib.Polygon.GetCentroid( 0, 0, 0, 4, 4, 4, 4, 0 )` + - `CentroidX, CentroidY = MLib.Polygon.GetCentroid( 0, 0, 0, 4, 4, 4, 4, 0 )` #####MLib.Polygon.CheckPoint - Checks whether or not a point is inside a simple polygon. @@ -469,11 +479,11 @@ Handles polygon-related functions. - `y2`: Number. Another y-coordinate on the line. - `...`: Table. The points of the polygon. - Returns: - - `true` if the line intersects the polygon. + - `Collisions`: Table. The spots where the line intersects the polygon if the line intersects the polygon. - `false` if the line does not intersect the polygon. - Example: - - Check if a line with the points ( 3, 7 ) and ( 5, 4 ) intersects with the polygon. It does. - - `LineIntersects = MLib.Polygon.LineIntersects( 3, 7, 5, 4, 3, 5, 4, 4, 5, 5, 6, 4, 6, 6, 3, 6 )` + - Check if a line with the points ( 3, 7 ) and ( 5, 4 ) intersects with the polygon. It does, at ( 3.8, 5.8 ), ( 4.6, 4.6 ). + - `Collision = MLib.Polygon.LineIntersects( 3, 7, 5, 4, 3, 5, 4, 4, 5, 5, 6, 4, 6, 6, 3, 6 )` - Checks if a line with the points ( 2, 5 ) and ( 3, 7 ) intersects with the polygon. It does not. - `LineIntersects = MLib.Polygon.LineIntersects( 2, 5, 3, 7, 3, 5, 4, 4, 5, 5, 6, 4, 6, 6, 3, 6 )` @@ -485,13 +495,13 @@ Handles polygon-related functions. - `Polygon1`: Table. A table containing all of the points of `polygon1` in the form of `( x1, y1, x2, y2, ... )`. - `Polygon2`: Table. A table containing all of the points of `polygon2` in the form of `( x1, y1, x2, y2, ... )`. - Returns: - - `true` if the polygons intersect. + - `Collisions`: Table. The spots where the polygon intersects the other polygon if the polygon intersects the other polygon. - `false` if the polygons don't intersect. - Example: - Checks if polygons with points ( 2, 6, ), ( 3, 8 ), ( 4, 6 ) and ( 4, 7 ), ( 3, 9 ), )( 5, 9 ) intersect. They don't. - - `PolygonIntersecs = MLib.Polygon.PolygonIntersects( { 2, 6, 3, 8, 4, 6 }, { 4, 7, 3, 9, 5, 9 } )` - - Checks if polygons with points ( 2, 6 ), ( 3, 8 ), ( 4, 6 ) and ( 3, 7 ), ( 2, 9 ), ( 4, 9 ) intersect. They do. - - `PolygonIntersects = MLib.Polygon.PolygonIntersects( { 2, 6, 3, 8, 4, 6 }, { 3, 7, 2, 9, 4, 9 } )` + - `PolygonIntersects = MLib.Polygon.PolygonIntersects( { 2, 6, 3, 8, 4, 6 }, { 4, 7, 3, 9, 5, 9 } )` + - Checks if polygons with points ( 2, 6 ), ( 3, 8 ), ( 4, 6 ) and ( 3, 7 ), ( 2, 9 ), ( 4, 9 ) intersect. They do, at, ( 2.75, 7.5 ), ( 3.25, 7.5 ). + - `Collisions = MLib.Polygon.PolygonIntersects( { 2, 6, 3, 8, 4, 6 }, { 3, 7, 2, 9, 4, 9 } )` #####MLib.Polygon.CircleIntersects - Checks whether or not a circle intersects a polygon. @@ -504,17 +514,17 @@ Handles polygon-related functions. - `CircleY`: Number. The y location of the circle center. - `Radius`: Number. The radius of the circle. - `Points`: Table. A table containing all of the points of the polygon in the form of `( x1, y1, x2, y2, ... )` - - `MLib.Polygon.CircleIntersects( CircleX, CircleY, r, ... )` + - `MLib.Polygon.CircleIntersects( CircleX, CircleY, Radius, ... )` - `CircleX`: Number. The x location of the circle center. - `CircleY`: Number. The y location of the circle center. - `Radius`: Number. The radius of the circle. - `...`: Numbers. All of the points of the polygon in the form of `( x1, y1, x2, y2, ... )` - Returns: - - `true` if the circle and polygon intersects. + - `Collisions`: Table. The spots where the circle intersects the polygon if the circle intersects the polygon. - `false` if the circle and polygon don't intersect. - Example: - - Checks if a circle with a radius of 2 located on ( 3, 5 ) intersects with a polygon with points on ( 2, 6 ), ( 4, 6 ), ( 3, 8 ). It does. - - `CircleIntersects = MLib.Polygon.CircleIntersects( 3, 5, 2, 2, 6, 4, 6, 3, 8 )` + - Checks if a circle with a radius of 2 located on ( 3, 5 ) intersects with a polygon with points on ( 3, 1 ), ( 3, 6 ), ( 7, 4 ). It does, at ( 3, 3 ) and ( 5, 5 ). + - `Collisions = MLib.Polygon.CircleIntersects( 3, 5, 2, 3, 1, 3, 6, 7, 4 )` - Checks if a circle with a radius of 2 located on ( 3, 3 ) intersects with a polygon with points on ( 2, 6 ), ( 4, 6 ), ( 3, 8 ). It does not. - `CircleIntersects = MLib.Polygon.CircleIntersects( 3, 3, 2, 2, 6, 4, 6, 3, 8 )` @@ -533,7 +543,7 @@ Handles functions dealing with circles. - `Area = MLib.Circle.GetArea( 1 )` #####MLib.Circle.CheckPoint -- Checks whether or not a point is within a circle. +- Checks whether or not a point is on the outer-edge of a circle. - Synopsis: - `MLib.Circle.CheckPoint( CircleX, CircleY, Radius, x, y )` - Arguments: @@ -582,7 +592,7 @@ Handles functions dealing with circles. - `Slope`: Number. The slope of the line. - `Intercept`: Number. The y-intercept of the line. - Returns: - - `type`: + - `Type`: - String: - `'Secant'` if the line is a secant to the circle. - `'Tangent'` if the line is tangentaltangent @@ -594,29 +604,34 @@ Handles functions dealing with circles. - `y2`: Number. The second y-coordinate of where the intersection occurs. - Example: - A line with points on ( 0, 9 ), ( 6, 9 ), and a circle center on ( 4, 9 ) and a radius of 1. - - `type, x1, y1, x2, y2 = MLib.Circle.IsLineSecant( 4, 9, 1, 0, 9, 6, 9 )` - - Output: `'Secant', 3, 9, 5, 9.` + - `Type, x1, y1, x2, y2 = MLib.Circle.IsLineSecant( 4, 9, 1, 0, 9, 6, 9 ) } + - Output: `'Secant', 3, 9, 5, 9` - A line with a slope of 0 and a y-intercept of 9 and a a circle center on ( 4, 9 ) and a radius of 1. - - `type, x1, y1, x2, y2 = MLib.Circle.IsLineSecant( 4, 9, 1, 0, 9 )` + - `Type, x1, y1, x2, y2 = MLib.Circle.IsLineSecant( 4, 9, 1, 0, 9 )` - Output: `'Secant', 3, 9, 5, 9. ` + - A line with points on ( 0, 8 ), ( 6, 8 ), and a circle center on ( 4, 9 ) and a radius of 1. + - `Type, x1, y1, x2, y2 = MLib.Circle.IsLineSecant( 4, 9, 1, 0, 8, 6, 8 ) + - Output: `'Tangent', 4, 8` #####MLib.Circle.IsSegmentSecant - Checks whether the line is a secant, a tangent or neither. - Synopsis: - - `MLib.Circle.IsSegmentSecant( cx, cy, r, x1, y1, x2, y2 )` + - `MLib.Circle.IsSegmentSecant( CircleX, CircleY, Radius, x1, y1, x2, y2 )` - Arguments: - - `cx`: Number. The x-coordinate of the center of the circle. - - `cy`: Number. The y-coordinate of the center of the circle. - - `r`: Number. The radius of the circle. + - `CircleX`: Number. The x-coordinate of the center of the circle. + - `CircleY`: Number. The y-coordinate of the center of the circle. + - `Radius`: Number. The radius of the circle. - `x1`: Number. The first x-coordinate of the line. - `y1`: Number. The first y-coordinate of the line. - `x2`: Number. The second x-coordinate of the line. - `y2`: Number. The second y-coordinate of the line. - Returns: - - `type`: + - `Type`: - String: - `'Secant'` if the line is a secant to the circle. - - `'Tangent'` if the line is tangentaltangent + - `'Tangent'` if the line is tangental tangent + - `'Enclosed'` if the line is completely within the circle. + - `'Chord'` if the line is exactly on the circle's circumference. - Boolean: - `false` if the line is neither a secant not a tangent. - `x1`: Number. The first x-coordinate of where the intersection occurs. @@ -625,21 +640,24 @@ Handles functions dealing with circles. - `y2`: Number. The second y-coordinate of where the intersection occurs. - Example: - A line segment with points on ( 0, 9 ), ( 6, 9 ), and a circle center on ( 4, 9 ) and a radius of 1. - - `type, x1, y1, x2, y2 = MLib.Circle.IsLineSecant( 4, 9, 1, 0, 9, 6, 9 )` - - Output: `'Secant', 3, 9, 5, 9.` + - `Type, x1, y1, x2, y2 = MLib.Circle.IsLineSecant( 4, 9, 1, 0, 9, 6, 9 )` + - Output: `'Secant', 3, 9, 5, 9` #####MLib.Circle.CircleIntersects - Returns the point that intersects the circles. - Synopsis: - - `MLib.Circle.CircleIntersects( cx1, cy1, r1, cx2, cy2, r2 )` + - `MLib.Circle.CircleIntersects( CircleX1, CircleY1, Radius1, CircleX2, CircleY2, Radius2 )` - Arguments: - - `cx1`: Number. The x-coordinate of the first center circle. - - `cy1`: Number. The y-coordinate of the first center circle. - - `r1`: Number. The radius of the first circle. - - `cx2`: Number. The x-coordinate of the second center circle. - - `cy2`: Number. The y-coordinate of the second center circle. - - `r2`: Number. The radius of the first circle. -- Returns: + - `CircleX1`: Number. The x-coordinate of the first center circle. + - `CircleY1`: Number. The y-coordinate of the first center circle. + - `Radius1`: Number. The radius of the first circle. + - `CircleX2`: Number. The x-coordinate of the second center circle. + - `CircleY2`: Number. The y-coordinate of the second center circle. + - `Radius2`: Number. The radius of the first circle. +- Returns: + - String: + - `'Equal'` if the circles have the same x and y and radius. + - `'Colinear'` if the circles have the same x and y, but different radii. - The points where the circles intersect. - `false` if they don't intersect. - Example: @@ -678,7 +696,7 @@ Handles functions dealing with statisticsistics. - The average of the numbers. - Example: - Gets the average of a data set containing the numbers 1, 2, 3, 4, and 5, which is 3. - - `mean = MLib.Statistics.GetMean( 1, 2, 3, 4, 5 )` + - `Mean = MLib.Statistics.GetMean( 1, 2, 3, 4, 5 )` #####MLib.Statistics.GetMedian - Gets the median of the data set. @@ -691,7 +709,7 @@ Handles functions dealing with statisticsistics. - The median of the numbers. - Example: - Gets the median of a data set containing the numbers, 1, 2, 3, 4, and 5, which is 3. - - `median = .Statistics.GetMedian( 1, 2, 3, 4, 5 )` + - `Median = MLib.Statistics.GetMedian( 1, 2, 3, 4, 5 )` #####MLib.Statistics.GetMode - Gets the mode of the data set. @@ -701,12 +719,13 @@ Handles functions dealing with statisticsistics. - Table. Contains the numbers data. - Numbers. Can just be the numbers, or the `unpack`ed table containing the numbers. - Returns: - - The mode of the numbers and how many times it appears. - - `false` if bimodial. - - `false` if no mode. + - If there is one mode or more: + - `Numbers`: Table. A table in the form `{ Number1, Number2, etc. }` + - `Occurrences`: Number. The number of times the number(s) appeared. + - `false` if there is not a mode. - Example: - Gets the median of a data set containing the numbers, 1, 5, 6, 22, 2, 2, 1, 2, which is 2, which appears 3 times. - - `mode, times = MLib.Statistics.GetMode( 1, 5, 6, 22, 2, 2, 1, 2 )` + - `Modes, Times = MLib.Statistics.GetMode( 1, 5, 6, 22, 2, 2, 1, 2 )` #####MLib.Statistics.GetRange - Gets the range of the data set. @@ -719,7 +738,7 @@ Handles functions dealing with statisticsistics. - The range of the numbers. - Example: - Gets the range of a data set containing the numbers, 1, 2, 3, and 4, which is 3. - - `range = MLib.Statistics.GetRange( 1, 2, 3, 4 )` + - `Range = MLib.Statistics.GetRange( 1, 2, 3, 4 )` ####MLib.Math Handles functions that have to do with math in general. @@ -731,23 +750,23 @@ Handles functions that have to do with math in general. - `number`: Number. The number you are getting the nth root of. - `root`: Number. The number that is n for the nth root. - Returns: - - The number. + - The roots of the number (positive, negative). - Example: - Gets the square (2) root of 4, which is 2. - - `root = MLib.Math.GetRoot( 4, 2 ) ` + - `Root = MLib.Math.GetRoot( 4, 2 ) ` #####MLib.Math.IsPrime - Checks whether a number or set of numbers is prime or not. - Synopsis: - - `MLib.Math.IsPrime( number )` + - `MLib.Math.IsPrime( Number )` - Arguments: - - `number`: Number. The number that is being checked for prime-ness. + - `Number`: Number. The number that is being checked for prime-ness. - Returns: - `true` if the number is prime. - `false` if the number is not prime. - Example: - Checks if the number 3 is prime or not. 3 is prime. - - `prime = MLib.Math.IsPrime( 3 )` + - `Prime = MLib.Math.IsPrime( 3 )` #####MLib.Math.Round - Rounds a number up or down, depending on which it's closer to. @@ -764,29 +783,29 @@ Handles functions that have to do with math in general. #####MLib.Math.GetSummation - Gives the summation. - Synopsis: - - `MLib.Math.GetSummation( start, stop, func )` + - `MLib.Math.GetSummation( Start, Stop, Function )` - Arguments: - - `start`: Number. Where should the summation begin? - - `stop`: Number. Where shuld the summation end? - - `func`: Function. A function that gives the new value to add to the previous. Can have two arguments: - - `i`: The first argument. Represents the current number - - `t`: The second argument. Has all the previous values in it. - - __IMPORTANT__: If you use `t`, your function must have an `if` statisticsement, regarding what to do if there is not a value for that number yet. + - `Start`: Number. Where should the summation begin? + - `Stop`: Number. Where shuld the summation end? + - `Function`: Function. A Functiontion that gives the new value to add to the previous. Can have two arguments: + - `Index`: The first argument. Represents the current number + - `Previous`: The second argument. Has all the previous values in it. + - __IMPORTANT__: If you use `Previous`, your function must have an `if` statement, regarding what to do if there is not a value for that number yet. - Returns: - The sum of all of the values. - Example: - - Gives the sum of numbers that start at 1 and end at 10, multuplying each time by 2, which is 110. - - `sum = MLib.Math.GetSummation( 1, 10, function( i ) return ( i * 2 ) end )` + - Gives the sum of numbers that start at 1 and end at 10, multiplying each time by 2, which is 110. + - `Sum = MLib.Math.GetSummation( 1, 10, function( Index ) return ( Index * 2 ) end )` - Gives the sum of numbers that start at 1 and end at 5, adding the previous value each time, which is 35. - - `sum = MLib.Math.GetSummation( 1, 5, function( i, t ) if t[i-1] then return i + t[i-1] else return 1 end end )` + - `Sum = MLib.Math.GetSummation( 1, 5, function( Index, Previous ) if Previous[Index - 1] then return Index + Previous[Index - 1] else return 1 end end )` #####MLib.Math.GetPercentOfChange - Gives the percent of change from two numbers. - Synopsis: - - `MLib.Math.GetPercentOfChange( old, new )` + - `MLib.Math.GetPercentOfChange( Old, New )` - Arguments: - - `old`: Number. The previous number. - - `new`: Number. The new number. + - `Old`: Number. The previous number. + - `New`: Number. The new number. - Returns: - The percentage difference from `old` to `new`. - Example: @@ -796,14 +815,14 @@ Handles functions that have to do with math in general. #####MLib.Math.GetPercent - Gets the percentage of a number. - Synopsis: - - `MLib.Math.GetPercent( percent, num )` + - `MLib.Math.GetPercent( Percent, Number )` - Arguments: - - `percent`: Number. The percentage you are getting of `num`. - - `num`: Number. The number that is getting changed. + - `Percent`: Number. The percentage you are getting of `Number`. + - `Number`: Number. The number that is getting changed. - Returns: - - `percentage`% of `num`. + - `Percentage`% of `Number`. - Example: - - 100% of 2 is 4. + - 100% of 2 is 2. - `MLib.Math.GetPercent( 1, 2 )` #####MLib.Math.GetRootsOfQuadratic @@ -815,7 +834,8 @@ Handles functions that have to do with math in general. - `b`: Number. The number that has `x` next to it. - `c`: Number. The number that has no variable next to it. - Returns: - - The value of x. + - `false` if the given quadratic has no solution. + - The value of x, otherwise. - Example: - Gets the x of the quadratic equation `1 * x ^ 2 + 3 * x - 4`, which is -4, 1. - `x1, x2 = MLib.Math.GetRootsOfQuadratic( 1, 3, -4 )` @@ -823,15 +843,8 @@ Handles functions that have to do with math in general. #####MLib.Math.GetAngle - Gets the angle between two points. - Synopsis: - - `MLib.Math.GetAngle( x1, y1, x2, y2, dir )` - `MLib.Math.GetAngle( x1, y1, x2, y2, x3, y3 )` - Arguments: - - `MLib.Math.GetAngle( x1, y1, x2, y2, dir )` - - `x1`: Number. The x-coordinate of the primary point. - - `y1`: Number. The y-coordinate of the primary point. - - `x2`: Number. The x-coordinate of the secondary point. - - `y2`: Number. The y-coordinate of the secondary point. - - `dir`: String. Can be `up`, `down`, `left` or `right`. Used for the orientation of the picture. If not applicable, leave blank or make it `up`. - `MLib.Math.GetAngle( x1, y1, x2, y2, x3, y3 )` - `x1`: Number. The x-coordinate of the first point. - `y1`: Number. The y-coordinate of the first point. @@ -840,9 +853,9 @@ Handles functions that have to do with math in general. - `x3`: Number. The x-coordinate of the third point. - `y3`: Number. The y-coordinate of the third point. - Returns: - - The angle from point 1 to point 2 (and possibly point 3) in radians. + - The angle between point ( 1, 3 ) and ( 3, 1 ) with a vertex of ( 1, 1 ). This is 90 degrees or about 1.57079633 radians. - Example: - - `angle = MLib.Math.GetAngle( 0, 0, 3, 3, 'Up' ), 2.35619449 )` + - `Angle = MLib.Math.GetAngle( 1, 3, 1, 1, 3, 1 ) ###MLib.Shape Handles shape collision/intersection. @@ -918,13 +931,14 @@ Handles shape collision/intersection. - `Shape:Remove( Shapes )` - Arguments: - `MLib.Shape.Remove()` + - Remove all shapes. - `MLib.Shape.Remove( Shapes )` - - `shapes`: Table. A table containing all of the shapes you want to remove. + - `Shapes`: Table. A table containing all of the shapes you want to remove. - `Shape:Remove()` - - `shape`: Table. The table returned from MLib.Shape.NewShape. + - `Shape`: Table. The table returned from MLib.Shape.NewShape. - `Shape:Remove( Shapes )` - - `shape`: Table. The table returned from MLib.Shape.NewShape. - - `shapes`: Table. A table containing all of the shapes you want to remove. + - `Shape`: Table. The table returned from MLib.Shape.NewShape. + - `Shapes`: Table. A table containing all of the shapes you want to remove. ##MLib.Shape Example Here is an example of how to use MLib.Shape. @@ -977,4 +991,4 @@ function love.update( dt ) print( Rectangle.collided, Line.collided, Circle.collided ) --> true, true, false end -```` +```` \ No newline at end of file diff --git a/mlib.lua b/mlib.lua index 166646c..2019a52 100644 --- a/mlib.lua +++ b/mlib.lua @@ -52,6 +52,29 @@ local function SortWithReference( Table, Function ) return Value, Key end +local function CheckFuzzy( Number1, Number2 ) + return ( Number1 - .00001 <= Number2 and Number2 <= Number1 + .00001 ) +end + +local function RemoveDuplicates( Table ) + for Index1 = #Table, 1, -1 do + local First = Table[Index1] + for Index2 = #Table, 1, -1 do + local Second = Table[Index2] + if Index1 ~= Index2 then + if type( First[1] ) == 'number' and type( Second[1] ) == 'number' and type( First[2] ) == 'number' and type( Second[2] ) == 'number' then + if CheckFuzzy( First[1], Second[1] ) and CheckFuzzy( First[2], Second[2] ) then + table.remove( Table, Index1 ) + end + elseif First[1] == Second[1] and First[2] == Second[2] then + table.remove( Table, Index1 ) + end + end + end + end + return Table +end + -- Lines function MLib.Line.GetLength( x1, y1, x2, y2 ) return math.sqrt( ( x1 - x2 ) ^ 2 + ( y1 - y2 ) ^ 2 ) @@ -68,6 +91,7 @@ end function MLib.Line.GetPerpendicularSlope( ... ) local Userdata = CheckUserdata( ... ) + local Slope if #Userdata ~= 1 then Slope = MLib.Line.GetSlope( unpack( Userdata ) ) @@ -82,7 +106,8 @@ end function MLib.Line.GetPerpendicularBisector( x1, y1, x2, y2 ) local Slope = MLib.Line.GetSlope( x1, y1, x2, y2 ) - return MLib.Line.GetMidpoint( x1, y1, x2, y2 ), MLib.Line.GetPerpendicularSlope( Slope ) + local MidpointX, MidpointY = MLib.Line.GetMidpoint( x1, y1, x2, y2 ) + return MidpointX, MidpointY, MLib.Line.GetPerpendicularSlope( Slope ) end function MLib.Line.GetIntercept( x, y, ... ) @@ -135,7 +160,7 @@ function MLib.Line.GetIntersection( ... ) return x, y end -function MLib.Line.GetClosestPoint( px, py, ... ) +function MLib.Line.GetClosestPoint( PerpendicularX, PerpendicularY, ... ) local Userdata = CheckUserdata( ... ) local x1, y1, x2, y2, Slope, Intercept local x, y @@ -152,8 +177,8 @@ function MLib.Line.GetClosestPoint( px, py, ... ) elseif Slope == 0 then x, y = PerpendicularX, y1 else - PerpendicularSlope = MLib.Line.GetPerpendicularSlope( Slope ) - PerpendicularIntercept = MLib.Line.GetIntercept( PerpendicularX, PerpendicularY, PerpendicularSlope ) + local PerpendicularSlope = MLib.Line.GetPerpendicularSlope( Slope ) + local PerpendicularIntercept = MLib.Line.GetIntercept( PerpendicularX, PerpendicularY, PerpendicularSlope ) x, y = MLib.Line.GetIntersection( Slope, Intercept, PerpendicularSlope, PerpendicularIntercept ) end @@ -217,29 +242,30 @@ function MLib.Line.Segment.GetIntersection( x1, y1, x2, y2, x3, y3, x4, y4 ) if Intercept1 == Intercept2 then local x = { x1, x2, x3, x4 } local y = { y1, y2, y3, y4 } + local OriginalX = { x1, x2, x3, x4 } local OriginalY = { y1, y2, y3, y4 } local Length1, Length2 = MLib.Line.GetLength( x[1], y[1], x[2], y[2] ), MLib.Line.GetLength( x[3], y[3], x[4], y[4] ) local LargestX, LargestXReference = SortWithReference( x, function ( Value1, Value2 ) return Value1 > Value2 end ) + table.remove( x, LargestXReference ) local LargestY, LargestYReference = SortWithReference( y, function ( Value1, Value2 ) return Value1 > Value2 end ) + table.remove( y, LargestYReference ) local SmallestX, SmallestXReference = SortWithReference( x, function ( Value1, Value2 ) return Value1 < Value2 end ) - local SmallestY, SmallestYReference = SortWithReference( y, function ( Value1, Value2 ) return Value1 < Value2 end ) - - table.remove( x, LargestXReference ) table.remove( x, SmallestXReference ) - table.remove( y, LargestYReference ) + local SmallestY, SmallestYReference = SortWithReference( y, function ( Value1, Value2 ) return Value1 < Value2 end ) table.remove( y, SmallestYReference ) local Distance = MLib.Line.GetLength( x[1], y[1], x[2], y[2] ) if Distance > Length1 or Distance > Length2 then return false end - - local Length1 = MLib.Line.GetLength( x[1], OriginalY[1], x[1], OriginalY[2] ) - local Length2 = MLib.Line.GetLength( x[1], OriginalY[3], x[1], OriginalY[4] ) - local Length3 = MLib.Line.GetLength( x[1], y[1], x[2], y[2] ) + + local Length3 = MLib.Line.GetLength( OriginalX[LargestXReference], OriginalY[LargestXReference], OriginalX[SmallestXReference], OriginalY[SmallestXReference] ) if Length3 >= Length1 or Length3 >= Length2 then return false end - return x[1], y[1], x[2], y[2] + + local _, Index = SortWithReference( x, function ( Value1, Value2 ) return Value1 > Value2 end ) + if Index == 1 then return x[1], y[1], x[2], y[2] + else return x[2], y[2], x[1], y[1] end else return false end @@ -324,14 +350,13 @@ function MLib.Line.Segment.GetIntersection( x1, y1, x2, y2, x3, y3, x4, y4 ) end -- Polygon -function MLib.Polygon.GetTriangleHeight( base, ... ) +function MLib.Polygon.GetTriangleHeight( Base, ... ) local Userdata = CheckUserdata( ... ) - local Area = 0 - local Intercept = 0 + local Area if #Userdata == 1 then Area = Userdata[1] else Area = MLib.Polygon.GetArea( Userdata ) end - return ( 2 * Area ) / base, Area + return ( 2 * Area ) / Base, Area end function MLib.Polygon.GetSignedArea( ... ) @@ -339,7 +364,7 @@ function MLib.Polygon.GetSignedArea( ... ) local Points = {} for Index = 1, #Userdata, 2 do - Points[#Points + 1] = { Userdata[a], Userdata[Index + 1] } + Points[#Points + 1] = { Userdata[Index], Userdata[Index + 1] } end Points[#Points + 1] = {} @@ -370,7 +395,7 @@ function MLib.Polygon.GetCentroid( ... ) Points[#Points + 1] = {} Points[#Points][1], Points[#Points][2] = Points[1][1], Points[1][2] - local Area = GetSignedArea( Userdata ) -- Needs to be signed here in case points are counter-clockwise. + local Area = MLib.Polygon.GetSignedArea( Userdata ) -- Needs to be signed here in case points are counter-clockwise. local CentroidX = ( 1 / ( 6 * Area ) ) * ( MLib.Math.GetSummation( 1, #Points, function( Index ) @@ -462,24 +487,21 @@ end function MLib.Polygon.LineIntersects( x1, y1, x2, y2, ... ) local Userdata = CheckUserdata( ... ) local Choices = {} - - if MLib.Polygon.CheckPoint( x1, y1, Userdata ) then Choices[#Choices + 1] = { x1, y1 } end - if MLib.Polygon.CheckPoint( x2, y2, Userdata ) then Choices[#Choices + 1] = { x2, y2 } end for Index = 1, #Userdata, 2 do -- if MLib.Line.Segment.CheckPoint( x1, y1, x2, y2, Userdata[Index], Userdata[Index + 1] ) then return true end if Userdata[Index + 2] then - local x1, y1, x2, y2 = MLib.Line.Segment.GetIntersection( Userdata[Index], Userdata[Index + 1], Userdata[Index + 2], Userdata[Index + 3], x1, y1, x2, y2 ) - if x2 then Choices[#Choices + 1] = { x1, y1, x2, y2 } - elseif x1 then Choices[#Choices + 1] = { x1, y1 } end + local x, y = MLib.Line.Segment.GetIntersection( Userdata[Index], Userdata[Index + 1], Userdata[Index + 2], Userdata[Index + 3], x1, y1, x2, y2 ) + if x then Choices[#Choices + 1] = { x, y } end else - local x1, y1, x2, y2 = MLib.Line.Segment.GetIntersection( Userdata[Index], Userdata[Index + 1], Userdata[1], Userdata[2], x1, y1, x2, y2 ) - if x2 then Choices[#Choices + 1] = { x1, y1, x2, y2 } - elseif x1 then Choices[#Choices + 1] = { x1, y1 } end + local x, y = MLib.Line.Segment.GetIntersection( Userdata[Index], Userdata[Index + 1], Userdata[1], Userdata[2], x1, y1, x2, y2 ) + if x then Choices[#Choices + 1] = { x, y } end end end + + local Final = RemoveDuplicates( Choices ) - return #Choices > 0 and Choices or false + return #Final > 0 and Final or false end function MLib.Polygon.PolygonIntersects( Polygon1, Polygon2 ) @@ -521,21 +543,6 @@ function MLib.Polygon.PolygonIntersects( Polygon1, Polygon2 ) end end - function RemoveDuplicates( Table ) -- Local because it is very custom-coded. - for Index1 = #Table, 1, -1 do - local First = Table[Index1] - for Index2 = #Table, 1, -1 do - local Second = Table[Index2] - if Index1 ~= Index2 then - if First[1] == Second[1] and First[2] == Second[2] then - table.remove( Table, Index1 ) - end - end - end - end - return Table - end - local Final = RemoveDuplicates( Choices ) return #Final > 0 and Final or false @@ -545,25 +552,43 @@ function MLib.Polygon.CircleIntersects( x, y, Radius, ... ) local Userdata = CheckUserdata( ... ) local Choices = {} - if MLib.Polygon.CheckPoint( x, y, Userdata ) then Choices[#Choices + 1] = { x, y } end - for Index = 1, #Userdata, 2 do if Userdata[Index + 2] then - local x1, y1, x2, y2 = MLib.Circle.IsSegmentSecant( x, y, Radius, Userdata[Index], Userdata[Index + 1], Userdata[Index + 2], Userdata[Index + 3] ) + local Type, x1, y1, x2, y2 = MLib.Circle.IsSegmentSecant( x, y, Radius, Userdata[Index], Userdata[Index + 1], Userdata[Index + 2], Userdata[Index + 3] ) if x2 then - Choices[#Choices + 1] = { x1, y1 } - Choices[#Choices + 1] = { x2, y2 } - elseif x1 then Choices[#Choices + 1] = { x1, y1 } end + Choices[#Choices + 1] = { Type, x1, y1, x2, y2 } + elseif x1 then Choices[#Choices + 1] = { Type, x1, y1 } end else - local x1, y1, x2, y2 = MLib.Circle.IsSegmentSecant( x, y, Radius, Userdata[Index], Userdata[Index + 1], Userdata[1], Userdata[2] ) + local Type, x1, y1, x2, y2 = MLib.Circle.IsSegmentSecant( x, y, Radius, Userdata[Index], Userdata[Index + 1], Userdata[1], Userdata[2] ) if x2 then - Choices[#Choices + 1] = { x1, y1 } - Choices[#Choices + 1] = { x2, y2 } - elseif x1 then Choices[#Choices + 1] = { x1, y1 } end + Choices[#Choices + 1] = { Type, x1, y1, x2, y2 } + elseif x1 then Choices[#Choices + 1] = { Type, x1, y1 } end + end + end + + local function RemoveDuplicates( Table ) + for Index1 = #Table, 1, -1 do + local First = Table[Index1] + for Index2 = #Table, 1, -1 do + local Second = Table[Index2] + if Index1 ~= Index2 then + if type( First[1] ) ~= type( Second[1] ) then return false end + if type( First[2] ) == 'number' and type( Second[2] ) == 'number' and type( First[3] ) == 'number' and type( Second[3] ) == 'number' then + if CheckFuzzy( First[2], Second[2] ) and CheckFuzzy( First[3], Second[3] ) then + table.remove( Table, Index1 ) + end + elseif First[1] == Second[1] and First[2] == Second[2] and First[3] == Second[3] then + table.remove( Table, Index1 ) + end + end + end end + return Table end - return #Choices > 0 and Choices or false + local Final = RemoveDuplicates( Choices ) + + return #Final > 0 and Final or false end -- Circle @@ -636,35 +661,30 @@ function MLib.Circle.IsSegmentSecant( CircleX, CircleY, Radius, x1, y1, x2, y2 ) local Slope, Intercept = MLib.Line.GetSlope( x1, y1, x2, y2 ), MLib.Line.GetIntercept( x1, y1, x2, y2 ) + if MLib.Circle.CheckPoint( CircleX, CircleY, Radius, x1, y1 ) and MLib.Circle.CheckPoint( CircleX, CircleY, Radius, x2, y2 ) then -- Both points are on line-segment. + return 'Chord', x1, y1, x2, y2 + end + if Slope then if MLib.Circle.IsPointInCircle( CircleX, CircleY, Radius, x1, y1 ) and MLib.Circle.IsPointInCircle( CircleX, CircleY, Radius, x2, y2 ) then -- Line-segment is fully in circle. - return x1, y1, x2, y2 + return 'Enclosed', x1, y1, x2, y2 elseif x3 and x4 then - if MLib.Line.Segment.CheckPoint( x1, y1, x2, y2, x3, y3 ) and MLib.Line.Segment.CheckPoint( x1, y1, x2, y2, x4, y4 ) then -- Both points are on line-segment. - return x3, y3, x4, y4 - elseif MLib.Line.Segment.CheckPoint( x1, y1, x2, y2, x3, y3 ) then -- Only the first of the points is on the line-segment. - return x3, y3 - elseif MLib.Line.Segment.CheckPoint( x1, y1, x2, y2, x4, y4 ) then -- Only the second of the points is on the line-segment. - return x4, y4 - else -- Neither of the points are on the line-segment (means that the segment is not on the circle or "encasing" the circle) + if MLib.Line.Segment.CheckPoint( x1, y1, x2, y2, x3, y3 ) and not MLib.Line.Segment.CheckPoint( x1, y1, x2, y2, x4, y4 ) then -- Only the first of the points is on the line-segment. + return 'Tangent', x3, y3 + elseif MLib.Line.Segment.CheckPoint( x1, y1, x2, y2, x4, y4 ) and not MLib.Line.Segment.CheckPoint( x1, y1, x2, y2, x3, y3 ) then -- Only the second of the points is on the line-segment. + return 'Tangent', x4, y4 + else -- Neither of the points are on the circle (means that the segment is not on the circle, but "encasing" the circle) local Length = MLib.Line.GetLength( x1, y1, x2, y2 ) - local Distance1 = MLib.Line.GetLength( x1, y1, x3, y3 ) - local Distance2 = MLib.Line.GetLength( x2, y2, x3, y3 ) - local Distance3 = MLib.Line.GetLength( x1, y1, x4, y4 ) - local Distance4 = MLib.Line.GetLength( x2, y3, x4, y4 ) - - if Length > Distance1 or Length > Distance2 or Length > Distance3 or Length > Distance4 then - return false - elseif Length < Distance1 and Length < Distance2 and Length < Distance3 and Length < Distance4 then - return false + if MLib.Line.Segment.CheckPoint( x1, y1, x2, y2, x3, y3 ) and MLib.Line.Segment.CheckPoint( x1, y1, x2, y2, x4, y4 ) then + return 'Secant', x3, y3, x4, y4 else - return true + return false end end elseif not x4 then -- Is a tangent. if MLib.Line.Segment.CheckPoint( x1, y1, x2, y2, x3, y3 ) then - return x3, y3 + return 'Tangent', x3, y3 else -- Neither of the points are on the line-segment (means that the segment is not on the circle or "encasing" the circle). local Length = MLib.Line.GetLength( x1, y1, x2, y2 ) local Distance1 = MLib.Line.GetLength( x1, y1, x3, y3 ) @@ -675,7 +695,7 @@ function MLib.Circle.IsSegmentSecant( CircleX, CircleY, Radius, x1, y1, x2, y2 ) elseif Length < Distance1 and Length < Distance2 then return false else - return true + return 'Tangent', x3, y3 end end end @@ -696,17 +716,17 @@ function MLib.Circle.IsSegmentSecant( CircleX, CircleY, Radius, x1, y1, x2, y2 ) if BottomY ~= TopY then if MLib.Line.Segment.CheckPoint( x1, y1, x2, y2, TopX, TopY ) and MLib.Line.Segment.CheckPoint( x1, y1, x2, y2, BottomX, BottomY ) then - return TopX, TopY, BottomX, BottomY + return 'Chord', TopX, TopY, BottomX, BottomY elseif MLib.Line.Segment.CheckPoint( x1, y1, x2, y2, TopX, TopY ) then - return TopX, TopX + return 'Tangent', TopX, TopX elseif MLib.Line.Segment.CheckPoint( x1, y1, x2, y2, BottomX, BottomY ) then - return BottomX, BottomY + return 'Tangent', BottomX, BottomY else return false end else if MLib.Line.Segment.CheckPoint( x1, y1, x2, y2, TopX, TopY ) then - return TopX, TopY + return 'Tangent', TopX, TopY else return false end @@ -717,11 +737,13 @@ end function MLib.Circle.CircleIntersects( Circle1CenterX, Circle1CenterY, Radius1, Circle2CenterX, Circle2CenterY, Radius2 ) local Distance = MLib.Line.GetLength( Circle1CenterX, Circle1CenterY, Circle2CenterX, Circle2CenterY ) if Distance > Radius1 + Radius2 then return false end - if Distance == 0 and Radius1 == Radius2 then return true end + if Distance == 0 and Radius1 == Radius2 then return 'Equal' end local a = ( Radius1 ^ 2 - Radius2 ^ 2 + Distance ^ 2 ) / ( 2 * Distance ) local h = math.sqrt( Radius1 ^ 2 - a ^ 2 ) + if Circle1CenterX == Circle2CenterX and Circle1CenterY == Circle2CenterY then return 'Colinear' end + local p2x = Circle1CenterX + a * ( Circle2CenterX - Circle1CenterX ) / Distance local p2y = Circle1CenterY + a * ( Circle2CenterY - Circle1CenterY ) / Distance local p3x = p2x + h * ( Circle2CenterY - Circle1CenterY ) / Distance @@ -768,38 +790,27 @@ function MLib.Statistics.GetMode( ... ) local Userdata = CheckUserdata( ... ) table.sort( Userdata ) - local Number = { { Userdata[1] } } - for Index = 2, #Userdata do - if Userdata[Index] == Number[#Number][1] then - table.insert( Number[#Number], Userdata[Index] ) - else - table.insert( Number, { Userdata[Index] } ) - end + local Sorted = {} + for Index, Value in ipairs( Userdata ) do + Sorted[Value] = Sorted[Value] and Sorted[Value] + 1 or 1 end - local Large = { { #Number[1], Number[1][1] } } - for Index = 2, #Number do - if #Number[Index] > Large[1][1] then - for NextIndex = #Large, 1, -1 do - table.remove( Large, NextIndex ) - end - table.insert( Large, { #Number[Index], Number[Index][1] } ) - elseif #Number[Index] == Large[1][1] then - table.insert( Large, { #Number[Index], Number[Index][1] } ) + local Occurrences, Least = 0, {} + for Index, Value in pairs( Sorted ) do + if Value > Occurrences then + Least = { Index } + Occurrences = Value + elseif Value == Occurrences then + Least[#Least + 1] = Index end end - if #Large < 1 then - return false - elseif #Large > 1 then - return false - else - return Large[1][2], Large[1][1] - end + if #Least >= 1 then return Least, Occurrences + else return false end end function MLib.Statistics.GetRange( ... ) - local Userdata = {} + local Userdata = CheckUserdata( ... ) local Upper, Lower = math.max( unpack( Userdata ) ), math.min( unpack( Userdata ) ) @@ -808,7 +819,8 @@ end -- Math (homeless functions) function MLib.Math.GetRoot( Number, Root ) - return Number ^ ( 1 / Root ) + local Num = Number ^ ( 1 / Root ) + return Num, -Num end function MLib.Math.IsPrime( Number ) @@ -858,7 +870,9 @@ function MLib.Math.GetSummation( Start, Stop, Function ) end function MLib.Math.GetPercentOfChange( Old, New ) - if Old == 0 then + if Old == 0 and New == 0 then + return 0 + elseif Old == 0 then return false else return ( New - Old ) / math.abs( Old ) @@ -866,7 +880,7 @@ function MLib.Math.GetPercentOfChange( Old, New ) end function MLib.Math.GetPercent( Percent, Number ) - return Percent * math.abs( Number ) + Number + return math.abs( Percent ) * Number end function MLib.Math.GetRootsOfQuadratic( a, b, c ) @@ -878,32 +892,12 @@ function MLib.Math.GetRootsOfQuadratic( a, b, c ) return ( -b - Discriminant ) / ( 2 * a ), ( -b + Discriminant ) / ( 2 * a ) end -function MLib.Math.GetAngle( ... ) - local Userdata = CheckUserdata( ... ) - local Angle = 0 - - if #Userdata <= 5 then - local x1, y1, x2, y2, Direction = unpack( Userdata ) - - if not Direction or Direction == 'Up' then Direction = math.rad( 90 ) - elseif Direction == 'Right' then Direction = 0 - elseif Direction == 'Down' then Direction = math.rad( -90 ) - elseif Direction == 'Left' then Direction = math.rad( -180 ) - end - - local dx, dy = x2 - x1, y2 - y1 - Angle = math.atan2( dy, dx ) + Direction - elseif #Userdata == 6 then - local x1, y1, x2, y2, x3, y3 = unpack( Userdata ) - - local AB = MLib.Line.GetLength( x1, y1, x2, y2 ) - local BC = MLib.Line.GetLength( x2, y2, x3, y3 ) - local AC = MLib.Line.GetLength( x1, y1, x3, y3 ) - - Angle = math.acos( ( BC * BC + AB * AB - AC * AC ) / ( 2 * BC * AB ) ) - end - - return Angle +function MLib.Math.GetAngle( x1, y1, x2, y2, x3, y3 ) + local A = MLib.Line.GetLength( x3, y3, x2, y2 ) + local B = MLib.Line.GetLength( x1, y1, x2, y2 ) + local C = MLib.Line.GetLength( x1, y1, x3, y3 ) + + return math.acos( ( A ^ 2 + B ^ 2 - C ^ 2 ) / ( 2 * A * B ) ) end -- Shape @@ -912,17 +906,17 @@ function MLib.Shape.NewShape( ... ) if #Userdata == 3 then Userdata.Type = 'Circle' - Userdata.x, Userdata.y, Userdata.radius = unpack( Userdata ) - Userdata.Area = MLib.Circle.GetArea( Userdata.radius ) + Userdata.x, Userdata.y, Userdata.Radius = unpack( Userdata ) + Userdata.Area = MLib.Circle.GetArea( Userdata.Radius ) elseif #Userdata == 4 then Userdata.Type = 'Line' Userdata.x1, Userdata.y1, Userdata.x2, Userdata.y2 = unpack( Userdata ) - Userdata.MLib.Line.GetSlope = MLib.Line.GetSlope( unpack( Userdata ) ) - Userdata.MLib.Line.GetIntercept = MLib.Line.GetIntercept( unpack( Userdata ) ) + Userdata.Slope = MLib.Line.GetSlope( unpack( Userdata ) ) + Userdata.Intercept = MLib.Line.GetIntercept( unpack( Userdata ) ) else + Userdata.Points = Userdata Userdata.Type = 'Polygon' Userdata.Area = MLib.Polygon.GetArea( Userdata ) - Userdata.Points = Userdata end Userdata.Collided = false @@ -938,74 +932,58 @@ end function MLib.Shape.CheckCollisions( Self, ... ) local Userdata = { ... } - if Type( Self ) == 'table' then -- Using Index Self:table. - if #Userdata == 0 then -- No arguments (colliding with everything). + local function Check( Self, Shape ) + local Collided = false + if not Shape.Removed and not Self.Removed then + if Self.Type == 'Line' then + if Shape.Type == 'Line' then + if MLib.Line.Segment.GetIntersection( Self.x1, Self.y1, Self.x2, Self.y2, Shape.x1, Shape.y1, Shape.x2, Shape.y2 ) then Collided, Self.Collided, Shape.Collided = true, true, true end + elseif Shape.Type == 'Polygon' then + if MLib.Polygon.LineIntersects( Self.x1, Self.y1, Self.x2, Self.y2, Shape.Points ) then Collided, Self.Collided, Shape.Collided = true, true, true end + elseif Shape.Type == 'Circle' then + if MLib.Circle.IsSegmentSecant( Shape.x, Shape.y, Shape.Radius, Self.x1, Self.y1, Self.x2, Self.y2 ) then Collided, Self.Collided, Shape.Collided = true, true, true end + end + elseif Self.Type == 'Polygon' then + if Shape.Type == 'Line' then + if MLib.Polygon.LineIntersects( Shape.x1, Shape.y1, Shape.x2, Shape.y2, Self.Points ) then Collided, Self.Collided, Shape.Collided = true, true, true end + elseif Shape.Type == 'Polygon' then + if MLib.Polygon.PolygonIntersects( Self.Points, Shape.Points ) then Collided, Self.Collided, Shape.Collided = true, true, true end + elseif Shape.Type == 'Circle' then + if MLib.Polygon.CircleIntersects( Shape.x, Shape.y, Shape.Radius, Self.Points ) then Collided, Self.Collided, Shape.Collided = true, true, true end + end + elseif Self.Type == 'Circle' then + if Shape.Type == 'Line' then + if MLib.Circle.IsSegmentSecant( Self.x, Self.y, Self.Radius, Shape.x1, Shape.y1, Shape.x2, Shape.y2 ) then Collided, Self.Collided, Shape.Collided = true, true, true end + elseif Shape.Type == 'Polygon' then + if MLib.Polygon.CircleIntersects( Self.x, Self.y, Self.Radius, Shape.Points ) then Collided, Self.Collided, Shape.Collided = true, true, true end + elseif Shape.Type == 'Circle' then + if MLib.Circle.CircleIntersects( Self.x, Self.y, Self.Radius, Shape.x, Shape.y, Shape.Radius ) then Collided, Self.Collided, Shape.Collided = true, true, true end + end + end + end + if not Collided then Self.Collided = false end + end + + if type( Self ) == 'table' then -- Using Index Self:table. + if #Userdata == 0 and Self.Type then -- No arguments (colliding with everything). for Index = 1, #MLib.Shape.User do if Index ~= Self.Index then - local Collided = false local Shape = MLib.Shape.User[Index] - if not Shape.Removed and not Self.Removed then - if Self.Type == 'Line' then - if Shape.Type == 'Line' then - if MLib.Line.Segment.GetIntersection( Self.x1, Self.y1, Self.x2, Self.y2, Shape.x1, Shape.y1, Shape.x2, Shape.y2 ) then Collided, Self.Collided, Shape.Collided = true, true end - elseif Shape.Type == 'Polygon' then - if MLib.Polygon.LineIntersects( Self.x1, Self.y1, Self.x2, Self.y2, Shape.Points ) then Collided, Self.Collided, Shape.Collided = true, true end - elseif Shape.Type == 'Circle' then - if MLib.Circle.IsSegmentSecant( Shape.x, Shape.y, Shape.radius, Self.x1, Self.y1, Self.x2, Self.y2 ) then Collided, Self.Collided, Shape.Collided = true, true end - end - elseif Self.Type == 'Polygon' then - if Shape.Type == 'Line' then - if MLib.Polygon.LineIntersects( Shape.x1, Shape.y1, Shape.x2, Shape.y2, Self.Points ) then Collided, Self.Collided, Shape.Collided = true, true end - elseif Shape.Type == 'Polygon' then - if MLib.Polygon.PolygonIntersects( Self.Points, Shape.Points ) then Collided, Self.Collided, Shape.Collided = true, true end - elseif Shape.Type == 'Circle' then - if MLib.Polygon.CircleIntersects( Shape.x, Shape.y, Shape.radius, Self.Points ) then Collided, Self.Collided, Shape.Collided = true, true end - end - elseif Self.Type == 'Circle' then - if Shape.Type == 'Line' then - if MLib.Circle.IsSegmentSecant( Self.x, Self.y, Self.radius, Shape.x1, Shape.y1, Shape.x2, Shape.y2 ) then Collided, Self.Collided, Shape.Collided = true, true end - elseif Shape.Type == 'Polygon' then - if MLib.Polygon.CircleIntersects( Self.x, Self.y, Self.radius, Shape.Points ) then Collided, Self.Collided, Shape.Collided = true, true end - elseif Shape.Type == 'Circle' then - if MLib.Circle.CircleIntersects( Self.x, Self.y, Self.radius, Shape.x, Shape.y, Shape.radius ) then Collided, Self.Collided, Shape.Collided = true, true end - end - end + Check( Self, Shape ) + end + end + elseif not Self.Type then -- Multi-item table. + for Index1, Primary in ipairs( Self ) do + for Index2, Secondary in ipairs( Self ) do + if Index1 ~= Index2 then + Check( Primary, Secondary ) end - if not Collided then Self.Collided = false end end end else -- Colliding with only certain things. for Index = 1, #Userdata do - local Collided = false local Shape = Userdata[Index] - if not Shape.Removed and not Self.Removed then - if Self.Type == 'Line' then - if Shape.Type == 'Line' then - if MLib.Line.Segment.GetIntersection( Self.x1, Self.y1, Self.x2, Self.y2, Shape.x1, Shape.y1, Shape.x2, Shape.y2 ) then Collided, Self.Collided, Shape.Collided = true, true end - elseif Shape.Type == 'Polygon' then - if MLib.Polygon.LineIntersects( Self.x1, Self.y1, Self.x2, Self.y2, Shape.Points ) then Collided, Self.Collided, Shape.Collided = true, true end - elseif Shape.Type == 'Circle' then - if MLib.Circle.IsSegmentSecant( Shape.x, Shape.y, Shape.radius, Self.x1, Self.y1, Self.x2, Self.y2 ) then Collided, Self.Collided, Shape.Collided = true, true end - end - elseif Self.Type == 'Polygon' then - if Shape.Type == 'Line' then - if MLib.Polygon.LineIntersects( Shape.x1, Shape.y1, Shape.x2, Shape.y2, Self.Points ) then Collided, Self.Collided, Shape.Collided = true, true end - elseif Shape.Type == 'Polygon' then - if MLib.Polygon.PolygonIntersects( Self.Points, Shape.Points ) then Collided, Self.Collided, Shape.Collided = true, true end - elseif Shape.Type == 'Circle' then - if MLib.Polygon.CircleIntersects( Shape.x, Shape.y, Shape.radius, Self.Points ) then Collided, Self.Collided, Shape.Collided = true, true end - end - elseif Self.Type == 'Circle' then - if Shape.Type == 'Line' then - if MLib.Circle.IsSegmentSecant( Self.x, Self.y, Self.radius, Shape.x1, Shape.y1, Shape.x2, Shape.y2 ) then Collided, Self.Collided, Shape.Collided = true, true end - elseif Shape.Type == 'Polygon' then - if MLib.Polygon.CircleIntersects( Self.x, Self.y, Self.radius, Shape.Points ) then Collided, Self.Collided, Shape.Collided = true, true end - elseif Shape.Type == 'Circle' then - if MLib.Circle.CircleIntersects( Self.x, Self.y, Self.radius, Shape.x, Shape.y, Shape.radius ) then Collided, Self.Collided, Shape.Collided = true, true end - end - end - end - if not Collided then Self.Collided = false end + Check( Self, Shape ) end end else -- Not using Self:table. @@ -1017,36 +995,9 @@ function MLib.Shape.CheckCollisions( Self, ... ) for Index2 = 1, #MLib.Shape.User do if Index ~= Index2 then local Shape = MLib.Shape.User[Index2] - if not Shape.Removed and not Self.Removed then - if Self.Type == 'Line' then - if Shape.Type == 'Line' then - if MLib.Line.Segment.GetIntersection( Self.x1, Self.y1, Self.x2, Self.y2, Shape.x1, Shape.y1, Shape.x2, Shape.y2 ) then Collided, Self.Collided, Shape.Collided = true, true, true end - elseif Shape.Type == 'Polygon' then - if MLib.Polygon.LineIntersects( Self.x1, Self.y1, Self.x2, Self.y2, Shape.Points ) then Collided, Self.Collided, Shape.Collided = true, true, true end - elseif Shape.Type == 'Circle' then - if MLib.Circle.IsSegmentSecant( Shape.x, Shape.y, Shape.radius, Self.x1, Self.y1, Self.x2, Self.y2 ) then Collided, Self.Collided, Shape.Collided = true, true, true end - end - elseif Self.Type == 'Polygon' then - if Shape.Type == 'Line' then - if MLib.Polygon.LineIntersects( Shape.x1, Shape.y1, Shape.x2, Shape.y2, Self.Points ) then Collided, Self.Collided, Shape.Collided = true, true, true end - elseif Shape.Type == 'Polygon' then - if MLib.Polygon.PolygonIntersects( Self.Points, Shape.Points ) then Collided, Self.Collided, Shape.Collided = true, true, true end - elseif Shape.Type == 'Circle' then - if MLib.Polygon.CircleIntersects( Shape.x, Shape.y, Shape.radius, Self.Points ) then Collided, Self.Collided, Shape.Collided = true, true, true end - end - elseif Self.Type == 'Circle' then - if Shape.Type == 'Line' then - if MLib.Circle.IsSegmentSecant( Self.x, Self.y, Self.radius, Shape.x1, Shape.y1, Shape.x2, Shape.y2 ) then Collided, Self.Collided, Shape.Collided = true, true, true end - elseif Shape.Type == 'Polygon' then - if MLib.Polygon.CircleIntersects( Self.x, Self.y, Self.radius, Shape.Points ) then Self.Collided, Collided, Self.Collided, Shape.Collided = true, true, true end - elseif Shape.Type == 'Circle' then - if MLib.Circle.CircleIntersects( Self.x, Self.y, Self.radius, Shape.x, Shape.y, Shape.radius ) then Collided, Self.Collided, Shape.Collided = true, true, true end - end - end - end + Check( Self, Shape ) end end - if not Collided then Self.Collided = false end end else -- Checking only certain collisions for Index = 1, #Userdata do @@ -1055,36 +1006,9 @@ function MLib.Shape.CheckCollisions( Self, ... ) for Index2 = 1, #MLib.Shape.User do if Self.Index ~= Userdata[Index2].Index then local Shape = MLib.Shape.User[Index2] - if not Shape.Removed and not Self.Removed then - if Self.Type == 'Line' then - if Shape.Type == 'Line' then - if MLib.Line.Segment.GetIntersection( Self.x1, Self.y1, Self.x2, Self.y2, Shape.x1, Shape.y1, Shape.x2, Shape.y2 ) then Collided, Self.Collided, Shape.Collided = true, true end - elseif Shape.Type == 'Polygon' then - if MLib.Polygon.LineIntersects( Self.x1, Self.y1, Self.x2, Self.y2, Shape.Points ) then Collided, Self.Collided, Shape.Collided = true, true end - elseif Shape.Type == 'Circle' then - if MLib.Circle.IsSegmentSecant( Shape.x, Shape.y, Shape.radius, Self.x1, Self.y1, Self.x2, Self.y2 ) then Collided, Self.Collided, Shape.Collided = true, true end - end - elseif Self.Type == 'Polygon' then - if Shape.Type == 'Line' then - if MLib.Polygon.LineIntersects( Shape.x1, Shape.y1, Shape.x2, Shape.y2, Self.Points ) then Collided, Self.Collided, Shape.Collided = true, true end - elseif Shape.Type == 'Polygon' then - if MLib.Polygon.PolygonIntersects( Self.Points, Shape.Points ) then Collided, Self.Collided, Shape.Collided = true, true end - elseif Shape.Type == 'Circle' then - if MLib.Polygon.CircleIntersects( Shape.x, Shape.y, Shape.radius, Self.Points ) then Collided, Self.Collided, Shape.Collided = true, true end - end - elseif Self.Type == 'Circle' then - if Shape.Type == 'Line' then - if MLib.Circle.IsSegmentSecant( Self.x, Self.y, Self.radius, Shape.x1, Shape.y1, Shape.x2, Shape.y2 ) then Collided, Self.Collided, Shape.Collided = true, true end - elseif Shape.Type == 'Polygon' then - if MLib.Polygon.CircleIntersects( Self.x, Self.y, Self.radius, Shape.Points ) then Collided, Self.Collided, Shape.Collided = true, true end - elseif Shape.Type == 'Circle' then - if MLib.Circle.CircleIntersects( Self.x, Self.y, Self.radius, Shape.x, Shape.y, Shape.radius ) then Collided, Self.Collided, Shape.Collided = true, true end - end - end - end + Check( Self, Shape ) end end - if not Collided then Self.Collided = false end end end end @@ -1093,21 +1017,19 @@ end function MLib.Shape.Remove( Self, ... ) local Userdata = { ... } - if Type( Self ) == 'table' then - MLib.Shape.User[Self.Index] = { Removed = false } + if type( Self ) == 'table' and Self.Type then + Self.Removed = true if #Userdata > 0 then for Index = 1, #Userdata do - MLib.Shape.User[Userdata[Index].Index] = { Removed = true } + MLib.Shape.User[Userdata[Index].Index].Removed = true end end else - if #Userdata > 0 then - for Index = 1, #Userdata do - MLib.Shape.User[Userdata[Index].Index] = { Removed = true } - end - else - MLib.Shape.User = {} + local Table = #Userdata > 0 and Userdata or ( Self or MLib.Shape.User ) + + for Index = 1, #Table do + MLib.Shape.User[Table[Index].Index].Removed = true end end end diff --git a/spec.lua b/spec.lua index 46288fa..e8020ca 100644 --- a/spec.lua +++ b/spec.lua @@ -1,540 +1,733 @@ require 'telescope' local _ = require 'mlib' -context( 'The module', - function() - before( function() end ) - after( function() end ) - - local function check_fuzzy( a, b ) - return ( a - .00001 <= b and b <= a + .00001 ) +context( 'MLib', function() + before( function() end ) + after( function() end ) + + local function check_fuzzy( a, b ) + return ( a - .00001 <= b and b <= a + .00001 ) + end + + local function DeepCompare( Table1, Table2 ) + if type( Table1 ) ~= type( Table2 ) then return false end + + for Key, Value in pairs( Table1 ) do + if ( type( Value ) == 'table' and type( Table2[Key] ) == 'table' ) then + if ( not DeepCompare( Value, Table2[Key] ) ) then return false end + else + if type( Value ) ~= type( Table2[Key] ) then return false end + if type( Value ) == 'number' then + return check_fuzzy( Value, Table2[Key] ) + elseif ( Value ~= Table2[Key] ) then return false end + end end - - make_assertion( 'fuzzy_equal', 'fuzzy values to be equal to each other', - function( a, b ) - return check_fuzzy( a, b ) - end - ) - make_assertion( 'multiple_fuzzy_equal', 'all fuzzy values to equal respective fuzzy value', - function( a, b ) - for i = 1, #a do - if type( a[i] ) ~= 'number' then - if a[i] ~= b[i] then return false end - else - if not check_fuzzy( a[i], b[i] ) then - return false - end + for Key, Value in pairs( Table2 ) do + if ( type( Value ) == 'table' and type( Table1[Key] ) == 'table' ) then + if ( not DeepCompare( Value, Table1[Key] ) ) then return false end + else + if type( Value ) ~= type( Table1[Key] ) then return false end + if type( Value ) == 'number' then + return check_fuzzy( Value, Table1[Key] ) + elseif ( Value ~= Table1[Key] ) then return false end + end + end + return true + end + + make_assertion( 'fuzzy_equal', 'fuzzy values to be equal to each other', + function( a, b ) + return check_fuzzy( a, b ) + end + ) + + make_assertion( 'multiple_fuzzy_equal', 'all fuzzy values to equal respective fuzzy value', + function( a, b ) + for i = 1, #a do + if type( a[i] ) ~= 'number' then + if a[i] ~= b[i] then return false end + else + if not check_fuzzy( a[i], b[i] ) then + return false end end - return true end - ) - - context( 'line.length', function() - test( 'gives the distance between two coordinates.', function() - assert_fuzzy_equal( _.line.length( 0, 0, 3, 0 ), 3 ) - assert_fuzzy_equal( _.line.length( 3, 4, 0, 0 ), 5 ) - assert_fuzzy_equal( _.line.length( 5, 4, 6, 1 ), 3.16227766017 ) - assert_fuzzy_equal( _.line.length( 7.48, 2.5, 6.54, 4.54 ), 2.24615226554 ) - end ) - - test( 'can get the distance between a single point.', function() - assert_fuzzy_equal( _.line.length( 3, 2, 3, 2 ), 0 ) + return true + end + ) + + make_assertion( 'tables_fuzzy_equal', 'all table values are equal', + function( Table1, Table2 ) + return DeepCompare( Table1, Table2 ) + end + ) + + context( 'Line', function() + context( 'GetLength', function() + test( 'Gives the length of a line.', function() + assert_fuzzy_equal( _.Line.GetLength( 1, 1, 1, 2 ), 1 ) + assert_fuzzy_equal( _.Line.GetLength( 0, 0, 1, 0 ), 1 ) + assert_fuzzy_equal( _.Line.GetLength( 4, 4, 7, 8 ), 5 ) + assert_fuzzy_equal( _.Line.GetLength( 9.3, 7.6, -12, .001 ), 22.61492 ) + assert_fuzzy_equal( _.Line.GetLength( 4.2, 4.134, 7.2342, -78 ), 82.190025 ) end ) end ) - context( 'line.midpoint', function() - test( 'gives the midpoint between two coordinates.', function() - assert_multiple_fuzzy_equal( { _.line.midpoint( 0, 0, 3, 0 ) }, { 1.5, 0 } ) - assert_multiple_fuzzy_equal( { _.line.midpoint( 3, 4, 0, 0 ) }, { 1.5, 2 } ) - assert_multiple_fuzzy_equal( { _.line.midpoint( 5, 4, 6, 1 ) }, { 5.5, 2.5 } ) - assert_multiple_fuzzy_equal( { _.line.midpoint( 7.48, 2.5, 6.54, 4.54 ) }, { 7.01, 3.52 } ) - end ) - - test( 'can get the midpoint of a single point.', function() - assert_multiple_fuzzy_equal( { _.line.midpoint( 3, 2, 3, 2 ) }, { 3, 2 } ) + context( 'GetMidpoint', function() + test( 'Gives the midpoint of a line.', function() + assert_multiple_fuzzy_equal( { _.Line.GetMidpoint( 0, 0, 2, 2 ) }, { 1, 1 } ) + assert_multiple_fuzzy_equal( { _.Line.GetMidpoint( 4, 4, 7, 8 ) }, { 5.5, 6 } ) + assert_multiple_fuzzy_equal( { _.Line.GetMidpoint( -1, 2, 3, -6 ) }, { 1, -2 } ) + assert_multiple_fuzzy_equal( { _.Line.GetMidpoint( 6.4, 3, -10.7, 4 ) }, { -2.15, 3.5 } ) + assert_multiple_fuzzy_equal( { _.Line.GetMidpoint( 3.14159, 3.14159, 2.71828, 2.71828 ) }, { 2.92993, 2.92993 } ) end ) end ) - context( 'line.slope', function() - test( 'gives the slope between two points.', function() - assert_fuzzy_equal( _.line.slope( 0, 0, 3, 0 ), 0 ) - assert_fuzzy_equal( _.line.slope( 3, 4, 0, 0 ), 4 / 3 ) - assert_fuzzy_equal( _.line.slope( 5, 4, 6, 1 ), -3 ) - assert_fuzzy_equal( _.line.slope( 7.48, 2.5, 6.54, 4.54 ), -2.17021276596 ) + context( 'GetSlope', function() + test( 'Gives the slope of a line given two points.', function() + assert_fuzzy_equal( _.Line.GetSlope( 1, 1, 2, 2 ), 1 ) + assert_fuzzy_equal( _.Line.GetSlope( 1, 1, 0, 1 ), 0 ) + assert_fuzzy_equal( _.Line.GetSlope( 1, 0, 0, 1 ), -1 ) end ) - test( 'returns false if the line is vertical (x1 == x2).', function() - assert_false( _.line.slope( 3, 2, 3, 4 ) ) + test( 'Returns false if the slope is vertical.', function() + assert_false( _.Line.GetSlope( 1, 0, 1, 5 ) ) + assert_false( _.Line.GetSlope( -4, 9, -4, 13423 ) ) end ) end ) - context( 'line.perpendicularSlope', function() - test( 'gives the perpendicular slope using the slope.', function() - assert_fuzzy_equal( _.line.perpendicularSlope( 4 / 3 ), -.75 ) - assert_fuzzy_equal( _.line.perpendicularSlope( -3 ), 1/3 ) - assert_fuzzy_equal( _.line.perpendicularSlope( -2.17021276596 ), 0.46078431372 ) + context( 'GetPerpendicularSlope', function() + test( 'Gives the perpendicular slope given two points.', function() + assert_fuzzy_equal( _.Line.GetPerpendicularSlope( 1, 1, 2, 2 ), -1 ) end ) - test( 'gives the perpendicular slope using the points.', function() - assert_fuzzy_equal( _.line.perpendicularSlope( 3, 4, 0, 0 ), -.75 ) - assert_fuzzy_equal( _.line.perpendicularSlope( 5, 4, 6, 1 ), 1/3 ) - assert_fuzzy_equal( _.line.perpendicularSlope( 7.48, 2.5, 6.54, 4.54 ), 0.46078431372 ) + test( 'Gives the perpendicular slope given the slope.', function() + assert_fuzzy_equal( _.Line.GetPerpendicularSlope( 2 ), -.5 ) end ) - test( 'gives the perpendicular slope to vertical lines.', function() - assert_fuzzy_equal( _.line.perpendicularSlope( 1 / 0 ), 0 ) - assert_fuzzy_equal( _.line.perpendicularSlope( 3, 2, 3, 4 ), 0 ) + test( 'Gives the perpendicular slope if the initial line is vertical.', function() + assert_fuzzy_equal( _.Line.GetPerpendicularSlope( 1, 0, 1, 5 ), 0 ) + assert_fuzzy_equal( _.Line.GetPerpendicularSlope( false ), 0 ) end ) - test( 'returns false if the perpendicular slope if vertical.', function() - assert_false( _.line.perpendicularSlope( 0 ) ) - assert_false( _.line.perpendicularSlope( 0, 0, 3, 0 ) ) + test( 'Returns false if the initial slope is horizontal.', function() + assert_false( _.Line.GetPerpendicularSlope( 0, 0, 5, 0 ) ) end ) end ) - context( 'line.perpendicularBisector', function() - test( 'gives the perpendicular bisector.', function() - assert_multiple_fuzzy_equal( { _.line.perpendicularBisector( 3, 4, 0, 0 ) }, { -.75, 1.5, 2 } ) - assert_multiple_fuzzy_equal( { _.line.perpendicularBisector( 5, 4, 6, 1 ) }, { 1 / 3, 5.5, 2.5 } ) - assert_multiple_fuzzy_equal( { _.line.perpendicularBisector( 7.48, 2.5, 6.54, 4.54 ) }, { .4607843, 7.01, 3.52 } ) + context( 'GetPerpendicularBisector', function() + test( 'Returns the midpoint and perpendicular slope given two points.', function() + assert_multiple_fuzzy_equal( { _.Line.GetPerpendicularBisector( 1, 1, 3, 3 ) }, { 2, 2, -1 } ) + assert_multiple_fuzzy_equal( { _.Line.GetPerpendicularBisector( 1, 0, 1, 8 ) }, { 1, 4, 0 } ) + assert_multiple_fuzzy_equal( { _.Line.GetPerpendicularBisector( 4, 4, 6, 8 ) }, { 5, 6, -.5 } ) end ) - test( 'gives the perpendicular bisector to vertical lines.', function() - assert_multiple_fuzzy_equal( { _.line.perpendicularBisector( 3, 2, 3, 4 ) }, { 0, 3, 3 } ) - end ) - - test( 'returns false, x, y if the perpendicular bisector is vertical.', function() - assert_multiple_fuzzy_equal( { _.line.perpendicularBisector( 0, 0, 3, 0 ) }, { false, 1.5, 0 } ) + test( 'Returns false and midpoint if original slope is horizontal.', function() + assert_multiple_fuzzy_equal( { _.Line.GetPerpendicularBisector( 0, 0, 6, 0 ) }, { 3, 0, false } ) + assert_multiple_fuzzy_equal( { _.Line.GetPerpendicularBisector( 5, 7, 10, 7 ) }, { 7.5, 7, false } ) end ) end ) - context( 'line.intercept', function() - test( 'gives the y-intercept using the points and slope.', function() - assert_fuzzy_equal( _.line.intercept( 0, 0, 0 ), 0 ) - assert_fuzzy_equal( _.line.intercept( 3, 4, 4 / 3 ), 0 ) - assert_fuzzy_equal( _.line.intercept( 5, 4, -3 ), 19 ) - assert_fuzzy_equal( _.line.intercept( 7.48, 2.5, -2.17021276596 ), 18.7331914893617 ) + context( 'GetIntercept', function() + test( 'Gives the y-intercept given two points.', function() + assert_fuzzy_equal( _.Line.GetIntercept( 0, 0, 1, 1 ), 0 ) + assert_fuzzy_equal( _.Line.GetIntercept( 2, 3, 4, 9 ), -3 ) end ) - test( 'gives the y-intercept using the points.', function() - assert_fuzzy_equal( _.line.intercept( 0, 0, 3, 0 ), 0 ) - assert_fuzzy_equal( _.line.intercept( 3, 4, 0, 0 ), 0 ) - assert_fuzzy_equal( _.line.intercept( 5, 4, 6, 1 ), 19 ) - assert_fuzzy_equal( _.line.intercept( 7.48, 2.5, 6.54, 4.54 ), 18.7331914893617 ) + test( 'Gives the y-intercept given one point and the slope.', function() + assert_fuzzy_equal( _.Line.GetIntercept( 0, 0, 1 ), 0 ) end ) - test( 'returns false for vertical lines.', function() - assert_false( _.line.intercept( 3, 2, 3, 4 ) ) - assert_false( _.line.intercept( 3, 2, false ) ) + test( 'Returns false if the slope is false.', function() + assert_false( _.Line.GetIntercept( 1, 0, 1, 5 ) ) + assert_false( _.Line.GetIntercept( 0, 0, false ) ) end ) end ) - context( 'line.func.get', function() - test( 'gives the exponential function of two points.', function() - assert_multiple_fuzzy_equal( { _.line.func.get( 1, 2, 2, 2 ) }, { 2, 1 } ) - assert_multiple_fuzzy_equal( { _.line.func.get( 2, 10, -1, .5 ) }, { 1.35720199237, 2.71441761659 } ) + context( 'GetIntersection', function() + test( 'Given the slope, y-intercept, and two points of other line.', function() + assert_multiple_fuzzy_equal( { _.Line.GetIntersection( 1, 0, 1, 0, 0, 1 ) }, { .5, .5 } ) end ) - test( 'returns false if one/both ys is/are <= 0.', function() - assert_false( _.line.func.get( 0, 1, 1, -3 ) ) - assert_false( _.line.func.get( 5, -2, 3, 3 ) ) + test( 'Given the slope, y-intercept, the other slope and y-intercept.', function() + assert_multiple_fuzzy_equal( { _.Line.GetIntersection( 1, 0, -1, 1 ) }, { .5, .5 } ) end ) - test( 'returns false if the xs are the same.', function() - assert_false( _.line.func.get( 0, 1, 0, 3 ) ) - end ) - end ) - - context( 'polygon.triangleHeight', function() - test( 'gives the height of a triangle given the base and area.', function() - assert_fuzzy_equal( _.polygon.triangleHeight( 4, 10 ), 5 ) - assert_fuzzy_equal( _.polygon.triangleHeight( 5, 10 ), 4 ) + test( 'Given two points on one line and two on the other.', function() + assert_multiple_fuzzy_equal( { _.Line.GetIntersection( 1, 1, 0, 0, 1, 0, 0, 1 ) }, { .5, .5 } ) end ) - test( 'gives the height of a triangle given the base and points. Returns height and area.', function() - assert_multiple_fuzzy_equal( { _.polygon.triangleHeight( 4, 2, 6, 2, 1, 6, 1 ) }, { 5, 10 } ) - assert_multiple_fuzzy_equal( { _.polygon.triangleHeight( 5, 1, 3, 6, 3, 1, 7 ) }, { 4, 10 } ) + test( 'Works for vertical lines.', function() + assert_multiple_fuzzy_equal( { _.Line.GetIntersection( 1, 0, 1, 5, 2, 2, 0, 2 ) }, { 1, 2 } ) end ) - test( 'can get the height of non-right triangles.', function() - assert_fuzzy_equal( _.polygon.triangleHeight( 6.32, 5 ), 1.58227848101 ) - assert_multiple_fuzzy_equal( { _.polygon.triangleHeight( 6.32, 8, 6, 9, 8, 14, 8 ) }, { 1.58227848101, 5 } ) + test( 'Returns false if the lines are parallel.', function() + assert_false( _.Line.GetIntersection( 2, 4, 2, 7 ) ) end ) end ) - context( 'circle.area', function() - test( 'gives the area of a circle.', function() - assert_fuzzy_equal( _.circle.area( 1 ), 3.14159265359 ) - assert_fuzzy_equal( _.circle.area( 2 ), 12.5663706144 ) - assert_fuzzy_equal( _.circle.area( 10 ), 314.159265359 ) - assert_fuzzy_equal( _.circle.area( 57 ), 10207.0345315 ) + context( 'GetClosestPoint', function() + test( 'Given the point and two points on the line.', function() + assert_multiple_fuzzy_equal( { _.Line.GetClosestPoint( 4, 2, 1, 1, 3, 5 ) }, { 2, 3 } ) + assert_multiple_fuzzy_equal( { _.Line.GetClosestPoint( 3, 5, 3, 0, 2, 2 ) }, { 1, 4 } ) + assert_multiple_fuzzy_equal( { _.Line.GetClosestPoint( -1, 3, -2, 0, 2, 2 ) }, { 0, 1 } ) + end ) + + test( 'Given the the point and the slope and y-intercept.', function() + assert_multiple_fuzzy_equal( { _.Line.GetClosestPoint( 4, 2, 2, -1 ) }, { 2, 3 } ) + assert_multiple_fuzzy_equal( { _.Line.GetClosestPoint( -1, 3, .5, 1 ) }, { 0, 1 } ) end ) end ) - context( 'stats.mean', function() - test( 'gives the mean (average).', function() - assert_fuzzy_equal( _.stats.mean( 1, 2, 3, 4, 5 ), 3 ) - assert_fuzzy_equal( _.stats.mean( 5, 4, 1, -7, 0, 19 ), 3 + 2 / 3 ) - assert_fuzzy_equal( _.stats.mean( math.pi, 12, -8 ), 2.38053088453 ) + context( 'GetSegmentIntersection', function() + test( 'Given the end points of the segment and 2 points on the line.', function() + assert_multiple_fuzzy_equal( { _.Line.GetSegmentIntersection( 3, 6, 5, 8, 3, 8, 5, 6 ) }, { 4, 7 } ) + assert_multiple_fuzzy_equal( { _.Line.GetSegmentIntersection( 0, 0, 4, 4, 0, 4, 4, 0 ) }, { 2, 2 } ) + end ) + + test( 'Given end points of the segmen and the slope and intercept.', function() + assert_multiple_fuzzy_equal( { _.Line.GetSegmentIntersection( 3, 6, 5, 8, -1, 11 ) }, { 4, 7 } ) end ) - test( 'gives the mean with tables.', function() - assert_fuzzy_equal( _.stats.mean( { 7, 7, 7 } ), 7 ) - assert_fuzzy_equal( _.stats.mean{ 7, 1, 5, 3 }, 4 ) + test( 'Returns false if they don\'t intersect.', function() + assert_false( _.Line.GetSegmentIntersection( 0, 0, 1, 1, 0, 4, 4, 0 ) ) + assert_false( _.Line.GetSegmentIntersection( 0, 0, 1, 1, -1, 4 ) ) end ) end ) - context( 'stats.median', function() - test( 'gives the median of the group', function() - assert_fuzzy_equal( _.stats.median( 1, 2, 3, 4, 5 ), 3 ) + context( 'Segment', function() + context( 'CheckPoint', function() + test( 'Returns true if the point is on the segment.', function() + assert_true( _.Line.Segment.CheckPoint( 2, 2, 0, 0, 1, 1 ) ) + assert_true( _.Line.Segment.CheckPoint( 1, 4, 5, 12, 3, 8 ) ) + assert_true( _.Line.Segment.CheckPoint( -1, 4, 0, 0, -.5, 2 ) ) + end ) + + test( 'Returns false if the point is not on the segment.', function() + assert_false( _.Line.Segment.CheckPoint( 2, 2, 0, 0, 3, 1 ) ) + assert_false( _.Line.Segment.CheckPoint( 1, 4, 5, 12, 3, 9 ) ) + end ) end ) - test( 'gives the median of the table', function() - assert_fuzzy_equal( _.stats.median{ 4, 5, 9, 4, 1, 7, 13 }, 5 ) + context( 'GetIntersection', function() + test( 'Returns the point of intersection if they do.', function() + assert_multiple_fuzzy_equal( { _.Line.Segment.GetIntersection( 1, 1, 5, 3, 2, 3, 4, 1 ) }, { 3, 2, nil, nil } ) + assert_multiple_fuzzy_equal( { _.Line.Segment.GetIntersection( 0, 0, 3, 3, 0, 1, 3, 1 ) }, { 1, 1, nil, nil } ) + end ) + + test( 'Returns false if they don\'t.', function() + assert_multiple_fuzzy_equal( { _.Line.Segment.GetIntersection( 3, 7, 6, 8, 1, 6, 5, 4 ) }, { false, nil, nil, nil } ) + end ) + + test( 'Return x1, y1, x2, y2 if lines have same slope and intercept.', function() + assert_multiple_fuzzy_equal( { _.Line.Segment.GetIntersection( 0, 0, 2, 2, 1, 1, 3, 3 ) }, { 1, 1, 2, 2 } ) + assert_multiple_fuzzy_equal( { _.Line.Segment.GetIntersection( 0, 1, 4, 1, 2, 1, 3, 1 ) }, { 2, 1, 3, 1 } ) + end ) end ) - - test( 'gives the median of an un-sorted group.', function() - assert_fuzzy_equal( _.stats.median( 5, 7, 3, 1, 10 ), 5 ) + end ) + end ) + + context( 'Polygon', function() + context( 'GetTriangleHeight', function() + test( 'Given points of triangle and length of base.', function() + assert_multiple_fuzzy_equal( { _.Polygon.GetTriangleHeight( 3, 0, 0, 0, 4, 3, 0 ) }, { 4, 6 } ) + assert_multiple_fuzzy_equal( { _.Polygon.GetTriangleHeight( 6, -2, 1, 2, 4, 4, 1 ) }, { 3, 9 } ) + assert_multiple_fuzzy_equal( { _.Polygon.GetTriangleHeight( 3, 1, 1, 3, 4, 0, 4 ) }, { 3, 4.5 } ) end ) - test( 'gives the median of an un-even group.', function() - assert_fuzzy_equal( _.stats.median( 1, 2, 3, 4, 5, 6 ), 3.5 ) - assert_fuzzy_equal( _.stats.median( 7, 8, 15, 4, 3, 19, 101, 32 ), 11.5 ) + test( 'Given the length of the base and the area.', function() + assert_fuzzy_equal( _.Polygon.GetTriangleHeight( 3, 6 ), 4 ) + assert_fuzzy_equal( _.Polygon.GetTriangleHeight( 6, 9 ), 3 ) end ) end ) - context( 'stats.mode', function() - test( 'gives the mode (most common) of the group and # of occurrences', function() - assert_multiple_fuzzy_equal( { _.stats.mode( 1, 5, 6, 22, 2, 2, 1, 2 ) }, { 2, 3 } ) - end ) - - test( 'gives the mode and # of occurrences of the table', function() - assert_multiple_fuzzy_equal( { _.stats.mode{ 91, 0 , 83, 401, 92, 83, 200 } }, { 83, 2 } ) + context( 'GetSignedArea', function() + test( 'Gives the sigend area of the shape. Positive if clockwise.', function() + assert_fuzzy_equal( _.Polygon.GetSignedArea( 0, 0, 3, 0, 3, 4, 0, 4 ), 12 ) + assert_fuzzy_equal( _.Polygon.GetSignedArea( 0, 0, 3, 0, 0, 4 ), 6 ) + assert_fuzzy_equal( _.Polygon.GetSignedArea( 4, 4, 0, 4, 0, 0, 4, 0 ), 16 ) end ) - test( 'returns false if the group has no mode', function() - assert_false( _.stats.mode( 1, 5, 6, 8, 23423 ) ) - end ) - - test( 'returns false if the group is bimodial', function() - assert_false( _.stats.mode( 1, 23, 5, 23, 1 ) ) - assert_false( _.stats.mode{ 9, 8, 2, 3, 1, 9, 0, 8 } ) + test( 'Negative if counter clock-wise.', function() + assert_fuzzy_equal( _.Polygon.GetSignedArea( 0, 0, 0, 4, 3, 4, 3, 0 ), -12 ) + assert_fuzzy_equal( _.Polygon.GetSignedArea( 0, 0, 0, 4, 3, 0 ), -6 ) end ) end ) - context( 'stats.range', function() - test( 'gets range (largest - smallest) of a set of numbers', function() - assert_fuzzy_equal( _.stats.range( 1, 2, 3, 4 ), 3 ) - assert_fuzzy_equal( _.stats.range( 2, 6, 213, -213 ), 426 ) - assert_fuzzy_equal( _.stats.range( 2, 2, 2, 2 ), 0 ) + context( 'GetArea', function() + test( 'Gives the sigend area of the shape. Positive if clockwise.', function() + assert_fuzzy_equal( _.Polygon.GetArea( 0, 0, 3, 0, 3, 4, 0, 4 ), 12 ) + assert_fuzzy_equal( _.Polygon.GetArea( 0, 0, 3, 0, 0, 4 ), 6 ) + assert_fuzzy_equal( _.Polygon.GetArea( 4, 4, 0, 4, 0, 0, 4, 0 ), 16 ) end ) - test( 'gets the range of a table', function() - assert_fuzzy_equal( _.stats.range( { 1, 2, 3, 4 } ), 3 ) - assert_fuzzy_equal( _.stats.range{ 2, 6, 213, -213 }, 426 ) + test( 'Gives the area of the shape. Negative if counter clock-wise.', function() + assert_fuzzy_equal( _.Polygon.GetArea( 0, 0, 0, 4, 3, 4, 3, 0 ), 12 ) + assert_fuzzy_equal( _.Polygon.GetArea( 0, 0, 0, 4, 3, 0 ), 6 ) end ) end ) - context( 'math.root', function() - test( 'gives the nth math.root of the first number', function() - assert_fuzzy_equal( _.math.root( 4, 2 ), 2 ) - assert_fuzzy_equal( _.math.root( 9, 2 ), 3 ) - assert_fuzzy_equal( _.math.root( 125, 3 ), 5 ) - assert_fuzzy_equal( _.math.root( 100000, 5 ), 10 ) - assert_fuzzy_equal( _.math.root( math.pi, math.pi ), 1.43961949585 ) + context( 'GetCentroid', function() + test( 'Gives the x and y of the centroid.', function() + assert_multiple_fuzzy_equal( { _.Polygon.GetCentroid( 0, 0, 0, 4, 4, 4, 4, 0 ) }, { 2, 2 } ) + assert_multiple_fuzzy_equal( { _.Polygon.GetCentroid( 0, 0, 0, 6, 3, 0 ) }, { 1, 2 } ) + assert_multiple_fuzzy_equal( { _.Polygon.GetCentroid( 2, -1, 2, 1, 1, 2, -1, 2, -2, 1, -2, -1, -1, -2, 1, -2 ) }, { 0, 0 } ) + assert_multiple_fuzzy_equal( { _.Polygon.GetCentroid( 2, 0, 3, 0, 4, 1, 3, 2, 2, 2, 1, 1 ) }, { 2.5, 1 } ) + assert_multiple_fuzzy_equal( { _.Polygon.GetCentroid( 3, 5, 2, 2, 4, 2 ) }, { 3, 3 } ) end ) end ) - context( 'math.prime', function() - test( 'returns true if number is prime, false if composite (not prime)', function() - assert_true( _.math.prime( 3 ) ) - assert_false( _.math.prime( 4 ) ) - assert_true( _.math.prime( 5 ) ) - assert_false( _.math.prime( 8 ) ) + context( 'CheckPoint', function() + test( 'Returns true if the point is in the polygon.', function() + assert_true( _.Polygon.CheckPoint( 2, 2, 0, 0, 0, 4, 4, 4, 4, 0 ) ) + assert_true( _.Polygon.CheckPoint( 1, 1, 0, 0, 2, 0, 2, 2, 0, 2 ) ) + assert_true( _.Polygon.CheckPoint( 3, 2, 2, 2, 3, 1, 4, 3, 5, 2, 4, 4 ) ) end ) - test( 'returns false for any non-interger value or value <= 1', function() - assert_false( _.math.prime( -2 ) ) + test( 'Returns false if the point is not.', function() + assert_false( _.Polygon.CheckPoint( 7, 8, 0, 0, 0, 4, 4, 4, 4, 0 ) ) + assert_false( _.Polygon.CheckPoint( -1, 1, 0, 0, 2, 0, 2, 2, 0, 2 ) ) end ) end ) - context( 'polygon.area', function() - test( 'returns the area of a polygon made going clockwise/counter-clockwise.', function() - assert_fuzzy_equal( _.polygon.area( 2, 2, 2, 5, 5, 5, 5, 2 ), 9 ) - assert_fuzzy_equal( _.polygon.area( 7, 4, 9, 2, 11, 4, 9, 6 ), 8 ) - assert_fuzzy_equal( _.polygon.area( 4, 7, 5, 6, 6, 7, 8, 5, 8, 4, 7, 3, 3, 3, 2, 4, 2, 5 ), 18 ) - assert_fuzzy_equal( _.polygon.area( 2, 2, 3, 3, 2, 4, 2, 6, 1, 7, 2, 7, 4, 6, 5, 7, 7, 4, 5, 5, 4.14, 4.02, 3, 5, 3, 4, 4, 3, 5, 4, 6, 3, 7, 3, 7, 2, 4, 2, 3, 1 ), 16.48 ) + context( 'LineIntersects', function() + test( 'Returns true if the line intersects the polygon.', function() + local tab = _.Polygon.LineIntersects( 0, 4, 4, 4, 0, 0, 0, 4, 4, 4, 4, 0 ) + assert_tables_fuzzy_equal( tab, { { 0, 4 }, { 4, 4 } } ) + tab = _.Polygon.LineIntersects( 0, 4, 4, 0, 0, 0, 0, 4, 4, 4, 4, 0 ) + assert_tables_fuzzy_equal( tab, { { 0, 4 }, { 4, 0 } } ) end ) - test( 'returns the area of a polygon etc. using a table.', function() - assert_fuzzy_equal( _.polygon.area{ 2, 2, 2, 5, 5, 5, 5, 2 }, 9 ) - end ) + test( 'Returns false if the line does not intersect.', function() + assert_false( _.Polygon.LineIntersects( 0, 5, 5, 5, 0, 0, 0, 4, 4, 4, 4, 0 ) ) + end ) + + test( 'Works with vertical lines.', function() + local tab = _.Polygon.LineIntersects( 0, 0, 0, 4, 0, 0, 0, 4, 4, 4, 4, 0 ) + assert_tables_fuzzy_equal( tab, { { 0, 4 }, { 0, 0 } } ) + assert_false( _.Polygon.LineIntersects( -1, 0, -1, 5, 0, 0, 0, 4, 4, 4, 4, 0 ) ) + end ) end ) - context( 'math.round', function() - test( 'math.rounds a number (up or down)', function() - assert_fuzzy_equal( _.math.round( .99999 ), 1 ) - assert_fuzzy_equal( _.math.round( 1 / 3 ), 0 ) - assert_fuzzy_equal( _.math.round( math.pi ), 3 ) - assert_fuzzy_equal( _.math.round( .1908094820398402938402938409234 ), 0 ) - assert_fuzzy_equal( _.math.round( 12345), 12345 ) + context( 'PolygonIntersects', function() + test( 'Returns true if the polygons intersect.', function() + local tab = _.Polygon.PolygonIntersects( { 2, 6, 3, 8, 4, 6 }, { 3, 7, 2, 9, 4, 9 } ) + assert_tables_fuzzy_equal( tab, { { 2.75, 7.5 }, { 3.25, 7.5 } } ) + tab = _.Polygon.PolygonIntersects( { 3, 5, 4, 4, 3, 3, 2, 3, 1, 4, 1, 2, 3, 2, 5, 4, 3, 6, 1, 6 }, { 0, 6, 4, 5, 2, 4 } ) + assert_tables_fuzzy_equal( tab, { { 3.33333, 6.66666 }, { 4, 5 }, { 2, 5.5 } } ) + end ) + + test( 'Returns false if the polygons don\'t intersect.', function() + assert_false( _.Polygon.PolygonIntersects( { 2, 6, 3, 8, 4, 6 }, { 4, 7, 3, 9, 5, 9 } ) ) + assert_false( _.Polygon.PolygonIntersects( { 3, 5, 4, 4, 3, 3, 2, 3, 1, 4, 1, 2, 3, 2, 5, 4, 3, 6, 1, 6 }, { 0, 6, 3, 4, 2, 4 } ) ) + end ) + + test( 'Works with vertical lines.', function() + local tab = _.Polygon.PolygonIntersects( { 2, 3, 2, 6, 4, 6, 4, 4, 5, 5, 5, 3 }, { 3, 2, 3, 5, 6, 4, 6, 3, 4, 3, 4, 2 } ) + assert_tables_fuzzy_equal( tab, { { 4, 4.66666 }, { 4.5, 4.5 }, { 5, 4.33333 }, { 5, 3 }, { 3, 3 }, { 4, 3 } } ) end ) end ) - context( 'math.getAngle', function() - test( 'returns the radians needed to get to start (1st 2), to location (2nd 2), w/ orientation (assumed up).', function() - assert_fuzzy_equal( _.math.getAngle( 0, 0, 3, 3, 'up' ), 2.35619449 ) - assert_fuzzy_equal( _.math.getAngle( 0, 0, 3, 3, 'right' ), 0.785398163 ) - assert_fuzzy_equal( _.math.getAngle( 0, 0, 3, 3, 'down' ), -0.785398163 ) - assert_fuzzy_equal( _.math.getAngle( 0, 0, 3, 3, 'left' ), -2.35619449 ) + context( 'CircleIntersects', function() + test( 'Returns true if the circle intersects', function() + local tab = _.Polygon.CircleIntersects( 3, 5, 2, 3, 1, 3, 6, 7, 4 ) + assert_tables_fuzzy_equal( tab, { { 'Tangent', 3, 3 }, { 'Tangent', 5, 5 } } ) + tab = _.Polygon.CircleIntersects( 5, 5, 1, 4, 4, 6, 4, 6, 6, 4, 6 ) + assert_tables_fuzzy_equal( tab, { { 'Tangent', 5, 4 }, { 'Tangent', 6, 5 }, { 'Tangent', 5, 6 }, { 'Tangent', 4, 5 } } ) + tab = _.Polygon.CircleIntersects( 3, 4, 2, 3, 3, 2, 4, 3, 5, 4, 4 ) + assert_tables_fuzzy_equal( tab, { { 'Enclosed', 3, 3, 2, 4 }, { 'Enclosed', 2, 4, 3, 5 }, { 'Enclosed', 3, 5, 4, 4 }, { 'Enclosed', 4, 4, 3, 3 } } ) + end ) + + test( 'Returns false if the circle doesn\'t intersect.', function() + assert_false( _.Polygon.CircleIntersects( 9, 9, 2, 3, 1, 3, 6, 7, 4 ) ) + assert_false( _.Polygon.CircleIntersects( 10, 5, 1, 4, 4, 6, 4, 6, 6, 4, 6 ) ) + end ) + end ) + end ) + + context( 'Circle', function() + context( 'GetArea', function() + test( 'Gives the area of the circle.', function() + assert_fuzzy_equal( _.Circle.GetArea( 1 ), 3.14159 ) + assert_fuzzy_equal( _.Circle.GetArea( 2 ), 12.56637 ) + assert_fuzzy_equal( _.Circle.GetArea( 5 ), 78.53981 ) + assert_fuzzy_equal( _.Circle.GetArea( 10 ), 314.15926 ) + assert_fuzzy_equal( _.Circle.GetArea( 20 ), 1256.63706 ) end ) end ) - context( 'polygon.centroid', function() - test( 'returns the centroid of a polygon.', function() - assert_multiple_fuzzy_equal( { _.polygon.centroid( 0, 100, 100, 50, 100, 100 ) }, { 66.666666, 83.333333 } ) - assert_multiple_fuzzy_equal( { _.polygon.centroid( 3, 5, 4, 4, 6, 4, 6, 6, 5, 7, 4, 7 ) }, { 4.666666, 5.38095238 } ) - assert_multiple_fuzzy_equal( { _.polygon.centroid( 2, 2, 5, 2, 6, 3, 6, 4, 5, 5, 2, 5, 2, 4, 4, 4, 5, 3 ) }, { 4.23809523, 3.69047619 } ) - assert_multiple_fuzzy_equal( { _.polygon.centroid( 4, 3, 3, 6, 9, 7, 12, 3, 10, 2, 8, 5, 7, 4, 7, 2, 6, 4, 6, 5, 5, 4, 6, 2, 5, 1, 3, 2 ) }, { 7.012578616, 4.377358490 } ) - assert_multiple_fuzzy_equal( { _.polygon.centroid( 2, 5, 4, 7, 8, 7, 11, 6, 15, 5, 14, 9, 16, 9, 18, 8, 19, 5, 18, 3, 16, 2, 14, 2, 14, 3, 16, 3, 18, 4, 17, 7, 16, 8, 13, 5, 13, 4, 11, 4, 10, 5, 7, 6, 7, 4, 4, 4 ) }, { 10.7810945, 5.57711442786 } ) + context( 'CheckPoint', function() + test( 'Returns true if the point is on the circle.', function() + assert_true( _.Circle.CheckPoint( 3, 4, 2, 1, 4 ) ) + assert_true( _.Circle.CheckPoint( 2, 2, 1, 2, 1 ) ) + assert_true( _.Circle.CheckPoint( 2, 4, 2, 0, 4 ) ) + end ) + + test( 'Returns false if the point is not on the circle.', function() + assert_false( _.Circle.CheckPoint( 3, 4, 2, 2, 4 ) ) + assert_false( _.Circle.CheckPoint( 2, 2, 1, 2, 2 ) ) end ) end ) - context( 'math.log', function() - test( 'returns the nth math.logarithm, with n being the second argument.', function() - assert_fuzzy_equal( _.math.log( 1, 2 ), 0 ) - assert_fuzzy_equal( _.math.log( 2, 2 ), 1 ) - assert_fuzzy_equal( _.math.log( 3, 2 ), 1.584962500721156 ) - assert_fuzzy_equal( _.math.log( 10, 2 ), 3.321928094887362 ) - assert_fuzzy_equal( _.math.log( 5, 3 ), 1.4649735207179 ) - assert_fuzzy_equal( _.math.log( 7, 2 ), 2.807354922057604 ) + context( 'GetCircumference', function() + test( 'Gives the circumference of the circle.', function() + assert_fuzzy_equal( _.Circle.GetCircumference( 1 ), 6.28318 ) + assert_fuzzy_equal( _.Circle.GetCircumference( 2 ), 12.56637 ) + assert_fuzzy_equal( _.Circle.GetCircumference( 5 ), 31.41592 ) + assert_fuzzy_equal( _.Circle.GetCircumference( 10 ), 62.83185 ) + assert_fuzzy_equal( _.Circle.GetCircumference( 20 ), 125.66370 ) end ) end ) - context( 'math.summation', function() - test( 'returns the sumation.', function() - assert_fuzzy_equal( _.math.summation( 3, 6, function( i ) return ( i * ( i + 1 ) ) / 2 end ), 52 ) - assert_fuzzy_equal( _.math.summation( 1, 10, function( i ) return ( i * 2 ) end ), 110 ) + context( 'IsLineSecant', function() + test( 'Returns \'Secant\' when intersects twice.', function() + assert_multiple_fuzzy_equal( { _.Circle.IsLineSecant( 4, 9, 1, 0, 9, 6, 9 ) }, { 'Secant', 3, 9, 5, 9 } ) + assert_multiple_fuzzy_equal( { _.Circle.IsLineSecant( 2, 2, 1, 2, 3, 3, 2 ) }, { 'Secant', 2, 3, 3, 2 } ) end ) - - test( 'can take the previous value in the argument.', function() - assert_fuzzy_equal( _.math.summation( 1, 5, function( i, t ) if t[i-1] then return i + t[i-1] else return 1 end end ), 35 ) + + test( 'Returns \'Tangent\' when intersects once.', function() + assert_multiple_fuzzy_equal( { _.Circle.IsLineSecant( 4, 9, 1, 0, 8, 6, 8 ) }, { 'Tangent', 4, 8 } ) + assert_multiple_fuzzy_equal( { _.Circle.IsLineSecant( 2, 2, 1, 2, 3, 0, 3 ) }, { 'Tangent', 2, 3 } ) end ) - test( 'returns false if math.huge is second argument.', function() - assert_false( _.math.summation( 1, math.huge, function( i ) return i end ) ) - assert_false( _.math.summation( 1, -math.huge, function( i ) return i end ) ) - end ) + test( 'Returns \'false\' when neither.', function() + assert_false( _.Circle.IsLineSecant( 4, 9, 1, 0, 7, 6, 8 ) ) + end ) end ) - context( 'math.percentOfChange', function() - test( 'gives the % of change of two numbers.', function() - assert_fuzzy_equal( _.math.percentOfChange( 2, 4 ), 1 ) - assert_fuzzy_equal( _.math.percentOfChange( 10, 0 ), -1 ) - assert_fuzzy_equal( _.math.percentOfChange( 40, 40 ), 0 ) + context( 'IsSegmentSecant', function() + test( 'Returns \'Secant\' if the line connects two points.', function() + assert_multiple_fuzzy_equal( { _.Circle.IsSegmentSecant( 4, 9, 1, 0, 9, 6, 9 ) }, { 'Secant', 3, 9, 5, 9 } ) end ) - test( 'gets the % of change from negative #s.', function() - assert_fuzzy_equal( _.math.percentOfChange( -15, 15 ), 2 ) + test( 'Returns \'Tangent\' if the line attaches only one point.', function() + assert_multiple_fuzzy_equal( { _.Circle.IsSegmentSecant( 1, 1, 1, 0, 0, 0, 2 ) }, { 'Tangent', 0, 1 } ) end ) - test( 'returns false if getting from 0.', function() - assert_false( _.math.percentOfChange( 0, 10 ) ) + test( 'Returns \'Chord\' if both points are on the circle.', function() + assert_multiple_fuzzy_equal( { _.Circle.IsSegmentSecant( 0, 0, 1, -1, 0, 1, 0 ) }, { 'Chord', -1, 0, 1, 0 } ) end ) - end ) - - context( 'math.percent', function() - test( 'gives the % of the 2nd number.', function() - assert_fuzzy_equal( _.math.percent( 1, 2 ), 4 ) - assert_fuzzy_equal( _.math.percent( -1, 10 ), 0 ) - assert_fuzzy_equal( _.math.percent( 0, 40 ), 40 ) - assert_fuzzy_equal( _.math.percent( 2, -15 ), 15 ) - assert_fuzzy_equal( _.math.percent( 10, 0 ), 0 ) + + test( 'Returns \'Enclosed\' if the line is within the circle entirely.', function() + assert_multiple_fuzzy_equal( { _.Circle.IsSegmentSecant( 0, 0, 2, -1, 0, 1, 0 ) }, { 'Enclosed', -1, 0, 1, 0 } ) end ) - end ) - - context( 'circle.checkPoint', function() - test( 'returns true if the point is on the circle.', function() - assert_true( _.circle.checkPoint( 3, 4, 2, 1, 4 ) ) - assert_false( _.circle.checkPoint( 8, 5, 3, 3, 4 ) ) - assert_false( _.circle.checkPoint( 8, 5, 3, 8, 9 ) ) - assert_true( _.circle.checkPoint( 8, 5, 2, 8, 3 ) ) - assert_true( _.circle.checkPoint( 13, 3, 5, 13, -2 ) ) + + test( 'Returns \'false\' if the line doesn\'t touch anywhere.', function() + assert_false( _.Circle.IsSegmentSecant( 0, 0, 1, 2, 2, 2, 3 ) ) end ) end ) - context( 'circle.circumference', function() - test( 'returns the circumference of the circle.', function() - assert_fuzzy_equal( _.circle.circumference( 1 ), 6.28318530718 ) - assert_fuzzy_equal( _.circle.circumference( 2 ), 12.5663706144 ) - assert_fuzzy_equal( _.circle.circumference( 3 ), 18.8495559215 ) - assert_fuzzy_equal( _.circle.circumference( 4 ), 25.1327412287 ) - assert_fuzzy_equal( _.circle.circumference( 10 ), 62.8318530718 ) + context( 'CirclesIntersect', function() + test( 'Returns true if the point is within the circle.', function() + assert_multiple_fuzzy_equal( { _.Circle.CircleIntersects( 0, 0, 4, 0, 4, 8 ) }, { 0, -4, 0, -4 } ) end ) - end ) - - context( 'math.quadraticFactor', function() - test( 'returns the 2 solutions for x in the quadratic equation.', function() - assert_multiple_fuzzy_equal( { _.math.quadraticFactor( 1, 3, -4 ) }, { -4, 1 } ) - assert_multiple_fuzzy_equal( { _.math.quadraticFactor( 2, -4, -3 ) }, { -.58113883, 2.5811388 } ) - assert_multiple_fuzzy_equal( { _.math.quadraticFactor( 2, 5, -3 ) }, { -3, .5 } ) + + test( 'Returns \'Equal\' if the circles are the same.', function() + assert_equal( _.Circle.CircleIntersects( 0, 0, 4, 0, 0, 4 ), 'Equal' ) end ) - test( 'returns 1 solution for x (if applicable).', function() - assert_multiple_fuzzy_equal( { _.math.quadraticFactor( 9, 12, 4 ) }, { -2 / 3, -2 / 3 } ) + test( 'Returns \'Colinear\' if circles have same x and y but not radii.', function() + assert_equal( _.Circle.CircleIntersects( 0, 0, 4, 0, 0, 8 ), 'Colinear' ) + assert_equal( _.Circle.CircleIntersects( 0, 0, 8, 0, 0, 4 ), 'Colinear' ) end ) - test( 'returns false if no solution.', function() - assert_false( _.math.quadraticFactor( 3, 4, 2 ) ) + test( 'Returns false if the point is not within the cirlce.', function() + assert_false( _.Circle.CircleIntersects( 4, 4, 1, 6, 6, 1 ) ) end ) end ) - context( 'circle.secant', function() - test( 'returns if line is a secant, tan, or false using slope and y-intercept.', function() - assert_multiple_fuzzy_equal( { _.circle.secant( 4, 9, 1, 0, 9 ) }, { 'secant', 3, 9, 5, 9 } ) - assert_multiple_fuzzy_equal( { _.circle.secant( 3, 8, 2, 0, 10 ) }, { 'tangent', 3, 10 } ) + context( 'IsPointInCircle', function() + test( 'Returns true if the point is within the circle.', function() + assert_true( _.Circle.IsPointInCircle( 0, 0, 2, 1, 1 ) ) + assert_true( _.Circle.IsPointInCircle( 5, 5, 5, 2, 2 ) ) + assert_true( _.Circle.IsPointInCircle( -3, 9, 2, -2, 8 ) ) end ) - test( 'returns if line is a secant, tan, or false using 2 points.', function() - assert_multiple_fuzzy_equal( { _.circle.secant( 4, 9, 1, 0, 9, 6, 9 ) }, { 'secant', 3, 9, 5, 9 } ) - assert_multiple_fuzzy_equal( { _.circle.secant( 3, 8, 2, 0, 10, 3, 10 ) }, { 'tangent', 3, 10 } ) + test( 'Returns false if the point is not within the cirlce.', function() + assert_false( _.Circle.IsPointInCircle( 0, 0, 2, 5, 1 ) ) + assert_false( _.Circle.IsPointInCircle( -3, 9, 2, -2, 7 ) ) end ) - - test( 'can get vertical line slope intersections with points.', function() - assert_multiple_fuzzy_equal( { _.circle.secant( 3, 8, 2, 0, 10, 3, 10 ) }, { 'tangent', 3, 10 } ) + end ) + end ) + + context( 'Statistics', function() + context( 'GetMean', function() + test( 'Gives the arithmetic mean of numbers.', function() + assert_equal( _.Statistics.GetMean( 1, 2, 3, 4, 5 ), 3 ) + assert_equal( _.Statistics.GetMean( 10, 10, 10, 10 ), 10 ) end ) end ) - context( 'circle.circlesIntersect', function() - test( 'returns the points that intersect.', function() - assert_multiple_fuzzy_equal( { _.circle.circlesIntersect( 1, 1, 1, 2, 2, 1 ) }, { 2, 1, 1, 2 } ) - assert_multiple_fuzzy_equal( { _.circle.circlesIntersect( 1, 1, 1, 4, 4, 3.60555127546 ) }, { 2, 1, 1, 2 } ) + context( 'GetMedian', function() + test( 'Gives the median of numbers.', function() + assert_equal( _.Statistics.GetMedian( 1, 2, 3, 4, 5 ), 3 ) end ) - test( 'returns only one set of point if there is only one point of intersection.', function() - assert_multiple_fuzzy_equal( { _.circle.circlesIntersect( 1, 1, 1, 3, 1, 1 ) }, { 2, 1 } ) + test( 'Gives average of two numbers if the amount of numbers is even.', function() + assert_equal( _.Statistics.GetMedian( 1, 2, 3, 4, 5, 6 ), 3.5 ) end ) - test( 'returns true if the circles are coincident.', function() - assert_true( _.circle.circlesIntersect( 1, 1, 1, 1, 1, 1 ) ) + test( 'Works when the numbers aren\'t ordered, too.', function() + assert_equal( _.Statistics.GetMedian( 5, 3, 4, 2, 1 ), 3 ) + assert_equal( _.Statistics.GetMedian( 3, 4, 1, 2, 5, 6 ), 3.5 ) end ) + end ) + + context( 'GetMode', function() + test( 'Returns the mode.', function() + assert_tables_fuzzy_equal( { _.Statistics.GetMode( math.pi, math.huge, math.pi ) }, { { math.pi }, 2 } ) + end ) - test( 'returns false if the circles do not collide anywhere,', function() - assert_false( _.circle.circlesIntersect( 5, 6, 1, 90, 2, 1 ) ) + test( 'Works if it\'s bimodial, too', function() + assert_tables_fuzzy_equal( { _.Statistics.GetMode( 2, 2, 1, 1, 3 ) }, { { 1, 2 }, 2 } ) + end ) + end ) + + context( 'GetRange', function() + test( 'Returns the range.', function() + assert_equal( _.Statistics.GetRange( 1, 2, 3, 4 ), 3 ) + assert_equal( _.Statistics.GetRange( 100, 5, 3, 6, 7 ), 97 ) + end ) + end ) + end ) + + context( 'Math', function() + context( 'GetRoot', function() + test( 'Gives the nth root to x, given n and x.', function() + assert_multiple_fuzzy_equal( { _.Math.GetRoot( 4, 2 ) }, { 2, -2 } ) + assert_multiple_fuzzy_equal( { _.Math.GetRoot( 16, 2 ) }, { 4, -4 } ) + assert_multiple_fuzzy_equal( { _.Math.GetRoot( 4, -2 ) }, { .5, -.5 } ) end ) end ) - context( 'line.segment.checkPoint', function() - test( 'returns true if the point is on the line segment.', function() - assert_true( _.line.segment.checkPoint( 0, 0, 2, 2, 1, 1 ) ) - assert_true( _.line.segment.checkPoint( 2, 4, 4, 6, 3, 5 ) ) - assert_true( _.line.segment.checkPoint( 1, 1, 3, 1, 2, 1 ) ) + context( 'IsPrime', function() + test( 'Returns true if a number is prime.', function() + assert_true( _.Math.IsPrime( 3 ) ) + assert_true( _.Math.IsPrime( 2 ) ) + assert_true( _.Math.IsPrime( 47 ) ) end ) - test( 'returns false if the point is not on the line segment.', function() - assert_false( _.line.segment.checkPoint( 0, 0, 2, 2, 1, 2 ) ) - assert_false( _.line.segment.checkPoint( 1, 1, 3, 1, 4, 1 ) ) + test( 'Returns false if a number is not prime.', function() + assert_false( _.Math.IsPrime( 1 ) ) + assert_false( _.Math.IsPrime( 100 ) ) end ) end ) - context( 'line.intersect', function() - test( 'returns the x and y where the lines intersect.', function() - assert_multiple_fuzzy_equal( { _.line.intersect( 1, 0, -1, 2 ) }, { 1, 1 } ) - assert_multiple_fuzzy_equal( { _.line.intersect( 1, 0, 2, 0, 0, 2 ) }, { 1, 1 } ) + context( 'Round', function() + test( 'Rounds down if the number is less than .5.', function() + assert_equal( _.Math.Round( 3.4 ), 3 ) end ) - test( 'can accept lines without a slope, too.', function() - assert_multiple_fuzzy_equal( { _.line.intersect( 0, 2, 2, 1, 2, 3 ) }, { 2, 2 } ) + test( 'Rounds up if the number is more than .5.', function() + assert_equal( _.Math.Round( 6.7 ), 7 ) end ) - test( 'returns false if lines are parallel.', function() - assert_false( _.line.intersect( 1, 1, 1, 2 ) ) - assert_false( _.line.intersect( 5, 0, 5, 6 ) ) + test( 'Specify the number of decimal places to use.', function() + assert_equal( _.Math.Round( 197.88, 2 ), 197.88 ) + assert_equal( _.Math.Round( 197.88, 1 ), 197.9 ) + assert_equal( _.Math.Round( 197, -2 ), 200 ) end ) end ) - context( 'line.segment.intersect', function() - test( 'returns the x and y where the line segments intersect.', function() - assert_multiple_fuzzy_equal( { _.line.segment.intersect( 1, 1, 5, 3, 2, 3, 4, 1 ) }, { 3, 2 } ) - assert_multiple_fuzzy_equal( { _.line.segment.intersect( 1, 5, 3, 7, 3, 5, 1, 7 ) }, { 2, 6 } ) + context( 'Summation', function() + test( 'Adds up numbers and such.', function() + assert_equal( _.Math.GetSummation( 1, 10, function( i ) return ( i * 2 ) end ), 110 ) end ) - test( 'returns false if off line segment.', function() - assert_false( _.line.segment.intersect( 3, 7, 6, 8, 1, 6, 5, 4 ) ) - assert_false( _.line.segment.intersect( 3, 7, 6, 8, 1, 6, 5, 4 ) ) - assert_false( _.line.segment.intersect( 1, 6, 5, 4, 7, 3, 11, 1 ) ) + test( 'Can access previous values with second function argument.', function() + assert_equal( _.Math.GetSummation( 1, 5, function( i, t ) if t[i-1] then return i + t[i-1] else return 1 end end ), 35 ) end ) end ) - context( 'polygon.checkPoint', function() - test( 'returns true if the point is in the polygon.', function() - assert_true( _.polygon.checkPoint( 2, 2, 0, 0, 0, 4, 4, 4, 4, 0 ) ) - assert_true( _.polygon.checkPoint( 2, 4, 4, 2, 3, 3, 2, 3, 2, 2, 1, 2, 1, 5, 4, 5 ) ) - assert_true( _.polygon.checkPoint( 7.5, 4.5, 7, 2, 6, 3, 5, 2, 4, 3, 4, 5, 5, 6, 6, 5, 6, 4, 7, 4, 7, 5, 8, 5, 8, 4, 9, 4, 9, 5, 10, 6, 11, 5, 10, 4, 11, 3, 10, 2, 9, 3, 8, 2 ) ) + context( 'GetPercentOfChange', function() + test( 'Gives the percentage of change.', function() + assert_equal( _.Math.GetPercentOfChange( 2, 4 ), 1 ) + assert_equal( _.Math.GetPercentOfChange( 4, 2 ), -.5 ) + assert_equal( _.Math.GetPercentOfChange( 4, 0 ), -1 ) + assert_equal( _.Math.GetPercentOfChange( 0, 0 ), 0 ) end ) - test( 'returns false if the point is outside the polygon.', function() - assert_false( _.polygon.checkPoint( 7, 8, 0, 0, 0, 4, 4, 4, 4, 0 ) ) - assert_false( _.polygon.checkPoint( 3, 2, 4, 2, 3, 3, 2, 3, 2, 2, 1, 2, 1, 5, 4, 5 ) ) + test( 'False if original is 0.', function() + assert_false( _.Math.GetPercentOfChange( 0, 3 ) ) end ) end ) - context( 'line.closestPoint', function() - test( 'returns the closest point to the line.', function() - assert_multiple_fuzzy_equal( { _.line.closestPoint( 4, 2, 1, 1, 3, 5 ) }, { 2, 3 } ) - assert_multiple_fuzzy_equal( { _.line.closestPoint( 9, 0, 4, 5, 5, 2 ) }, { 6, -1 } ) - assert_multiple_fuzzy_equal( { _.line.closestPoint( 4, 8, 2, 8, 4, 6 ) }, { 3, 7 } ) - assert_multiple_fuzzy_equal( { _.line.closestPoint( 3, 4, 2, 3, 4, 3 ) }, { 3, 3 } ) - assert_multiple_fuzzy_equal( { _.line.closestPoint( 3, 3, 2, 4, 2, 2 ) }, { 2, 3 } ) + context( 'GetPercent', function() + test( 'Gives the percent.', function() + assert_equal( _.Math.GetPercent( 1, 2 ), 2 ) + assert_equal( _.Math.GetPercent( 2, 1 ), 2 ) + assert_equal( _.Math.GetPercent( .5, 50 ), 25 ) + assert_equal( _.Math.GetPercent( 50, 2 ), 100 ) + assert_equal( _.Math.GetPercent( -.5, 4 ), 2 ) end ) end ) - context( 'line.segmentIntersects.', function() - test( 'returns the x and y if the line segment intersects.', function() - assert_multiple_fuzzy_equal( { _.line.segmentIntersects( 3, 6, 5, 8, 3, 8, 5, 6 ) }, { 4, 7 } ) - end ) - - test( 'returns the x and y of a vertical line.', function() - assert_multiple_fuzzy_equal( { _.line.segmentIntersects( 0, 3, 2, 5, 1, 1, 1, 5 ) }, { 1, 4 } ) - assert_multiple_fuzzy_equal( { _.line.segmentIntersects( 3, 5, 3, 7, 1, 8, 4, 5 ) }, { 3, 6 } ) + context( 'GetRootsOfQuadratic', function() + test( 'Gives roots given a, b, and c.', function() + assert_multiple_fuzzy_equal( { _.Math.GetRootsOfQuadratic( 1, -3, -4 ) }, { -1, 4 } ) + assert_multiple_fuzzy_equal( { _.Math.GetRootsOfQuadratic( 1, 0, -4 ) }, { -2, 2 } ) + assert_multiple_fuzzy_equal( { _.Math.GetRootsOfQuadratic( 6, 11, -35 ) }, { -3.5, 5/3 } ) end ) - test( 'returns the x and y of a horizontal line.', function() - assert_multiple_fuzzy_equal( { _.line.segmentIntersects( 2, 4, 4, 4, 4, 3, 2, 5 ) }, { 3, 4 } ) - assert_multiple_fuzzy_equal( { _.line.segmentIntersects( 4, 6, 8, 8, 5, 8, 9, 8 ) }, { 8, 8 } ) + test( 'Returns false it has no roots.', function() + assert_false( _.Math.GetRootsOfQuadratic( 1, 2, 4 ) ) + assert_false( _.Math.GetRootsOfQuadratic( .6, .3, .9 ) ) end ) end ) - context( 'polygon.lineIntersects', function() - test( 'returns true if the line (segment) intersects with the polygon.', function() - assert_true( _.polygon.lineIntersects( 3, 7, 5, 4, 3, 5, 4, 4, 5, 5, 6, 4, 6, 6, 3, 6 ) ) - assert_true( _.polygon.lineIntersects( 2, 5, 7, 6, 3, 5, 4, 4, 5, 5, 6, 4, 6, 6, 3, 6 ) ) - end ) - - test( 'returns fasle if the line does not intersect with the polygon.', function() - assert_false( _.polygon.lineIntersects( 2, 5, 3, 7, 3, 5, 4, 4, 5, 5, 6, 4, 6, 6, 3, 6 ) ) + context( 'GetAngle', function() + test( 'Gives the angle between three points.', function() + assert_fuzzy_equal( _.Math.GetAngle( 1, 3, 1, 1, 3, 1 ), 1.57079633 ) + assert_fuzzy_equal( _.Math.GetAngle( 4, 4, 1, 1, 4, 1 ), 0.785398163 ) end ) end ) - - context( 'polygon.polygonIntersects', function() - test( 'returns true if the polygons intersect.', function() - assert_true( _.polygon.polygonIntersects( { 2, 6, 3, 8, 4, 6 }, { 3, 7, 2, 9, 4, 9 } ) ) + end ) + + context( 'Shape', function() + context( 'NewShape', function() + test( 'Makes circles if given 3 arguments.', function() + assert_tables_fuzzy_equal( { _.Shape.NewShape( 1, 1, 1 ) }, { { + 1, 1, 1, + Type = 'Circle', + x = 1, + y = 1, + Radius = 1, + Area = math.pi, + Collided = false, + Index = 1, + Removed = false, + } } ) end ) - test( 'returns false if the polygons don\'t intersect.', function() - assert_false( _.polygon.polygonIntersects( { 2, 6, 3, 8, 4, 6 }, { 4, 7, 3, 9, 5, 9 } ) ) - end ) - end ) - - context( 'polygon.circleIntersects', function() - test( 'returns true if the polygon intersects the circle.', function() - assert_true( _.polygon.circleIntersects( 3, 5, 2, 2, 6, 4, 6, 3, 8 ) ) + test( 'Makes lines if given 4 arguments.', function() + assert_tables_fuzzy_equal( { _.Shape.NewShape( 1, 1, 0, 0 ) }, { { + 1, 1, 0, 0, + Type = 'Line', + x1 = 1, + y1 = 1, + x2 = 0, + y2 = 0, + Slope = 1, + Intercept = 0, + Collided = false, + Index = 1, + Removed = false, + } } ) end ) - test( 'returns false if the polygon does not intersect the circle.', function() - assert_false( _.polygon.circleIntersects( 3, 3, 2, 2, 6, 4, 6, 3, 8 ) ) + test( 'Makes polygons if given more than 4 arguments.', function() + assert_tables_fuzzy_equal( { _.Shape.NewShape( -1, -1, -1, 1, 1, 1, 1, -1 ) }, { { + -1, -1, -1, 1, 1, 1, 1, -1, + Points = { -1, -1, -1, 1, 1, 1, 1, -1 }, + Type = 'Polygon', + Area = 4, + Collided = false, + Index = 1, + Removed = false, + } } ) + end ) + end ) + + context( 'CheckCollisions', function() + _.Shape.Remove() + local Circle = _.Shape.NewShape( 300, 300, 10 ) + local Rectangle = _.Shape.NewShape( 400, 300, 400, 200, 600, 200, 600, 300 ) + local Line = _.Shape.NewShape( 400, 200, 300, 400 ) + + local function Reset() + _.Shape.Remove() + Circle = _.Shape.NewShape( 300, 300, 10 ) + Rectangle = _.Shape.NewShape( 400, 300, 400, 200, 600, 200, 600, 300 ) + Line = _.Shape.NewShape( 400, 200, 300, 400 ) + + Circle.Collided = false + Rectangle.Collided = false + Line.Collided = false + end + + test( 'When called with no arguments it checks all collisions.', function() + Reset() + _.Shape.CheckCollisions() + assert_true( Rectangle.Collided ) + assert_true( Line.Collided ) + assert_false( Circle.Collided ) + end ) + + test( 'A table with arguments only checks mentioned items.', function() + Reset() + _.Shape.CheckCollisions( { Rectangle, Circle } ) + assert_false( Rectangle.Collided ) + assert_false( Line.Collided ) + assert_false( Circle.Collided ) + + Reset() + _.Shape.CheckCollisions{ Rectangle, Line } + assert_true( Rectangle.Collided ) + assert_true( Line.Collided ) + assert_false( Circle.Collided ) + end ) + + test( 'Can use ":" to check collisions on a certain item.', function() + Reset() + Circle:CheckCollisions() + assert_false( Rectangle.Collided ) + assert_false( Line.Collided ) + assert_false( Circle.Collided ) + end ) + + test( 'You can also use it to check only certain collisions.', function() + Reset() + Line:CheckCollisions( Rectangle ) + assert_true( Rectangle.Collided ) + assert_true( Line.Collided ) + assert_false( Circle.Collided ) + end ) + end ) + + context( 'Remove', function() + _.Shape.Remove() + local Circle = _.Shape.NewShape( 300, 300, 10 ) + local Rectangle = _.Shape.NewShape( 400, 300, 400, 200, 600, 200, 600, 300 ) + local Line = _.Shape.NewShape( 400, 200, 300, 400 ) + + local function Reset() + _.Shape.Remove() + Circle = _.Shape.NewShape( 300, 300, 10 ) + Rectangle = _.Shape.NewShape( 400, 300, 400, 200, 600, 200, 600, 300 ) + Line = _.Shape.NewShape( 400, 200, 300, 400 ) + end + + test( 'No arguments removes all shapes.', function() + Reset() + _.Shape.Remove() + assert_true( Circle.Removed ) + assert_true( Rectangle.Removed ) + assert_true( Line.Removed ) + end ) + + test( 'Pass arguments to remove certain shapes.', function() + Reset() + _.Shape.Remove( { Circle, Rectangle } ) + assert_true( Circle.Removed ) + assert_true( Rectangle.Removed ) + assert_false( Line.Removed ) + end ) + + test( 'Use ":" to remove a single item.', function() + Reset() + Circle:Remove() + assert_true( Circle.Removed ) + assert_false( Rectangle.Removed ) + assert_false( Line.Removed ) + end ) + + test( 'Use ":" and a table to remove multiple items.', function() + Reset() + Circle:Remove( Rectangle ) + assert_true( Circle.Removed ) + assert_true( Rectangle.Removed ) + assert_false( Line.Removed ) end ) end ) - end -) \ No newline at end of file + end ) +end ) \ No newline at end of file