Last active
November 29, 2023 23:29
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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