|
import 'dart:math'; |
|
|
|
void main() async { |
|
const degrees = 30; |
|
print(sin(degrees)); |
|
print(cos(degrees)); |
|
print(tan(degrees)); |
|
} |
|
|
|
double sin(num degrees, {int resolution = 1000}) => |
|
calulateUnitCirclePoint(degrees, resolution: resolution).y; |
|
|
|
double cos(num degrees, {int resolution = 1000}) => |
|
calulateUnitCirclePoint(degrees, resolution: resolution).x; |
|
|
|
double tan(num degrees, {int resolution = 1000}) { |
|
final unitCirclePoint = |
|
calulateUnitCirclePoint(degrees, resolution: resolution); |
|
return unitCirclePoint.y / unitCirclePoint.x; |
|
} |
|
|
|
/// Calculates a point on the unit circle at the given angle in [degrees]. |
|
/// Rotation is anticlockwise, starting at the positive side of the x axis. |
|
/// |
|
/// The higher the [resolution], the more accurate lengths will be during |
|
/// calculations. |
|
Point<double> calulateUnitCirclePoint(num degrees, {int resolution = 1000}) { |
|
// Calculate co-ordinates in the direction of [degrees], and scale it up to |
|
// the radius specified by [resolution] at maximum. |
|
final direction = getDirection(degrees); |
|
final minimumPoint = direction * resolution; |
|
|
|
// Extend the point until it's as close to the circumference as possible. |
|
int x = minimumPoint.x.floor(); |
|
int y = minimumPoint.y.floor(); |
|
while (lineLength(0, 0, x, y) < resolution) { |
|
x += x.sign; |
|
y += y.sign; |
|
} |
|
|
|
// Return the point. |
|
return Point(x / resolution, y / resolution); |
|
} |
|
|
|
/// Approximates the width to height ratio of a line pointing at [degrees] |
|
/// degrees. |
|
/// |
|
/// This function is fairly inaccurate, as it assumes that as the degree value |
|
/// changes, the x and y values change in a linear fashion. This is not a |
|
/// correct assumption, but it yields decent results. |
|
Point<double> getDirection(num degrees) { |
|
// Calculate X and Y values ranging between -1 and 1, that can later be |
|
// multiplied by a constant to generate lines of any length at the same angle. |
|
// |
|
// In other words, convert a polar value in the form (1,θ) to rectangular |
|
// coordinates in the form (x,y). |
|
final double xScale; |
|
final double yScale; |
|
if (degrees >= 0 && degrees < 90) { |
|
// Q1 |
|
xScale = 1 - degrees / 90; // [1, 0) |
|
yScale = -xScale + 1; // [0, 1) |
|
} else if (degrees >= 90 && degrees < 180) { |
|
// Q4 |
|
xScale = -(degrees - 90) / 90; // [0, -1) |
|
yScale = xScale + 1; // [1, 0) |
|
} else if (degrees >= 180 && degrees < 270) { |
|
// Q3 |
|
xScale = (degrees - 270) / 90; // [-1, 0) |
|
yScale = -xScale - 1; // [0, -1) |
|
} else if (degrees >= 270 && degrees < 360) { |
|
// Q2 |
|
xScale = (degrees - 270) / 90; // [0, 1) |
|
yScale = xScale - 1; // [-1, 0) |
|
} else { |
|
throw ArgumentError('Degree value outside 0 - 359!'); |
|
} |
|
return Point(xScale, yScale); |
|
} |
|
|
|
/// Calculates the absolute distance between two points. |
|
double lineLength(num x1, num y1, num x2, num y2) { |
|
// Calculate the differences between the x and y values. |
|
final width = (x2 - x1).abs(); |
|
final height = (y2 - y1).abs(); |
|
// Thanks, Pythagoras! |
|
return sqrt(width * width + height * height); |
|
} |