Skip to content

Instantly share code, notes, and snippets.

@hacker1024
Created August 19, 2021 10:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hacker1024/2ed1b4887571f4c48a8d26fded74be23 to your computer and use it in GitHub Desktop.
Save hacker1024/2ed1b4887571f4c48a8d26fded74be23 to your computer and use it in GitHub Desktop.
Primitive (inaccurate) trigonometric calculations in pure Dart

trigonometry.dart

This is an experiment to test myself: Using only my knowlege of trigonometry and the unit circle, can I implement trigonometric functions?

The algorithm

The degree value is analyzed to determine what quadrant it's in. The angle's "progress" through the quadrant (0 - 90 degrees) is expressed as a value between 0 and 1 (or -1 and 0, depending on the quadrant), which is then used to approximate X and Y coordinates.
These coordinates are then scaled to an arbitrary radius length (the higher, the more accurate) and then normalised as a point on the unit circle to determine sine and cosine values.

Flaws

The conversion of polar values to rectangular form is innacurate. It assumes X and Y values change as the angle does in a linear fashion, but this is not the case.

What now?

Using calculus, I should be able to determine the relationship between the angle and unit circle coordinates. I can then incorporate it into my polar -> rectangular coordinate conversion code, and acheive better accuracy.

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);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment