Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Angle parsing #6

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,19 @@ a1.radians; // = 3.1415...
a2.turns; // = 0.5
```

You can also convert strings to angles.

```dart
// Various ways to parse a half turn:
Angle.parse("180deg"); // = 180.0
Angle.parse("180.0°"); // = 180.0
Angle.parse("0.0° 10800.0′ 0.0″"); // = 180.0
Angle.parse("180degrees"); // = 180.0
Angle.parse("pi rad"); // = π
Angle.parse("π radians"); // = π
Angle.parse("200ᵍ"); // = 200ᵍ
Angle.parse("200 gradians"); // = 200ᵍ
```

## Documentation

Expand All @@ -41,6 +54,7 @@ Constructors:
- `Angle.radians(x)`
- `Angle.gradians(x)`
- `Angle.turns(x)`
- `Angle.parse(x)`

Accessors:
- `.degrees()`
Expand Down
50 changes: 50 additions & 0 deletions lib/angle_format.dart
RivMt marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*library angles;

import 'package:angles/angles.dart';

/// Represent angle by given format
///
///
///
/// The following characters are available:
/// ```
/// Symbol Meaning
/// ------ -------------------------
/// r radians
/// d degrees
/// m minutes
/// s seconds
/// D degrees (decimal digits)
/// g gradians
/// t turns
/// ' escape letter
/// ```
class AngleFormat {

final String format;

AngleFormat(this.format);

String format(Angle angle, [String? format]) {
format = format ?? this.format;

}

}

enum AngleFormatFields {
r("r"),
d("d"),
m("m"),
s("s"),
D("D"),
g("g"),
t("t");

final String char;

const AngleFormatFields(this.char);

RegExp get pattern => RegExp("[^']$char+");

}*/
33 changes: 33 additions & 0 deletions lib/angle_type.dart
RivMt marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
part of 'package:angles/angles.dart';

/// Type of angle
///
/// Each type has `expressions` for matching strings. Also it is possible that
/// Creating [Angle] instances via the corresponding [Angle]'s [constructor].
/// The [radix] is a number for conversion to subunits such as minutes and
/// seconds, so `60` for [degrees] and `0` otherwise.
enum AngleType {
// Gradians
gradians([r"(grad(ians?)?|ᵍ)"], Angle.gradians, 0),
// Radians
radians([r"(rad(ians?)?|㎭)"], Angle.radians, 0),
// Degrees
degrees([r"(°|deg(rees?)?|h(ours?)?)", r"(′|'|m(in(utes?)?)?)", r'(″|"|s(ec(onds?)?)?)'], Angle.degrees, 60),
// Unknown
unknown([], Angle.radians, 0);

/// List of expressions
final List<String> expressions;

/// Constructor of [Angle] class.
final Angle Function(double) constructor;

/// Radix number of type.
final int radix;

const AngleType(this.expressions, this.constructor, this.radix);

/// [RegExp] pattern to match strings
RegExp get pattern => RegExp(this.expressions.join("|"));

}
30 changes: 30 additions & 0 deletions lib/angles.dart
jim-ec marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import 'dart:math' as math;

import 'package:meta/meta.dart';

part 'package:angles/angle_type.dart';

/// Represents an angle.
/// The unit used for construction does not matter, as angles internally
/// always use radians.
Expand Down Expand Up @@ -64,6 +66,34 @@ class Angle implements Comparable<Angle> {
/// Create an angle defining one eighth turn.
factory Angle.eighthTurn() => Angle.turns(0.125);

/// Parse [string] to angle. You can specify [type] such as [AngleType.degrees].
factory Angle.parse(String string, [AngleType? type]) {
// Remove space
string = string.replaceAll(" ", "");
// Find type when `sep` is null
type = type ?? AngleType.values.firstWhere((AngleType s) => string.contains(s.pattern), orElse: () => AngleType.unknown);
// Split by pattern
final List<String> splits = (type == AngleType.unknown) ? [string] : string.split(type.pattern);
// Convert
double value = 0.0;
for(int i=0; i < splits.length; i++) {
double multiplier = 1;
final pi = RegExp(r"π|(pi)");
if (splits[i].contains(pi)) {
multiplier = math.pi;
splits[i] = splits[i].replaceAll(pi, "");
if (splits[i].isEmpty) {
splits[i] = "1";
}
}
if (splits[i] == "") {
break;
}
value += double.parse(splits[i]) * multiplier / math.pow(type.radix, i);
}
return type.constructor(value);
}

/// Create an angle by computing the arc sine of [c].
Angle.asin(final double c) : _storage = math.asin(c);

Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ homepage: https://github.com/Jim-Eckerlein/dart-angles
version: 2.1.1

environment:
sdk: '>=2.12.0 <3.0.0'
sdk: '>=2.17.0 <3.0.0'

dependencies:
meta: ^1.3.0
Expand Down
22 changes: 22 additions & 0 deletions test/angles_test.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'dart:math' as math;
import 'package:test/test.dart';

import 'package:angles/angles.dart';
Expand Down Expand Up @@ -144,4 +145,25 @@ void main() {
expect(Angle.fromTurns(45), Angle.turns(45));
});
});
group("Parse", () {
test("Radians", () {
expect(Angle.parse("pi"), Angle.degrees(180.0));
expect(Angle.parse("πrad"), Angle.degrees(180.0));
expect(Angle.parse("2π㎭"), Angle.degrees(360.0));
expect(Angle.parse("2π"), Angle.degrees(360.0));
expect(Angle.parse("0.5pi radians"), Angle.degrees(90.0));
});
test("Degrees", () {
expect(Angle.parse("180deg"), Angle.degrees(180.0));
expect(Angle.parse("180° 0′ 0″"), Angle.degrees(180.0));
expect(Angle.parse("90h 30m 0s"), Angle.degrees(90.5));
expect(Angle.parse("360pi degrees"), Angle.degrees(360.0 * math.pi));
});
test("Gradians", () {
expect(Angle.parse("200grad"), Angle.degrees(180.0));
expect(Angle.parse("300gradians"), Angle.degrees(270.0));
expect(Angle.parse("400piᵍ"), Angle.degrees(360 * math.pi));
expect(Angle.parse("400πgradian"), Angle.degrees(360.0 * math.pi));
});
});
}