Skip to content

Instantly share code, notes, and snippets.

@flar
Last active November 29, 2023 23:29
Show Gist options
  • Save flar/60b95e86c0d641be1d7505d75605fe48 to your computer and use it in GitHub Desktop.
Save flar/60b95e86c0d641be1d7505d75605fe48 to your computer and use it in GitHub Desktop.
Dart program to compute divisions of a quarter circle based on a pixel error metric.
import 'dart:math';
class Point {
Point(this.x, this.y);
double x;
double y;
operator+(Point p) { return Point(x + p.x, y + p.y); }
operator*(double v) { return Point(x * v, y * v); }
double length() { return sqrt(x * x + y * y); }
String toString() { return 'Point($x, $y)'; }
}
class MinMaxAvg {
MinMaxAvg(this.label);
final String label;
int minValue = 0;
int maxValue = 0;
int total = 0;
int count = 0;
void add(int i) {
minValue = min(minValue, i);
maxValue = max(maxValue, i);
total += i.abs();
count++;
}
void output() {
print('$label: min = $minValue, max = $maxValue, total = $total, avg = ${total / count}');
}
}
main(List<String> arguments) {
double tolerance = 0.1;
if (arguments.length > 0) {
tolerance = double.parse(arguments[0]);
if (tolerance <= 0) {
throw 'tolerance value ($tolerance) must be > 0.0';
}
if (arguments.length > 1) {
throw 'Usage: dart tessellation_divisions.dart <pixel tolerance>';
}
}
int totalApproxDiff = 0;
MinMaxAvg sqrtApproxError = MinMaxAvg('sqrt approximation');
MinMaxAvg acosApproxError = MinMaxAvg('acos approximation');
int valueCount = 0;
for (double radius = 0.25; radius <= 2000; radius += (radius < 5 ? 0.25 : 1)) {
Point start = Point(radius, 0);
for (int divisions = 1; divisions < 200; divisions++) {
// 1 division means the start and end of each quarter circle
// 2 divisions adds the 45 degree point
// etc.
double angle = (pi / 2) * (1.0 / divisions);
Point end = Point(cos(angle) * radius, sin(angle) * radius);
Point midpoint = (start + end) * 0.5;
double length = midpoint.length();
if (length > radius - tolerance) {
print('dividing radius $radius into $divisions slices has deviation ${radius - length}');
double k = tolerance / radius;
int sqrtApproximation = (pi / sqrt(2 * k) / 4).ceil();
int acosApproximation = (pi / acos(1 - k) / 4).ceil();
print('approximations are sqrt version = $sqrtApproximation and acos version = $acosApproximation');
// positive errors are OK, we just generate more divisions
// 0 errors are great!
// negative errors mean our prediction failed to produce enough divisions
sqrtApproxError.add(sqrtApproximation - divisions);
acosApproxError.add(acosApproximation - divisions);
totalApproxDiff += (sqrtApproximation - acosApproximation).abs();
valueCount++;
break;
}
}
}
print('average difference between approximations is ${totalApproxDiff / valueCount}');
sqrtApproxError.output();
acosApproxError.output();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment