Update tests
This commit is contained in:
parent
62c488740e
commit
e03a34905e
@ -31,11 +31,11 @@ const _commands = {
|
||||
final _commandPattern = RegExp("(?=[${_commands.join('')}])");
|
||||
final _floatPattern = RegExp(r"^[-+]?[0-9]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?");
|
||||
|
||||
class _ParserResult<T> {
|
||||
class ParserResult<T> {
|
||||
final T value;
|
||||
final String remaining;
|
||||
|
||||
const _ParserResult({required this.value, required this.remaining});
|
||||
const ParserResult({required this.value, required this.remaining});
|
||||
}
|
||||
|
||||
class InvalidPathError implements Exception {
|
||||
@ -73,7 +73,7 @@ String _stripArray(String stringToParse) {
|
||||
return stringToParse;
|
||||
}
|
||||
|
||||
_ParserResult<num> _parseNumber(String stringToParse) {
|
||||
ParserResult<num> _parseNumber(String stringToParse) {
|
||||
final res = _floatPattern.firstMatch(stringToParse);
|
||||
if (res == null) {
|
||||
throw InvalidPathError("Expected a number, got '$stringToParse'.");
|
||||
@ -86,10 +86,10 @@ _ParserResult<num> _parseNumber(String stringToParse) {
|
||||
stringToParse.substring(0, start) + stringToParse.substring(end);
|
||||
stringToParse = _stripArray(stringToParse);
|
||||
|
||||
return _ParserResult(value: number, remaining: stringToParse);
|
||||
return ParserResult(value: number, remaining: stringToParse);
|
||||
}
|
||||
|
||||
_ParserResult<num> _parseUnsignedNumber(String stringToParse) {
|
||||
ParserResult<num> _parseUnsignedNumber(String stringToParse) {
|
||||
final number = _parseNumber(stringToParse);
|
||||
if (number.value < 0) {
|
||||
throw InvalidPathError("Expected a non-negative number, got '$number'.");
|
||||
@ -97,18 +97,18 @@ _ParserResult<num> _parseUnsignedNumber(String stringToParse) {
|
||||
return number;
|
||||
}
|
||||
|
||||
_ParserResult<Point> _parseCoordinatePair(String stringToParse) {
|
||||
ParserResult<Point> _parseCoordinatePair(String stringToParse) {
|
||||
final x = _parseNumber(stringToParse);
|
||||
final y = _parseNumber(x.remaining);
|
||||
return _ParserResult(value: Point(x.value, y.value), remaining: y.remaining);
|
||||
return ParserResult(value: Point(x.value, y.value), remaining: y.remaining);
|
||||
}
|
||||
|
||||
_ParserResult<bool> _parseflag(String stringToParse) {
|
||||
ParserResult<bool> _parseflag(String stringToParse) {
|
||||
final flag = stringToParse[0];
|
||||
stringToParse = stringToParse.substring(1);
|
||||
stringToParse = _stripArray(stringToParse);
|
||||
if (flag == '0') return _ParserResult(value: false, remaining: stringToParse);
|
||||
if (flag == '1') return _ParserResult(value: true, remaining: stringToParse);
|
||||
if (flag == '0') return ParserResult(value: false, remaining: stringToParse);
|
||||
if (flag == '1') return ParserResult(value: true, remaining: stringToParse);
|
||||
|
||||
throw InvalidPathError("Expected either 1 or 0, got '$flag'");
|
||||
}
|
||||
@ -120,19 +120,26 @@ const fieldParsers = {
|
||||
"f": _parseflag,
|
||||
};
|
||||
|
||||
class _Command {
|
||||
class Command {
|
||||
final String command;
|
||||
final String args;
|
||||
|
||||
const _Command({required this.command, required this.args});
|
||||
const Command({required this.command, required this.args});
|
||||
|
||||
@override
|
||||
String toString() => 'Command: $command $args';
|
||||
bool operator ==(Object other) =>
|
||||
other is Command && command == other.command && args == other.args;
|
||||
|
||||
@override
|
||||
int get hashCode => command.hashCode ^ args.hashCode;
|
||||
|
||||
@override
|
||||
String toString() => '$command $args';
|
||||
}
|
||||
|
||||
// Splits path into commands and arguments
|
||||
List<_Command> _commandifyPath(String pathdef) {
|
||||
List<_Command> tokens = [];
|
||||
List<Command> commandifyPath(String pathdef) {
|
||||
List<Command> tokens = [];
|
||||
List<String> token = [];
|
||||
for (String c in pathdef.split(_commandPattern)) {
|
||||
String x = c[0];
|
||||
@ -142,7 +149,7 @@ List<_Command> _commandifyPath(String pathdef) {
|
||||
throw InvalidPathError("Path does not start with a command: $pathdef");
|
||||
}
|
||||
if (token.isNotEmpty) {
|
||||
tokens.add(_Command(command: token[0], args: token[1]));
|
||||
tokens.add(Command(command: token[0], args: token[1]));
|
||||
// yield token;
|
||||
}
|
||||
if (x == "z" || x == "Z") {
|
||||
@ -156,7 +163,7 @@ List<_Command> _commandifyPath(String pathdef) {
|
||||
token.add(y);
|
||||
}
|
||||
}
|
||||
tokens.add(_Command(command: token[0], args: token[1]));
|
||||
tokens.add(Command(command: token[0], args: token[1]));
|
||||
// yield token;
|
||||
return tokens;
|
||||
}
|
||||
@ -168,12 +175,23 @@ class Token {
|
||||
const Token({required this.command, required this.args});
|
||||
|
||||
@override
|
||||
String toString() => 'Token: $command ($args)';
|
||||
bool operator ==(Object other) =>
|
||||
other is Token &&
|
||||
command == other.command &&
|
||||
args.length == other.args.length &&
|
||||
![for (int i = 0; i < args.length; i++) args[i] == other.args[i]]
|
||||
.any((b) => !b);
|
||||
|
||||
@override
|
||||
int get hashCode => command.hashCode ^ args.hashCode;
|
||||
|
||||
@override
|
||||
String toString() => '$command ($args)';
|
||||
}
|
||||
|
||||
List<Token> _tokenizePath(String pathdef) {
|
||||
List<Token> tokenizePath(String pathdef) {
|
||||
List<Token> tokens = [];
|
||||
for (final token in _commandifyPath(pathdef)) {
|
||||
for (final token in commandifyPath(pathdef)) {
|
||||
String command = token.command;
|
||||
String args = token.args;
|
||||
|
||||
@ -218,7 +236,7 @@ Path parsePath(String pathdef) {
|
||||
String? lastCommand;
|
||||
Point currentPos = Point.zero;
|
||||
|
||||
for (final token in _tokenizePath(pathdef)) {
|
||||
for (final token in tokenizePath(pathdef)) {
|
||||
final command = token.command.toUpperCase();
|
||||
final absolute = token.command.toUpperCase() == token.command;
|
||||
if (command == "M") {
|
||||
|
@ -2,7 +2,7 @@ import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:kanimaji/svg/parser.dart';
|
||||
|
||||
void main() {
|
||||
test('Test SVG Paths', () {
|
||||
test('Test generating SVG path strings', () {
|
||||
final paths = [
|
||||
"M 100,100 L 300,100 L 200,300 Z",
|
||||
"M 0,0 L 50,20 M 100,100 L 300,100 L 200,300 Z",
|
@ -4,353 +4,416 @@ import 'package:kanimaji/svg/parser.dart';
|
||||
import 'package:kanimaji/svg/path.dart';
|
||||
|
||||
void main() {
|
||||
/// """Examples from the SVG spec"""
|
||||
test("svg_examples", () {
|
||||
Path path1 = parsePath("M 100 100 L 300 100 L 200 300 z");
|
||||
expect(
|
||||
path1,
|
||||
Path.fromSegments([
|
||||
const Move(to: Point(100, 100)),
|
||||
const Line(start: Point(100, 100), end: Point(300, 100)),
|
||||
const Line(start: Point(300, 100), end: Point(200, 300)),
|
||||
const Close(start: Point(200, 300), end: Point(100, 100)),
|
||||
]),
|
||||
group("Examples from the SVG spec", () {
|
||||
test(
|
||||
"[Path 1]: MLLz",
|
||||
() => expect(
|
||||
parsePath("M 100 100 L 300 100 L 200 300 z"),
|
||||
Path.fromSegments(const [
|
||||
Move(to: Point(100, 100)),
|
||||
Line(start: Point(100, 100), end: Point(300, 100)),
|
||||
Line(start: Point(300, 100), end: Point(200, 300)),
|
||||
Close(start: Point(200, 300), end: Point(100, 100)),
|
||||
]),
|
||||
));
|
||||
|
||||
//
|
||||
test(
|
||||
"[Path 2]: MLMLLz (Z command behavior when there is multiple subpaths)",
|
||||
() => expect(
|
||||
parsePath("M 0 0 L 50 20 M 100 100 L 300 100 L 200 300 z"),
|
||||
Path.fromSegments(const [
|
||||
Move(to: Point.zero),
|
||||
Line(start: Point.zero, end: Point(50, 20)),
|
||||
Move(to: Point(100, 100)),
|
||||
Line(start: Point(100, 100), end: Point(300, 100)),
|
||||
Line(start: Point(300, 100), end: Point(200, 300)),
|
||||
Close(start: Point(200, 300), end: Point(100, 100)),
|
||||
]),
|
||||
),
|
||||
);
|
||||
|
||||
// for Z command behavior when there is multiple subpaths
|
||||
path1 = parsePath("M 0 0 L 50 20 M 100 100 L 300 100 L 200 300 z");
|
||||
expect(
|
||||
path1,
|
||||
Path.fromSegments(const [
|
||||
Move(to: Point.zero),
|
||||
Line(start: Point.zero, end: Point(50, 20)),
|
||||
Move(to: Point(100, 100)),
|
||||
Line(start: Point(100, 100), end: Point(300, 100)),
|
||||
Line(start: Point(300, 100), end: Point(200, 300)),
|
||||
Close(start: Point(200, 300), end: Point(100, 100)),
|
||||
]),
|
||||
test(
|
||||
"[Path 3]: ML",
|
||||
() => expect(
|
||||
parsePath("M 100 100 L 200 200"),
|
||||
parsePath("M100 100L200 200"),
|
||||
),
|
||||
);
|
||||
|
||||
path1 = parsePath("M 100 100 L 200 200");
|
||||
Path path2 = parsePath("M100 100L200 200");
|
||||
expect(path1, path2);
|
||||
|
||||
path1 = parsePath("M 100 200 L 200 100 L -100 -200");
|
||||
path2 = parsePath("M 100 200 L 200 100 -100 -200");
|
||||
expect(path1, path2);
|
||||
|
||||
path1 = parsePath("""M100,200 C100,100 250,100 250,200
|
||||
S400,300 400,200""");
|
||||
expect(
|
||||
path1,
|
||||
Path.fromSegments(const [
|
||||
Move(to: Point(100, 200)),
|
||||
CubicBezier(
|
||||
start: Point(100, 200),
|
||||
control1: Point(100, 100),
|
||||
control2: Point(250, 100),
|
||||
end: Point(250, 200),
|
||||
),
|
||||
CubicBezier(
|
||||
start: Point(250, 200),
|
||||
control1: Point(250, 300),
|
||||
control2: Point(400, 300),
|
||||
end: Point(400, 200),
|
||||
),
|
||||
]),
|
||||
test(
|
||||
"[Path 4]: MLL",
|
||||
() => expect(
|
||||
parsePath("M 100 200 L 200 100 L -100 -200"),
|
||||
parsePath("M 100 200 L 200 100 -100 -200"),
|
||||
),
|
||||
);
|
||||
|
||||
path1 = parsePath("M100,200 C100,100 400,100 400,200");
|
||||
expect(
|
||||
path1,
|
||||
Path.fromSegments(const [
|
||||
Move(to: Point(100, 200)),
|
||||
CubicBezier(
|
||||
start: Point(100, 200),
|
||||
control1: Point(100, 100),
|
||||
control2: Point(400, 100),
|
||||
end: Point(400, 200),
|
||||
),
|
||||
]),
|
||||
test(
|
||||
"[Path 5]: MCS",
|
||||
() => expect(
|
||||
parsePath("""M100,200 C100,100 250,100 250,200
|
||||
S400,300 400,200"""),
|
||||
Path.fromSegments(const [
|
||||
Move(to: Point(100, 200)),
|
||||
CubicBezier(
|
||||
start: Point(100, 200),
|
||||
control1: Point(100, 100),
|
||||
control2: Point(250, 100),
|
||||
end: Point(250, 200),
|
||||
),
|
||||
CubicBezier(
|
||||
start: Point(250, 200),
|
||||
control1: Point(250, 300),
|
||||
control2: Point(400, 300),
|
||||
end: Point(400, 200),
|
||||
),
|
||||
]),
|
||||
),
|
||||
);
|
||||
|
||||
path1 = parsePath("M100,500 C25,400 475,400 400,500");
|
||||
expect(
|
||||
path1,
|
||||
Path.fromSegments(const [
|
||||
Move(to: Point(100, 500)),
|
||||
CubicBezier(
|
||||
start: Point(100, 500),
|
||||
control1: Point(25, 400),
|
||||
control2: Point(475, 400),
|
||||
end: Point(400, 500),
|
||||
),
|
||||
]),
|
||||
test(
|
||||
"[Path 6]: MC",
|
||||
() => expect(
|
||||
parsePath("M100,200 C100,100 400,100 400,200"),
|
||||
Path.fromSegments(const [
|
||||
Move(to: Point(100, 200)),
|
||||
CubicBezier(
|
||||
start: Point(100, 200),
|
||||
control1: Point(100, 100),
|
||||
control2: Point(400, 100),
|
||||
end: Point(400, 200),
|
||||
),
|
||||
]),
|
||||
),
|
||||
);
|
||||
|
||||
path1 = parsePath("M100,800 C175,700 325,700 400,800");
|
||||
expect(
|
||||
path1,
|
||||
Path.fromSegments(const [
|
||||
Move(to: Point(100, 800)),
|
||||
CubicBezier(
|
||||
start: Point(100, 800),
|
||||
control1: Point(175, 700),
|
||||
control2: Point(325, 700),
|
||||
end: Point(400, 800),
|
||||
),
|
||||
]),
|
||||
test(
|
||||
"[Path 7]: MC",
|
||||
() => expect(
|
||||
parsePath("M100,500 C25,400 475,400 400,500"),
|
||||
Path.fromSegments(const [
|
||||
Move(to: Point(100, 500)),
|
||||
CubicBezier(
|
||||
start: Point(100, 500),
|
||||
control1: Point(25, 400),
|
||||
control2: Point(475, 400),
|
||||
end: Point(400, 500),
|
||||
),
|
||||
]),
|
||||
),
|
||||
);
|
||||
|
||||
path1 = parsePath("M600,200 C675,100 975,100 900,200");
|
||||
expect(
|
||||
path1,
|
||||
Path.fromSegments(const [
|
||||
Move(to: Point(600, 200)),
|
||||
CubicBezier(
|
||||
start: Point(600, 200),
|
||||
control1: Point(675, 100),
|
||||
control2: Point(975, 100),
|
||||
end: Point(900, 200),
|
||||
),
|
||||
]),
|
||||
test(
|
||||
"[Path 8]: MC",
|
||||
() => expect(
|
||||
parsePath("M100,800 C175,700 325,700 400,800"),
|
||||
Path.fromSegments(const [
|
||||
Move(to: Point(100, 800)),
|
||||
CubicBezier(
|
||||
start: Point(100, 800),
|
||||
control1: Point(175, 700),
|
||||
control2: Point(325, 700),
|
||||
end: Point(400, 800),
|
||||
),
|
||||
]),
|
||||
),
|
||||
);
|
||||
|
||||
path1 = parsePath("M600,500 C600,350 900,650 900,500");
|
||||
expect(
|
||||
path1,
|
||||
Path.fromSegments(const [
|
||||
Move(to: Point(600, 500)),
|
||||
CubicBezier(
|
||||
start: Point(600, 500),
|
||||
control1: Point(600, 350),
|
||||
control2: Point(900, 650),
|
||||
end: Point(900, 500),
|
||||
),
|
||||
]),
|
||||
test(
|
||||
"[Path 9]: MC",
|
||||
() => expect(
|
||||
parsePath("M600,200 C675,100 975,100 900,200"),
|
||||
Path.fromSegments(const [
|
||||
Move(to: Point(600, 200)),
|
||||
CubicBezier(
|
||||
start: Point(600, 200),
|
||||
control1: Point(675, 100),
|
||||
control2: Point(975, 100),
|
||||
end: Point(900, 200),
|
||||
),
|
||||
]),
|
||||
),
|
||||
);
|
||||
|
||||
path1 = parsePath("""M600,800 C625,700 725,700 750,800
|
||||
S875,900 900,800""");
|
||||
expect(
|
||||
path1,
|
||||
Path.fromSegments(const [
|
||||
Move(to: Point(600, 800)),
|
||||
CubicBezier(
|
||||
start: Point(600, 800),
|
||||
control1: Point(625, 700),
|
||||
control2: Point(725, 700),
|
||||
end: Point(750, 800)),
|
||||
CubicBezier(
|
||||
start: Point(750, 800),
|
||||
control1: Point(775, 900),
|
||||
control2: Point(875, 900),
|
||||
end: Point(900, 800),
|
||||
),
|
||||
]),
|
||||
test(
|
||||
"[Path 10]: MC",
|
||||
() => expect(
|
||||
parsePath("M600,500 C600,350 900,650 900,500"),
|
||||
Path.fromSegments(const [
|
||||
Move(to: Point(600, 500)),
|
||||
CubicBezier(
|
||||
start: Point(600, 500),
|
||||
control1: Point(600, 350),
|
||||
control2: Point(900, 650),
|
||||
end: Point(900, 500),
|
||||
),
|
||||
]),
|
||||
),
|
||||
);
|
||||
|
||||
path1 = parsePath("M200,300 Q400,50 600,300 T1000,300");
|
||||
expect(
|
||||
path1,
|
||||
Path.fromSegments(const [
|
||||
Move(to: Point(200, 300)),
|
||||
QuadraticBezier(
|
||||
start: Point(200, 300),
|
||||
control: Point(400, 50),
|
||||
end: Point(600, 300),
|
||||
),
|
||||
QuadraticBezier(
|
||||
start: Point(600, 300),
|
||||
control: Point(800, 550),
|
||||
end: Point(1000, 300),
|
||||
),
|
||||
]),
|
||||
test(
|
||||
"[Path 11]: MCS",
|
||||
() => expect(
|
||||
parsePath("""M600,800 C625,700 725,700 750,800
|
||||
S875,900 900,800"""),
|
||||
Path.fromSegments(const [
|
||||
Move(to: Point(600, 800)),
|
||||
CubicBezier(
|
||||
start: Point(600, 800),
|
||||
control1: Point(625, 700),
|
||||
control2: Point(725, 700),
|
||||
end: Point(750, 800)),
|
||||
CubicBezier(
|
||||
start: Point(750, 800),
|
||||
control1: Point(775, 900),
|
||||
control2: Point(875, 900),
|
||||
end: Point(900, 800),
|
||||
),
|
||||
]),
|
||||
),
|
||||
);
|
||||
|
||||
path1 = parsePath("M300,200 h-150 a150,150 0 1,0 150,-150 z");
|
||||
expect(
|
||||
path1,
|
||||
Path.fromSegments(const [
|
||||
Move(to: Point(300, 200)),
|
||||
Line(start: Point(300, 200), end: Point(150, 200)),
|
||||
Arc(
|
||||
start: Point(150, 200),
|
||||
radius: Point(150, 150),
|
||||
rotation: 0,
|
||||
arc: true,
|
||||
sweep: false,
|
||||
end: Point(300, 50),
|
||||
),
|
||||
Close(start: Point(300, 50), end: Point(300, 200)),
|
||||
]),
|
||||
test(
|
||||
"[Path 12]: MQT",
|
||||
() => expect(
|
||||
parsePath("M200,300 Q400,50 600,300 T1000,300"),
|
||||
Path.fromSegments(const [
|
||||
Move(to: Point(200, 300)),
|
||||
QuadraticBezier(
|
||||
start: Point(200, 300),
|
||||
control: Point(400, 50),
|
||||
end: Point(600, 300),
|
||||
),
|
||||
QuadraticBezier(
|
||||
start: Point(600, 300),
|
||||
control: Point(800, 550),
|
||||
end: Point(1000, 300),
|
||||
),
|
||||
]),
|
||||
),
|
||||
);
|
||||
|
||||
path1 = parsePath("M275,175 v-150 a150,150 0 0,0 -150,150 z");
|
||||
expect(
|
||||
path1,
|
||||
Path.fromSegments(const [
|
||||
Move(to: Point(275, 175)),
|
||||
Line(start: Point(275, 175), end: Point(275, 25)),
|
||||
Arc(
|
||||
start: Point(275, 25),
|
||||
radius: Point(150, 150),
|
||||
rotation: 0,
|
||||
arc: false,
|
||||
sweep: false,
|
||||
end: Point(125, 175),
|
||||
),
|
||||
Close(start: Point(125, 175), end: Point(275, 175)),
|
||||
]),
|
||||
test(
|
||||
"[Path 13]: Mhaz",
|
||||
() => expect(
|
||||
parsePath("M300,200 h-150 a150,150 0 1,0 150,-150 z"),
|
||||
Path.fromSegments(const [
|
||||
Move(to: Point(300, 200)),
|
||||
Line(start: Point(300, 200), end: Point(150, 200)),
|
||||
Arc(
|
||||
start: Point(150, 200),
|
||||
radius: Point(150, 150),
|
||||
rotation: 0,
|
||||
arc: true,
|
||||
sweep: false,
|
||||
end: Point(300, 50),
|
||||
),
|
||||
Close(start: Point(300, 50), end: Point(300, 200)),
|
||||
]),
|
||||
),
|
||||
);
|
||||
|
||||
path1 = parsePath("M275,175 v-150 a150,150 0 0,0 -150,150 L 275,175 z");
|
||||
expect(
|
||||
path1,
|
||||
Path.fromSegments(const [
|
||||
Move(to: Point(275, 175)),
|
||||
Line(start: Point(275, 175), end: Point(275, 25)),
|
||||
Arc(
|
||||
start: Point(275, 25),
|
||||
radius: Point(150, 150),
|
||||
rotation: 0,
|
||||
arc: false,
|
||||
sweep: false,
|
||||
end: Point(125, 175),
|
||||
),
|
||||
Line(start: Point(125, 175), end: Point(275, 175)),
|
||||
Close(start: Point(275, 175), end: Point(275, 175)),
|
||||
]),
|
||||
test(
|
||||
"[Path 14]: Mvaz",
|
||||
() => expect(
|
||||
parsePath("M275,175 v-150 a150,150 0 0,0 -150,150 z"),
|
||||
Path.fromSegments(const [
|
||||
Move(to: Point(275, 175)),
|
||||
Line(start: Point(275, 175), end: Point(275, 25)),
|
||||
Arc(
|
||||
start: Point(275, 25),
|
||||
radius: Point(150, 150),
|
||||
rotation: 0,
|
||||
arc: false,
|
||||
sweep: false,
|
||||
end: Point(125, 175),
|
||||
),
|
||||
Close(start: Point(125, 175), end: Point(275, 175)),
|
||||
]),
|
||||
),
|
||||
);
|
||||
|
||||
path1 = parsePath("""M600,350 l 50,-25
|
||||
test(
|
||||
"[Path 15]: MvaLz",
|
||||
() => expect(
|
||||
parsePath("M275,175 v-150 a150,150 0 0,0 -150,150 L 275,175 z"),
|
||||
Path.fromSegments(const [
|
||||
Move(to: Point(275, 175)),
|
||||
Line(start: Point(275, 175), end: Point(275, 25)),
|
||||
Arc(
|
||||
start: Point(275, 25),
|
||||
radius: Point(150, 150),
|
||||
rotation: 0,
|
||||
arc: false,
|
||||
sweep: false,
|
||||
end: Point(125, 175),
|
||||
),
|
||||
Line(start: Point(125, 175), end: Point(275, 175)),
|
||||
Close(start: Point(275, 175), end: Point(275, 175)),
|
||||
]),
|
||||
),
|
||||
);
|
||||
|
||||
test(
|
||||
"[Path 16]: Mlalalalal",
|
||||
() => expect(
|
||||
parsePath("""M600,350 l 50,-25
|
||||
a25,25 -30 0,1 50,-25 l 50,-25
|
||||
a25,50 -30 0,1 50,-25 l 50,-25
|
||||
a25,75 -30 0,1 50,-25 l 50,-25
|
||||
a25,100 -30 0,1 50,-25 l 50,-25""");
|
||||
expect(
|
||||
path1,
|
||||
Path.fromSegments(const [
|
||||
Move(to: Point(600, 350)),
|
||||
Line(start: Point(600, 350), end: Point(650, 325)),
|
||||
Arc(
|
||||
start: Point(650, 325),
|
||||
radius: Point(25, 25),
|
||||
rotation: -30,
|
||||
arc: false,
|
||||
sweep: true,
|
||||
end: Point(700, 300),
|
||||
),
|
||||
Line(start: Point(700, 300), end: Point(750, 275)),
|
||||
Arc(
|
||||
start: Point(750, 275),
|
||||
radius: Point(25, 50),
|
||||
rotation: -30,
|
||||
arc: false,
|
||||
sweep: true,
|
||||
end: Point(800, 250),
|
||||
),
|
||||
Line(start: Point(800, 250), end: Point(850, 225)),
|
||||
Arc(
|
||||
start: Point(850, 225),
|
||||
radius: Point(25, 75),
|
||||
rotation: -30,
|
||||
arc: false,
|
||||
sweep: true,
|
||||
end: Point(900, 200),
|
||||
),
|
||||
Line(start: Point(900, 200), end: Point(950, 175)),
|
||||
Arc(
|
||||
start: Point(950, 175),
|
||||
radius: Point(25, 100),
|
||||
rotation: -30,
|
||||
arc: false,
|
||||
sweep: true,
|
||||
end: Point(1000, 150),
|
||||
),
|
||||
Line(start: Point(1000, 150), end: Point(1050, 125)),
|
||||
]),
|
||||
a25,100 -30 0,1 50,-25 l 50,-25"""),
|
||||
Path.fromSegments(const [
|
||||
Move(to: Point(600, 350)),
|
||||
Line(start: Point(600, 350), end: Point(650, 325)),
|
||||
Arc(
|
||||
start: Point(650, 325),
|
||||
radius: Point(25, 25),
|
||||
rotation: -30,
|
||||
arc: false,
|
||||
sweep: true,
|
||||
end: Point(700, 300),
|
||||
),
|
||||
Line(start: Point(700, 300), end: Point(750, 275)),
|
||||
Arc(
|
||||
start: Point(750, 275),
|
||||
radius: Point(25, 50),
|
||||
rotation: -30,
|
||||
arc: false,
|
||||
sweep: true,
|
||||
end: Point(800, 250),
|
||||
),
|
||||
Line(start: Point(800, 250), end: Point(850, 225)),
|
||||
Arc(
|
||||
start: Point(850, 225),
|
||||
radius: Point(25, 75),
|
||||
rotation: -30,
|
||||
arc: false,
|
||||
sweep: true,
|
||||
end: Point(900, 200),
|
||||
),
|
||||
Line(start: Point(900, 200), end: Point(950, 175)),
|
||||
Arc(
|
||||
start: Point(950, 175),
|
||||
radius: Point(25, 100),
|
||||
rotation: -30,
|
||||
arc: false,
|
||||
sweep: true,
|
||||
end: Point(1000, 150),
|
||||
),
|
||||
Line(start: Point(1000, 150), end: Point(1050, 125)),
|
||||
]),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
// def test_others(self):
|
||||
// # Other paths that need testing:
|
||||
group("Other paths that need testing", () {
|
||||
test(
|
||||
"Relative moveto",
|
||||
() => expect(
|
||||
parsePath("M 0 0 L 50 20 m 50 80 L 300 100 L 200 300 z"),
|
||||
Path.fromSegments(const [
|
||||
Move(to: Point.zero),
|
||||
Line(start: Point(0, 0), end: Point(50, 20)),
|
||||
Move(to: Point(100, 100)),
|
||||
Line(start: Point(100, 100), end: Point(300, 100)),
|
||||
Line(start: Point(300, 100), end: Point(200, 300)),
|
||||
Close(start: Point(200, 300), end: Point(100, 100)),
|
||||
]),
|
||||
),
|
||||
);
|
||||
|
||||
// # Relative moveto:
|
||||
// path1 = parsePath("M 0 0 L 50 20 m 50 80 L 300 100 L 200 300 z")
|
||||
// expect(
|
||||
// path1,
|
||||
// Path.fromSegments(const [
|
||||
// Move(0j),
|
||||
// Line(start: Point(0, 0), end: Point(50, 20)),
|
||||
// Move(to: Point(100, 100)),
|
||||
// Line(start: Point(100, 100), end: Point(300, 100)),
|
||||
// Line(start: Point(300, 100), end: Point(200, 300)),
|
||||
// Close(Point(200, 300), Point(100, 100)),
|
||||
// ),
|
||||
// )
|
||||
//
|
||||
test(
|
||||
"Initial smooth and relative CubicBezier",
|
||||
() => expect(
|
||||
parsePath("""M100,200 s 150,-100 150,0"""),
|
||||
Path.fromSegments(const [
|
||||
Move(to: Point(100, 200)),
|
||||
CubicBezier(
|
||||
start: Point(100, 200),
|
||||
control1: Point(100, 200),
|
||||
control2: Point(250, 100),
|
||||
end: Point(250, 200),
|
||||
),
|
||||
]),
|
||||
),
|
||||
);
|
||||
|
||||
// # Initial smooth and relative CubicBezier
|
||||
// path1 = parsePath("""M100,200 s 150,-100 150,0""")
|
||||
// expect(
|
||||
// path1,
|
||||
// Path.fromSegments(const [
|
||||
// Move(to: Point(100, 200)),
|
||||
// CubicBezier(start: Point(100, 200), control1: Point(100, 200), control2: Point(250, 100), end: Point(250, 200),),
|
||||
// ),
|
||||
// )
|
||||
test(
|
||||
"Initial smooth and relative QuadraticBezier",
|
||||
() => expect(
|
||||
parsePath("""M100,200 t 150,0"""),
|
||||
Path.fromSegments(const [
|
||||
Move(to: Point(100, 200)),
|
||||
QuadraticBezier(
|
||||
start: Point(100, 200),
|
||||
control: Point(100, 200),
|
||||
end: Point(250, 200)),
|
||||
]),
|
||||
),
|
||||
);
|
||||
|
||||
// # Initial smooth and relative QuadraticBezier
|
||||
// path1 = parsePath("""M100,200 t 150,0""")
|
||||
// expect(
|
||||
// path1,
|
||||
// Path.fromSegments(const [Move(Point(100, 200)), QuadraticBezier(start: Point(100, 200), control: Point(100, 200), end: Point(250, 200)),),
|
||||
// )
|
||||
test(
|
||||
"Relative QuadraticBezier",
|
||||
() => expect(
|
||||
parsePath("""M100,200 q 0,0 150,0"""),
|
||||
Path.fromSegments(const [
|
||||
Move(to: Point(100, 200)),
|
||||
QuadraticBezier(
|
||||
start: Point(100, 200),
|
||||
control: Point(100, 200),
|
||||
end: Point(250, 200)),
|
||||
]),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
// # Relative QuadraticBezier
|
||||
// path1 = parsePath("""M100,200 q 0,0 150,0""")
|
||||
// expect(
|
||||
// path1,
|
||||
// Path.fromSegments(const [Move(Point(100, 200)), QuadraticBezier(start: Point(100, 200), control: Point(100, 200), end: Point(250, 200)),),
|
||||
// )
|
||||
test(
|
||||
"You don't need spaces before a minus-sign",
|
||||
() => expect(
|
||||
parsePath("M100,200c10-5,20-10,30-20"),
|
||||
parsePath("M 100 200 c 10 -5 20 -10 30 -20"),
|
||||
),
|
||||
);
|
||||
|
||||
// def test_negative(self):
|
||||
// """You don't need spaces before a minus-sign"""
|
||||
// path1 = parsePath("M100,200c10-5,20-10,30-20")
|
||||
// path2 = parsePath("M 100 200 c 10 -5 20 -10 30 -20")
|
||||
// expect(path1, path2)
|
||||
test(
|
||||
'Exponents and other number format cases',
|
||||
() =>
|
||||
// It can be e or E, the plus is optional, and a minimum of +/-3.4e38 must be supported.
|
||||
expect(
|
||||
parsePath("M-3.4e38 3.4E+38L-3.4E-38,3.4e-38"),
|
||||
Path.fromSegments(const [
|
||||
Move(to: Point(-3.4e38, 3.4e38)),
|
||||
Line(start: Point(-3.4e38, 3.4e38), end: Point(-3.4e-38, 3.4e-38))
|
||||
]),
|
||||
),
|
||||
);
|
||||
|
||||
// def test_numbers(self):
|
||||
// """Exponents and other number format cases"""
|
||||
// # It can be e or E, the plus is optional, and a minimum of +/-3.4e38 must be supported.
|
||||
// path1 = parsePath("M-3.4e38 3.4E+38L-3.4E-38,3.4e-38")
|
||||
// path2 = Path.fromSegments(const [
|
||||
// Move(-3.4e38 + 3.4e38j), Line(-3.4e38 + 3.4e38j, -3.4e-38 + 3.4e-38j)
|
||||
// )
|
||||
// expect(path1, path2)
|
||||
test(
|
||||
'Errors',
|
||||
() => expect(
|
||||
parsePath("M 100 100 L 200 200 Z 100 200"),
|
||||
throwsA(const TypeMatcher<InvalidPathError>()),
|
||||
),
|
||||
);
|
||||
|
||||
// def test_errors(self):
|
||||
// self.assertRaises(ValueError, parsePath, "M 100 100 L 200 200 Z 100 200")
|
||||
test(
|
||||
"Nonpath: It's possible in SVG to create paths that has zero length, "
|
||||
'we need to handle that.',
|
||||
() => expect(parsePath("M10.236,100.184").d(), "M 10.236,100.184"),
|
||||
);
|
||||
|
||||
// def test_non_path(self):
|
||||
// # It's possible in SVG to create paths that has zero length,
|
||||
// # we need to handle that.
|
||||
|
||||
// path = parsePath("M10.236,100.184")
|
||||
// expect(path.d(), "M 10.236,100.184")
|
||||
|
||||
// def test_issue_45(self):
|
||||
// path = parsePath(
|
||||
// "m 1672.2372,-54.8161 "
|
||||
// "a 14.5445,14.5445 0 0 0 -11.3152,23.6652 "
|
||||
// "l 27.2573,27.2572 27.2572,-27.2572 "
|
||||
// "a 14.5445,14.5445 0 0 0 -11.3012,-23.634 "
|
||||
// "a 14.5445,14.5445 0 0 0 -11.414,5.4625 "
|
||||
// "l -4.542,4.5420 "
|
||||
// "l -4.5437,-4.5420 "
|
||||
// "a 14.5445,14.5445 0 0 0 -11.3984,-5.4937 "
|
||||
// "z"
|
||||
// )
|
||||
|
||||
// self.assertIn("A 14.5445,14.5445 0 0,0 1672.24,-54.8161 Z", path.d())
|
||||
test('svg.path library, issue 45', () {
|
||||
final path = parsePath("m 1672.2372,-54.8161 "
|
||||
"a 14.5445,14.5445 0 0 0 -11.3152,23.6652 "
|
||||
"l 27.2573,27.2572 27.2572,-27.2572 "
|
||||
"a 14.5445,14.5445 0 0 0 -11.3012,-23.634 "
|
||||
"a 14.5445,14.5445 0 0 0 -11.414,5.4625 "
|
||||
"l -4.542,4.5420 "
|
||||
"l -4.5437,-4.5420 "
|
||||
"a 14.5445,14.5445 0 0 0 -11.3984,-5.4937 "
|
||||
"z");
|
||||
expect(path.d(), contains("A 14.5445,14.5445 0 0,0 1672.2372,-54.8161 Z"));
|
||||
});
|
||||
}
|
||||
|
676
test/svg/path_test.dart
Normal file
676
test/svg/path_test.dart
Normal file
@ -0,0 +1,676 @@
|
||||
// import unittest
|
||||
import 'dart:math' show sqrt, pi;
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:kanimaji/common/point.dart';
|
||||
import 'package:kanimaji/svg/path.dart';
|
||||
|
||||
|
||||
// from ..path import CubicBezier, QuadraticBezier, Line, Arc, Move, Close, Path
|
||||
|
||||
// Most of these test points are not calculated serparately, as that would
|
||||
// take too long and be too error prone. Instead the curves have been verified
|
||||
// to be correct visually, by drawing them with the turtle module, with code
|
||||
// like this:
|
||||
//
|
||||
// import turtle
|
||||
// t = turtle.Turtle()
|
||||
// t.penup()
|
||||
//
|
||||
// for arc in (path1, path2):
|
||||
// p = arc.point(0)
|
||||
// t.goto(p.real - 500, -p.imag + 300)
|
||||
// t.dot(3, 'black')
|
||||
// t.pendown()
|
||||
// for x in range(1, 101):
|
||||
// p = arc.point(x * 0.01)
|
||||
// t.goto(p.real - 500, -p.imag + 300)
|
||||
// t.penup()
|
||||
// t.dot(3, 'black')
|
||||
//
|
||||
// raw_input()
|
||||
//
|
||||
// After the paths have been verified to be correct this way, the testing of
|
||||
// points along the paths has been added as regression tests, to make sure
|
||||
// nobody changes the way curves are drawn by mistake. Therefore, do not take
|
||||
// these points religiously. They might be subtly wrong, unless otherwise
|
||||
// noted.
|
||||
|
||||
// class MoreOrLessEqualsToPoint extends CustomMatcher {
|
||||
// MoreOrLessEqualsToPoint(matcher) : super("Widget with price that is", "price", matcher);
|
||||
// featureValueOf(actual) => (actual as Point).x;
|
||||
// }
|
||||
|
||||
class MoreOrLessEqualsToPoint extends Matcher {
|
||||
const MoreOrLessEqualsToPoint();
|
||||
|
||||
@override
|
||||
bool matches(covariant Finder finder, Map<dynamic, dynamic> matchState)
|
||||
=> finder is Point && finder.x
|
||||
|
||||
@override
|
||||
Description describe(Description description) => description.add('in card');
|
||||
}
|
||||
|
||||
void main() {
|
||||
group('Line tests', () {
|
||||
|
||||
test('Test lines', () {
|
||||
|
||||
// These points are calculated, and not just regression tests.
|
||||
const line1 = Line(start:Point.zero, end:Point(400, 0));
|
||||
expect(line1.point(0), moreOrLessEquals(0));
|
||||
// expect(line1.point(0.3), moreOrLessEquals(const Point(120, 0)));
|
||||
// expect(line1.point(0.5), moreOrLessEquals(const Point(200, 0)));
|
||||
// expect(line1.point(0.9), moreOrLessEquals(const Point(360, 0)));
|
||||
// expect(line1.point(1), moreOrLessEquals(const Point(400, 0)));
|
||||
expect(line1.size(), moreOrLessEquals(400));
|
||||
|
||||
const line2 = Line(start: Point(400, 0), end:Point(400, 300));
|
||||
// expect(line2.point(0), (400 + 0j))
|
||||
// expect(line2.point(0.3), (400 + 90j))
|
||||
// expect(line2.point(0.5), (400 + 150j))
|
||||
// expect(line2.point(0.9), (400 + 270j))
|
||||
// expect(line2.point(1), (400 + 300j))
|
||||
expect(line2.size(), moreOrLessEquals(300));
|
||||
|
||||
const line3 = Line(start:Point(400, 300), end:Point.zero);
|
||||
// expect(line3.point(0), (400 + 300j))
|
||||
// expect(line3.point(0.3), (280 + 210j))
|
||||
// expect(line3.point(0.5), (200 + 150j))
|
||||
// expect(line3.point(0.9), (40 + 30j))
|
||||
// expect(line3.point(1), (0j))
|
||||
expect(line3.size(), moreOrLessEquals(500));
|
||||
});
|
||||
|
||||
def test_equality(self):
|
||||
// This is to test the __eq__ and __ne__ methods, so we can't use
|
||||
// assertEqual and assertNotEqual
|
||||
line = Line(0j, 400 + 0j)
|
||||
self.assertTrue(line == Line(0, 400))
|
||||
self.assertTrue(line != Line(100, 400))
|
||||
self.assertFalse(line == str(line))
|
||||
self.assertTrue(line != str(line))
|
||||
self.assertFalse(
|
||||
CubicBezier(600 + 500j, 600 + 350j, 900 + 650j, 900 + 500j) == line
|
||||
)
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
// class CubicBezierTest(unittest.TestCase):
|
||||
// def test_approx_circle(self):
|
||||
// """This is a approximate circle drawn in Inkscape"""
|
||||
|
||||
// arc1 = CubicBezier(
|
||||
// complex(0, 0),
|
||||
// complex(0, 109.66797),
|
||||
// complex(-88.90345, 198.57142),
|
||||
// complex(-198.57142, 198.57142),
|
||||
// )
|
||||
|
||||
// self.assertAlmostEqual(arc1.point(0), (0j))
|
||||
// self.assertAlmostEqual(arc1.point(0.1), (-2.59896457 + 32.20931647j))
|
||||
// self.assertAlmostEqual(arc1.point(0.2), (-10.12330256 + 62.76392816j))
|
||||
// self.assertAlmostEqual(arc1.point(0.3), (-22.16418039 + 91.25500149j))
|
||||
// self.assertAlmostEqual(arc1.point(0.4), (-38.31276448 + 117.27370288j))
|
||||
// self.assertAlmostEqual(arc1.point(0.5), (-58.16022125 + 140.41119875j))
|
||||
// self.assertAlmostEqual(arc1.point(0.6), (-81.29771712 + 160.25865552j))
|
||||
// self.assertAlmostEqual(arc1.point(0.7), (-107.31641851 + 176.40723961j))
|
||||
// self.assertAlmostEqual(arc1.point(0.8), (-135.80749184 + 188.44811744j))
|
||||
// self.assertAlmostEqual(arc1.point(0.9), (-166.36210353 + 195.97245543j))
|
||||
// self.assertAlmostEqual(arc1.point(1), (-198.57142 + 198.57142j))
|
||||
|
||||
// arc2 = CubicBezier(
|
||||
// complex(-198.57142, 198.57142),
|
||||
// complex(-109.66797 - 198.57142, 0 + 198.57142),
|
||||
// complex(-198.57143 - 198.57142, -88.90345 + 198.57142),
|
||||
// complex(-198.57143 - 198.57142, 0),
|
||||
// )
|
||||
|
||||
// self.assertAlmostEqual(arc2.point(0), (-198.57142 + 198.57142j))
|
||||
// self.assertAlmostEqual(arc2.point(0.1), (-230.78073675 + 195.97245543j))
|
||||
// self.assertAlmostEqual(arc2.point(0.2), (-261.3353492 + 188.44811744j))
|
||||
// self.assertAlmostEqual(arc2.point(0.3), (-289.82642365 + 176.40723961j))
|
||||
// self.assertAlmostEqual(arc2.point(0.4), (-315.8451264 + 160.25865552j))
|
||||
// self.assertAlmostEqual(arc2.point(0.5), (-338.98262375 + 140.41119875j))
|
||||
// self.assertAlmostEqual(arc2.point(0.6), (-358.830082 + 117.27370288j))
|
||||
// self.assertAlmostEqual(arc2.point(0.7), (-374.97866745 + 91.25500149j))
|
||||
// self.assertAlmostEqual(arc2.point(0.8), (-387.0195464 + 62.76392816j))
|
||||
// self.assertAlmostEqual(arc2.point(0.9), (-394.54388515 + 32.20931647j))
|
||||
// self.assertAlmostEqual(arc2.point(1), (-397.14285 + 0j))
|
||||
|
||||
// arc3 = CubicBezier(
|
||||
// complex(-198.57143 - 198.57142, 0),
|
||||
// complex(0 - 198.57143 - 198.57142, -109.66797),
|
||||
// complex(88.90346 - 198.57143 - 198.57142, -198.57143),
|
||||
// complex(-198.57142, -198.57143),
|
||||
// )
|
||||
|
||||
// self.assertAlmostEqual(arc3.point(0), (-397.14285 + 0j))
|
||||
// self.assertAlmostEqual(arc3.point(0.1), (-394.54388515 - 32.20931675j))
|
||||
// self.assertAlmostEqual(arc3.point(0.2), (-387.0195464 - 62.7639292j))
|
||||
// self.assertAlmostEqual(arc3.point(0.3), (-374.97866745 - 91.25500365j))
|
||||
// self.assertAlmostEqual(arc3.point(0.4), (-358.830082 - 117.2737064j))
|
||||
// self.assertAlmostEqual(arc3.point(0.5), (-338.98262375 - 140.41120375j))
|
||||
// self.assertAlmostEqual(arc3.point(0.6), (-315.8451264 - 160.258662j))
|
||||
// self.assertAlmostEqual(arc3.point(0.7), (-289.82642365 - 176.40724745j))
|
||||
// self.assertAlmostEqual(arc3.point(0.8), (-261.3353492 - 188.4481264j))
|
||||
// self.assertAlmostEqual(arc3.point(0.9), (-230.78073675 - 195.97246515j))
|
||||
// self.assertAlmostEqual(arc3.point(1), (-198.57142 - 198.57143j))
|
||||
|
||||
// arc4 = CubicBezier(
|
||||
// complex(-198.57142, -198.57143),
|
||||
// complex(109.66797 - 198.57142, 0 - 198.57143),
|
||||
// complex(0, 88.90346 - 198.57143),
|
||||
// complex(0, 0),
|
||||
// )
|
||||
|
||||
// self.assertAlmostEqual(arc4.point(0), (-198.57142 - 198.57143j))
|
||||
// self.assertAlmostEqual(arc4.point(0.1), (-166.36210353 - 195.97246515j))
|
||||
// self.assertAlmostEqual(arc4.point(0.2), (-135.80749184 - 188.4481264j))
|
||||
// self.assertAlmostEqual(arc4.point(0.3), (-107.31641851 - 176.40724745j))
|
||||
// self.assertAlmostEqual(arc4.point(0.4), (-81.29771712 - 160.258662j))
|
||||
// self.assertAlmostEqual(arc4.point(0.5), (-58.16022125 - 140.41120375j))
|
||||
// self.assertAlmostEqual(arc4.point(0.6), (-38.31276448 - 117.2737064j))
|
||||
// self.assertAlmostEqual(arc4.point(0.7), (-22.16418039 - 91.25500365j))
|
||||
// self.assertAlmostEqual(arc4.point(0.8), (-10.12330256 - 62.7639292j))
|
||||
// self.assertAlmostEqual(arc4.point(0.9), (-2.59896457 - 32.20931675j))
|
||||
// self.assertAlmostEqual(arc4.point(1), (0j))
|
||||
|
||||
// def test_svg_examples(self):
|
||||
|
||||
// # M100,200 C100,100 250,100 250,200
|
||||
// path1 = CubicBezier(100 + 200j, 100 + 100j, 250 + 100j, 250 + 200j)
|
||||
// self.assertAlmostEqual(path1.point(0), (100 + 200j))
|
||||
// self.assertAlmostEqual(path1.point(0.3), (132.4 + 137j))
|
||||
// self.assertAlmostEqual(path1.point(0.5), (175 + 125j))
|
||||
// self.assertAlmostEqual(path1.point(0.9), (245.8 + 173j))
|
||||
// self.assertAlmostEqual(path1.point(1), (250 + 200j))
|
||||
|
||||
// # S400,300 400,200
|
||||
// path2 = CubicBezier(250 + 200j, 250 + 300j, 400 + 300j, 400 + 200j)
|
||||
// self.assertAlmostEqual(path2.point(0), (250 + 200j))
|
||||
// self.assertAlmostEqual(path2.point(0.3), (282.4 + 263j))
|
||||
// self.assertAlmostEqual(path2.point(0.5), (325 + 275j))
|
||||
// self.assertAlmostEqual(path2.point(0.9), (395.8 + 227j))
|
||||
// self.assertAlmostEqual(path2.point(1), (400 + 200j))
|
||||
|
||||
// # M100,200 C100,100 400,100 400,200
|
||||
// path3 = CubicBezier(100 + 200j, 100 + 100j, 400 + 100j, 400 + 200j)
|
||||
// self.assertAlmostEqual(path3.point(0), (100 + 200j))
|
||||
// self.assertAlmostEqual(path3.point(0.3), (164.8 + 137j))
|
||||
// self.assertAlmostEqual(path3.point(0.5), (250 + 125j))
|
||||
// self.assertAlmostEqual(path3.point(0.9), (391.6 + 173j))
|
||||
// self.assertAlmostEqual(path3.point(1), (400 + 200j))
|
||||
|
||||
// # M100,500 C25,400 475,400 400,500
|
||||
// path4 = CubicBezier(100 + 500j, 25 + 400j, 475 + 400j, 400 + 500j)
|
||||
// self.assertAlmostEqual(path4.point(0), (100 + 500j))
|
||||
// self.assertAlmostEqual(path4.point(0.3), (145.9 + 437j))
|
||||
// self.assertAlmostEqual(path4.point(0.5), (250 + 425j))
|
||||
// self.assertAlmostEqual(path4.point(0.9), (407.8 + 473j))
|
||||
// self.assertAlmostEqual(path4.point(1), (400 + 500j))
|
||||
|
||||
// # M100,800 C175,700 325,700 400,800
|
||||
// path5 = CubicBezier(100 + 800j, 175 + 700j, 325 + 700j, 400 + 800j)
|
||||
// self.assertAlmostEqual(path5.point(0), (100 + 800j))
|
||||
// self.assertAlmostEqual(path5.point(0.3), (183.7 + 737j))
|
||||
// self.assertAlmostEqual(path5.point(0.5), (250 + 725j))
|
||||
// self.assertAlmostEqual(path5.point(0.9), (375.4 + 773j))
|
||||
// self.assertAlmostEqual(path5.point(1), (400 + 800j))
|
||||
|
||||
// # M600,200 C675,100 975,100 900,200
|
||||
// path6 = CubicBezier(600 + 200j, 675 + 100j, 975 + 100j, 900 + 200j)
|
||||
// self.assertAlmostEqual(path6.point(0), (600 + 200j))
|
||||
// self.assertAlmostEqual(path6.point(0.3), (712.05 + 137j))
|
||||
// self.assertAlmostEqual(path6.point(0.5), (806.25 + 125j))
|
||||
// self.assertAlmostEqual(path6.point(0.9), (911.85 + 173j))
|
||||
// self.assertAlmostEqual(path6.point(1), (900 + 200j))
|
||||
|
||||
// # M600,500 C600,350 900,650 900,500
|
||||
// path7 = CubicBezier(600 + 500j, 600 + 350j, 900 + 650j, 900 + 500j)
|
||||
// self.assertAlmostEqual(path7.point(0), (600 + 500j))
|
||||
// self.assertAlmostEqual(path7.point(0.3), (664.8 + 462.2j))
|
||||
// self.assertAlmostEqual(path7.point(0.5), (750 + 500j))
|
||||
// self.assertAlmostEqual(path7.point(0.9), (891.6 + 532.4j))
|
||||
// self.assertAlmostEqual(path7.point(1), (900 + 500j))
|
||||
|
||||
// # M600,800 C625,700 725,700 750,800
|
||||
// path8 = CubicBezier(600 + 800j, 625 + 700j, 725 + 700j, 750 + 800j)
|
||||
// self.assertAlmostEqual(path8.point(0), (600 + 800j))
|
||||
// self.assertAlmostEqual(path8.point(0.3), (638.7 + 737j))
|
||||
// self.assertAlmostEqual(path8.point(0.5), (675 + 725j))
|
||||
// self.assertAlmostEqual(path8.point(0.9), (740.4 + 773j))
|
||||
// self.assertAlmostEqual(path8.point(1), (750 + 800j))
|
||||
|
||||
// # S875,900 900,800
|
||||
// inversion = (750 + 800j) + (750 + 800j) - (725 + 700j)
|
||||
// path9 = CubicBezier(750 + 800j, inversion, 875 + 900j, 900 + 800j)
|
||||
// self.assertAlmostEqual(path9.point(0), (750 + 800j))
|
||||
// self.assertAlmostEqual(path9.point(0.3), (788.7 + 863j))
|
||||
// self.assertAlmostEqual(path9.point(0.5), (825 + 875j))
|
||||
// self.assertAlmostEqual(path9.point(0.9), (890.4 + 827j))
|
||||
// self.assertAlmostEqual(path9.point(1), (900 + 800j))
|
||||
|
||||
// def test_length(self):
|
||||
|
||||
// # A straight line:
|
||||
// arc = CubicBezier(
|
||||
// complex(0, 0), complex(0, 0), complex(0, 100), complex(0, 100)
|
||||
// )
|
||||
|
||||
// self.assertAlmostEqual(arc.length(), 100)
|
||||
|
||||
// # A diagonal line:
|
||||
// arc = CubicBezier(
|
||||
// complex(0, 0), complex(0, 0), complex(100, 100), complex(100, 100)
|
||||
// )
|
||||
|
||||
// self.assertAlmostEqual(arc.length(), sqrt(2 * 100 * 100))
|
||||
|
||||
// # A quarter circle arc with radius 100:
|
||||
// kappa = (
|
||||
// 4 * (sqrt(2) - 1) / 3
|
||||
// ) # http://www.whizkidtech.redprince.net/bezier/circle/
|
||||
|
||||
// arc = CubicBezier(
|
||||
// complex(0, 0),
|
||||
// complex(0, kappa * 100),
|
||||
// complex(100 - kappa * 100, 100),
|
||||
// complex(100, 100),
|
||||
// )
|
||||
|
||||
// # We can't compare with pi*50 here, because this is just an
|
||||
// # approximation of a circle arc. pi*50 is 157.079632679
|
||||
// # So this is just yet another "warn if this changes" test.
|
||||
// # This value is not verified to be correct.
|
||||
// self.assertAlmostEqual(arc.length(), 157.1016698)
|
||||
|
||||
// # A recursive solution has also been suggested, but for CubicBezier
|
||||
// # curves it could get a false solution on curves where the midpoint is on a
|
||||
// # straight line between the start and end. For example, the following
|
||||
// # curve would get solved as a straight line and get the length 300.
|
||||
// # Make sure this is not the case.
|
||||
// arc = CubicBezier(
|
||||
// complex(600, 500), complex(600, 350), complex(900, 650), complex(900, 500)
|
||||
// )
|
||||
// self.assertTrue(arc.length() > 300.0)
|
||||
|
||||
// def test_equality(self):
|
||||
// # This is to test the __eq__ and __ne__ methods, so we can't use
|
||||
// # assertEqual and assertNotEqual
|
||||
// segment = CubicBezier(
|
||||
// complex(600, 500), complex(600, 350), complex(900, 650), complex(900, 500)
|
||||
// )
|
||||
|
||||
// self.assertTrue(
|
||||
// segment == CubicBezier(600 + 500j, 600 + 350j, 900 + 650j, 900 + 500j)
|
||||
// )
|
||||
// self.assertTrue(
|
||||
// segment != CubicBezier(600 + 501j, 600 + 350j, 900 + 650j, 900 + 500j)
|
||||
// )
|
||||
// self.assertTrue(segment != Line(0, 400))
|
||||
|
||||
|
||||
// class QuadraticBezierTest(unittest.TestCase):
|
||||
// def test_svg_examples(self):
|
||||
// """These is the path in the SVG specs"""
|
||||
// # M200,300 Q400,50 600,300 T1000,300
|
||||
// path1 = QuadraticBezier(200 + 300j, 400 + 50j, 600 + 300j)
|
||||
// self.assertAlmostEqual(path1.point(0), (200 + 300j))
|
||||
// self.assertAlmostEqual(path1.point(0.3), (320 + 195j))
|
||||
// self.assertAlmostEqual(path1.point(0.5), (400 + 175j))
|
||||
// self.assertAlmostEqual(path1.point(0.9), (560 + 255j))
|
||||
// self.assertAlmostEqual(path1.point(1), (600 + 300j))
|
||||
|
||||
// # T1000, 300
|
||||
// inversion = (600 + 300j) + (600 + 300j) - (400 + 50j)
|
||||
// path2 = QuadraticBezier(600 + 300j, inversion, 1000 + 300j)
|
||||
// self.assertAlmostEqual(path2.point(0), (600 + 300j))
|
||||
// self.assertAlmostEqual(path2.point(0.3), (720 + 405j))
|
||||
// self.assertAlmostEqual(path2.point(0.5), (800 + 425j))
|
||||
// self.assertAlmostEqual(path2.point(0.9), (960 + 345j))
|
||||
// self.assertAlmostEqual(path2.point(1), (1000 + 300j))
|
||||
|
||||
// def test_length(self):
|
||||
// # expected results calculated with
|
||||
// # svg.path.segment_length(q, 0, 1, q.start, q.end, 1e-14, 20, 0)
|
||||
// q1 = QuadraticBezier(200 + 300j, 400 + 50j, 600 + 300j)
|
||||
// q2 = QuadraticBezier(200 + 300j, 400 + 50j, 500 + 200j)
|
||||
// closedq = QuadraticBezier(6 + 2j, 5 - 1j, 6 + 2j)
|
||||
// linq1 = QuadraticBezier(1, 2, 3)
|
||||
// linq2 = QuadraticBezier(1 + 3j, 2 + 5j, -9 - 17j)
|
||||
// nodalq = QuadraticBezier(1, 1, 1)
|
||||
// tests = [
|
||||
// (q1, 487.77109389525975),
|
||||
// (q2, 379.90458193489155),
|
||||
// (closedq, 3.1622776601683795),
|
||||
// (linq1, 2),
|
||||
// (linq2, 22.73335777124786),
|
||||
// (nodalq, 0),
|
||||
// ]
|
||||
// for q, exp_res in tests:
|
||||
// self.assertAlmostEqual(q.length(), exp_res)
|
||||
|
||||
// def test_equality(self):
|
||||
// # This is to test the __eq__ and __ne__ methods, so we can't use
|
||||
// # assertEqual and assertNotEqual
|
||||
// segment = QuadraticBezier(200 + 300j, 400 + 50j, 600 + 300j)
|
||||
// self.assertTrue(segment == QuadraticBezier(200 + 300j, 400 + 50j, 600 + 300j))
|
||||
// self.assertTrue(segment != QuadraticBezier(200 + 301j, 400 + 50j, 600 + 300j))
|
||||
// self.assertFalse(segment == Arc(0j, 100 + 50j, 0, 0, 0, 100 + 50j))
|
||||
// self.assertTrue(Arc(0j, 100 + 50j, 0, 0, 0, 100 + 50j) != segment)
|
||||
|
||||
|
||||
// class ArcTest(unittest.TestCase):
|
||||
// def test_points(self):
|
||||
// arc1 = Arc(0j, 100 + 50j, 0, 0, 0, 100 + 50j)
|
||||
// self.assertAlmostEqual(arc1.center, 100 + 0j)
|
||||
// self.assertAlmostEqual(arc1.theta, 180.0)
|
||||
// self.assertAlmostEqual(arc1.delta, -90.0)
|
||||
|
||||
// self.assertAlmostEqual(arc1.point(0.0), (0j))
|
||||
// self.assertAlmostEqual(arc1.point(0.1), (1.23116594049 + 7.82172325201j))
|
||||
// self.assertAlmostEqual(arc1.point(0.2), (4.89434837048 + 15.4508497187j))
|
||||
// self.assertAlmostEqual(arc1.point(0.3), (10.8993475812 + 22.699524987j))
|
||||
// self.assertAlmostEqual(arc1.point(0.4), (19.0983005625 + 29.3892626146j))
|
||||
// self.assertAlmostEqual(arc1.point(0.5), (29.2893218813 + 35.3553390593j))
|
||||
// self.assertAlmostEqual(arc1.point(0.6), (41.2214747708 + 40.4508497187j))
|
||||
// self.assertAlmostEqual(arc1.point(0.7), (54.6009500260 + 44.5503262094j))
|
||||
// self.assertAlmostEqual(arc1.point(0.8), (69.0983005625 + 47.5528258148j))
|
||||
// self.assertAlmostEqual(arc1.point(0.9), (84.3565534960 + 49.3844170298j))
|
||||
// self.assertAlmostEqual(arc1.point(1.0), (100 + 50j))
|
||||
|
||||
// arc2 = Arc(0j, 100 + 50j, 0, 1, 0, 100 + 50j)
|
||||
// self.assertAlmostEqual(arc2.center, 50j)
|
||||
// self.assertAlmostEqual(arc2.theta, 270.0)
|
||||
// self.assertAlmostEqual(arc2.delta, -270.0)
|
||||
|
||||
// self.assertAlmostEqual(arc2.point(0.0), (0j))
|
||||
// self.assertAlmostEqual(arc2.point(0.1), (-45.399049974 + 5.44967379058j))
|
||||
// self.assertAlmostEqual(arc2.point(0.2), (-80.9016994375 + 20.6107373854j))
|
||||
// self.assertAlmostEqual(arc2.point(0.3), (-98.7688340595 + 42.178276748j))
|
||||
// self.assertAlmostEqual(arc2.point(0.4), (-95.1056516295 + 65.4508497187j))
|
||||
// self.assertAlmostEqual(arc2.point(0.5), (-70.7106781187 + 85.3553390593j))
|
||||
// self.assertAlmostEqual(arc2.point(0.6), (-30.9016994375 + 97.5528258148j))
|
||||
// self.assertAlmostEqual(arc2.point(0.7), (15.643446504 + 99.3844170298j))
|
||||
// self.assertAlmostEqual(arc2.point(0.8), (58.7785252292 + 90.4508497187j))
|
||||
// self.assertAlmostEqual(arc2.point(0.9), (89.1006524188 + 72.699524987j))
|
||||
// self.assertAlmostEqual(arc2.point(1.0), (100 + 50j))
|
||||
|
||||
// arc3 = Arc(0j, 100 + 50j, 0, 0, 1, 100 + 50j)
|
||||
// self.assertAlmostEqual(arc3.center, 50j)
|
||||
// self.assertAlmostEqual(arc3.theta, 270.0)
|
||||
// self.assertAlmostEqual(arc3.delta, 90.0)
|
||||
|
||||
// self.assertAlmostEqual(arc3.point(0.0), (0j))
|
||||
// self.assertAlmostEqual(arc3.point(0.1), (15.643446504 + 0.615582970243j))
|
||||
// self.assertAlmostEqual(arc3.point(0.2), (30.9016994375 + 2.44717418524j))
|
||||
// self.assertAlmostEqual(arc3.point(0.3), (45.399049974 + 5.44967379058j))
|
||||
// self.assertAlmostEqual(arc3.point(0.4), (58.7785252292 + 9.54915028125j))
|
||||
// self.assertAlmostEqual(arc3.point(0.5), (70.7106781187 + 14.6446609407j))
|
||||
// self.assertAlmostEqual(arc3.point(0.6), (80.9016994375 + 20.6107373854j))
|
||||
// self.assertAlmostEqual(arc3.point(0.7), (89.1006524188 + 27.300475013j))
|
||||
// self.assertAlmostEqual(arc3.point(0.8), (95.1056516295 + 34.5491502813j))
|
||||
// self.assertAlmostEqual(arc3.point(0.9), (98.7688340595 + 42.178276748j))
|
||||
// self.assertAlmostEqual(arc3.point(1.0), (100 + 50j))
|
||||
|
||||
// arc4 = Arc(0j, 100 + 50j, 0, 1, 1, 100 + 50j)
|
||||
// self.assertAlmostEqual(arc4.center, 100 + 0j)
|
||||
// self.assertAlmostEqual(arc4.theta, 180.0)
|
||||
// self.assertAlmostEqual(arc4.delta, 270.0)
|
||||
|
||||
// self.assertAlmostEqual(arc4.point(0.0), (0j))
|
||||
// self.assertAlmostEqual(arc4.point(0.1), (10.8993475812 - 22.699524987j))
|
||||
// self.assertAlmostEqual(arc4.point(0.2), (41.2214747708 - 40.4508497187j))
|
||||
// self.assertAlmostEqual(arc4.point(0.3), (84.3565534960 - 49.3844170298j))
|
||||
// self.assertAlmostEqual(arc4.point(0.4), (130.901699437 - 47.5528258148j))
|
||||
// self.assertAlmostEqual(arc4.point(0.5), (170.710678119 - 35.3553390593j))
|
||||
// self.assertAlmostEqual(arc4.point(0.6), (195.105651630 - 15.4508497187j))
|
||||
// self.assertAlmostEqual(arc4.point(0.7), (198.768834060 + 7.82172325201j))
|
||||
// self.assertAlmostEqual(arc4.point(0.8), (180.901699437 + 29.3892626146j))
|
||||
// self.assertAlmostEqual(arc4.point(0.9), (145.399049974 + 44.5503262094j))
|
||||
// self.assertAlmostEqual(arc4.point(1.0), (100 + 50j))
|
||||
|
||||
// def test_length(self):
|
||||
// # I'll test the length calculations by making a circle, in two parts.
|
||||
// arc1 = Arc(0j, 100 + 100j, 0, 0, 0, 200 + 0j)
|
||||
// arc2 = Arc(200 + 0j, 100 + 100j, 0, 0, 0, 0j)
|
||||
// self.assertAlmostEqual(arc1.length(), pi * 100)
|
||||
// self.assertAlmostEqual(arc2.length(), pi * 100)
|
||||
|
||||
// def test_length_out_of_range(self):
|
||||
// # See F.6.2 Out-of-range parameters
|
||||
|
||||
// # If the endpoints (x1, y1) and (x2, y2) are identical, then this is
|
||||
// # equivalent to omitting the elliptical arc segment entirely.
|
||||
// arc = Arc(0j, 100 + 100j, 0, 0, 0, 0j)
|
||||
// self.assertAlmostEqual(arc.length(), 0)
|
||||
|
||||
// # If rx = 0 or ry = 0 then this arc is treated as a straight
|
||||
// # line segment (a "lineto") joining the endpoints.
|
||||
// arc = Arc(0j, 0j, 0, 0, 0, 200 + 0j)
|
||||
// self.assertAlmostEqual(arc.length(), 200)
|
||||
|
||||
// # If rx or ry have negative signs, these are dropped;
|
||||
// # the absolute value is used instead.
|
||||
// arc = Arc(200 + 0j, -100 - 100j, 0, 0, 0, 0j)
|
||||
// self.assertAlmostEqual(arc.length(), pi * 100)
|
||||
|
||||
// # If rx, ry and φ are such that there is no solution (basically,
|
||||
// # the ellipse is not big enough to reach from (x1, y1) to (x2, y2))
|
||||
// # then the ellipse is scaled up uniformly until there is exactly
|
||||
// # one solution (until the ellipse is just big enough).
|
||||
// arc = Arc(0j, 1 + 1j, 0, 0, 0, 200 + 0j)
|
||||
// self.assertAlmostEqual(arc.length(), pi * 100)
|
||||
|
||||
// # φ is taken mod 360 degrees.
|
||||
// arc = Arc(200 + 0j, -100 - 100j, 720, 0, 0, 0j)
|
||||
// self.assertAlmostEqual(arc.length(), pi * 100)
|
||||
|
||||
// def test_equality(self):
|
||||
// # This is to test the __eq__ and __ne__ methods, so we can't use
|
||||
// # assertEqual and assertNotEqual
|
||||
// segment = Arc(0j, 100 + 50j, 0, 0, 0, 100 + 50j)
|
||||
// self.assertTrue(segment == Arc(0j, 100 + 50j, 0, 0, 0, 100 + 50j))
|
||||
// self.assertTrue(segment != Arc(0j, 100 + 50j, 0, 1, 0, 100 + 50j))
|
||||
|
||||
// def test_issue25(self):
|
||||
// # This raised a math domain error
|
||||
// Arc(
|
||||
// (725.307482225571 - 915.5548199281527j),
|
||||
// (202.79421639137703 + 148.77294617167183j),
|
||||
// 225.6910319606926,
|
||||
// 1,
|
||||
// 1,
|
||||
// (-624.6375539637027 + 896.5483089399895j),
|
||||
// )
|
||||
|
||||
|
||||
// class TestPath(unittest.TestCase):
|
||||
// def test_circle(self):
|
||||
// arc1 = Arc(0j, 100 + 100j, 0, 0, 0, 200 + 0j)
|
||||
// arc2 = Arc(200 + 0j, 100 + 100j, 0, 0, 0, 0j)
|
||||
// path = Path(arc1, arc2)
|
||||
// self.assertAlmostEqual(path.point(0.0), (0j))
|
||||
// self.assertAlmostEqual(path.point(0.25), (100 + 100j))
|
||||
// self.assertAlmostEqual(path.point(0.5), (200 + 0j))
|
||||
// self.assertAlmostEqual(path.point(0.75), (100 - 100j))
|
||||
// self.assertAlmostEqual(path.point(1.0), (0j))
|
||||
// self.assertAlmostEqual(path.length(), pi * 200)
|
||||
|
||||
// def test_svg_specs(self):
|
||||
// """The paths that are in the SVG specs"""
|
||||
|
||||
// # Big pie: M300,200 h-150 a150,150 0 1,0 150,-150 z
|
||||
// path = Path(
|
||||
// Line(300 + 200j, 150 + 200j),
|
||||
// Arc(150 + 200j, 150 + 150j, 0, 1, 0, 300 + 50j),
|
||||
// Line(300 + 50j, 300 + 200j),
|
||||
// )
|
||||
// # The points and length for this path are calculated and not regression tests.
|
||||
// self.assertAlmostEqual(path.point(0.0), (300 + 200j))
|
||||
// self.assertAlmostEqual(path.point(0.14897825542), (150 + 200j))
|
||||
// self.assertAlmostEqual(path.point(0.5), (406.066017177 + 306.066017177j))
|
||||
// self.assertAlmostEqual(path.point(1 - 0.14897825542), (300 + 50j))
|
||||
// self.assertAlmostEqual(path.point(1.0), (300 + 200j))
|
||||
// # The errors seem to accumulate. Still 6 decimal places is more than good enough.
|
||||
// self.assertAlmostEqual(path.length(), pi * 225 + 300, places=6)
|
||||
|
||||
// # Little pie: M275,175 v-150 a150,150 0 0,0 -150,150 z
|
||||
// path = Path(
|
||||
// Line(275 + 175j, 275 + 25j),
|
||||
// Arc(275 + 25j, 150 + 150j, 0, 0, 0, 125 + 175j),
|
||||
// Line(125 + 175j, 275 + 175j),
|
||||
// )
|
||||
// # The points and length for this path are calculated and not regression tests.
|
||||
// self.assertAlmostEqual(path.point(0.0), (275 + 175j))
|
||||
// self.assertAlmostEqual(path.point(0.2800495767557787), (275 + 25j))
|
||||
// self.assertAlmostEqual(
|
||||
// path.point(0.5), (168.93398282201787 + 68.93398282201787j)
|
||||
// )
|
||||
// self.assertAlmostEqual(path.point(1 - 0.2800495767557787), (125 + 175j))
|
||||
// self.assertAlmostEqual(path.point(1.0), (275 + 175j))
|
||||
// # The errors seem to accumulate. Still 6 decimal places is more than good enough.
|
||||
// self.assertAlmostEqual(path.length(), pi * 75 + 300, places=6)
|
||||
|
||||
// # Bumpy path: M600,350 l 50,-25
|
||||
// # a25,25 -30 0,1 50,-25 l 50,-25
|
||||
// # a25,50 -30 0,1 50,-25 l 50,-25
|
||||
// # a25,75 -30 0,1 50,-25 l 50,-25
|
||||
// # a25,100 -30 0,1 50,-25 l 50,-25
|
||||
// path = Path(
|
||||
// Line(600 + 350j, 650 + 325j),
|
||||
// Arc(650 + 325j, 25 + 25j, -30, 0, 1, 700 + 300j),
|
||||
// Line(700 + 300j, 750 + 275j),
|
||||
// Arc(750 + 275j, 25 + 50j, -30, 0, 1, 800 + 250j),
|
||||
// Line(800 + 250j, 850 + 225j),
|
||||
// Arc(850 + 225j, 25 + 75j, -30, 0, 1, 900 + 200j),
|
||||
// Line(900 + 200j, 950 + 175j),
|
||||
// Arc(950 + 175j, 25 + 100j, -30, 0, 1, 1000 + 150j),
|
||||
// Line(1000 + 150j, 1050 + 125j),
|
||||
// )
|
||||
|
||||
// # These are *not* calculated, but just regression tests. Be skeptical.
|
||||
// self.assertAlmostEqual(path.point(0.0), (600 + 350j))
|
||||
// self.assertAlmostEqual(path.point(0.3), (755.23979927 + 212.1820209585j))
|
||||
// self.assertAlmostEqual(path.point(0.5), (827.73074926 + 147.8241574162j))
|
||||
// self.assertAlmostEqual(path.point(0.9), (971.28435780 + 106.3023526073j))
|
||||
// self.assertAlmostEqual(path.point(1.0), (1050 + 125j))
|
||||
// self.assertAlmostEqual(path.length(), 928.388639381)
|
||||
|
||||
// def test_repr(self):
|
||||
// path = Path(
|
||||
// Line(start=600 + 350j, end=650 + 325j),
|
||||
// Arc(
|
||||
// start=650 + 325j,
|
||||
// radius=25 + 25j,
|
||||
// rotation=-30,
|
||||
// arc=0,
|
||||
// sweep=1,
|
||||
// end=700 + 300j,
|
||||
// ),
|
||||
// CubicBezier(
|
||||
// start=700 + 300j,
|
||||
// control1=800 + 400j,
|
||||
// control2=750 + 200j,
|
||||
// end=600 + 100j,
|
||||
// ),
|
||||
// QuadraticBezier(start=600 + 100j, control=600, end=600 + 300j),
|
||||
// )
|
||||
// self.assertEqual(eval(repr(path)), path)
|
||||
|
||||
// def test_reverse(self):
|
||||
// # Currently you can't reverse paths.
|
||||
// self.assertRaises(NotImplementedError, Path().reverse)
|
||||
|
||||
// def test_equality(self):
|
||||
// # This is to test the __eq__ and __ne__ methods, so we can't use
|
||||
// # assertEqual and assertNotEqual
|
||||
// path1 = Path(
|
||||
// Line(start=600 + 350j, end=650 + 325j),
|
||||
// Arc(
|
||||
// start=650 + 325j,
|
||||
// radius=25 + 25j,
|
||||
// rotation=-30,
|
||||
// arc=0,
|
||||
// sweep=1,
|
||||
// end=700 + 300j,
|
||||
// ),
|
||||
// CubicBezier(
|
||||
// start=700 + 300j,
|
||||
// control1=800 + 400j,
|
||||
// control2=750 + 200j,
|
||||
// end=600 + 100j,
|
||||
// ),
|
||||
// QuadraticBezier(start=600 + 100j, control=600, end=600 + 300j),
|
||||
// )
|
||||
// path2 = Path(
|
||||
// Line(start=600 + 350j, end=650 + 325j),
|
||||
// Arc(
|
||||
// start=650 + 325j,
|
||||
// radius=25 + 25j,
|
||||
// rotation=-30,
|
||||
// arc=0,
|
||||
// sweep=1,
|
||||
// end=700 + 300j,
|
||||
// ),
|
||||
// CubicBezier(
|
||||
// start=700 + 300j,
|
||||
// control1=800 + 400j,
|
||||
// control2=750 + 200j,
|
||||
// end=600 + 100j,
|
||||
// ),
|
||||
// QuadraticBezier(start=600 + 100j, control=600, end=600 + 300j),
|
||||
// )
|
||||
|
||||
// self.assertTrue(path1 == path2)
|
||||
// # Modify path2:
|
||||
// path2[0].start = 601 + 350j
|
||||
// self.assertTrue(path1 != path2)
|
||||
|
||||
// # Modify back:
|
||||
// path2[0].start = 600 + 350j
|
||||
// self.assertFalse(path1 != path2)
|
||||
|
||||
// # Get rid of the last segment:
|
||||
// del path2[-1]
|
||||
// self.assertFalse(path1 == path2)
|
||||
|
||||
// # It's not equal to a list of it's segments
|
||||
// self.assertTrue(path1 != path1[:])
|
||||
// self.assertFalse(path1 == path1[:])
|
||||
|
||||
// def test_non_arc(self):
|
||||
// # And arc with the same start and end is a noop.
|
||||
// segment = Arc(0j + 70j, 35 + 35j, 0, 1, 0, 0 + 70j)
|
||||
// self.assertEqual(segment.length(), 0)
|
||||
// self.assertEqual(segment.point(0.5), segment.start)
|
||||
|
||||
// def test_zero_paths(self):
|
||||
// move_only = Path(Move(0))
|
||||
// self.assertEqual(move_only.point(0), 0 + 0j)
|
||||
// self.assertEqual(move_only.point(0.5), 0 + 0j)
|
||||
// self.assertEqual(move_only.point(1), 0 + 0j)
|
||||
// self.assertEqual(move_only.length(), 0)
|
||||
|
||||
// move_onlyz = Path(Move(0), Close(0, 0))
|
||||
// self.assertEqual(move_onlyz.point(0), 0 + 0j)
|
||||
// self.assertEqual(move_onlyz.point(0.5), 0 + 0j)
|
||||
// self.assertEqual(move_onlyz.point(1), 0 + 0j)
|
||||
// self.assertEqual(move_onlyz.length(), 0)
|
||||
|
||||
// zero_line = Path(Move(0), Line(0, 0))
|
||||
// self.assertEqual(zero_line.point(0), 0 + 0j)
|
||||
// self.assertEqual(zero_line.point(0.5), 0 + 0j)
|
||||
// self.assertEqual(zero_line.point(1), 0 + 0j)
|
||||
// self.assertEqual(zero_line.length(), 0)
|
||||
|
||||
// only_line = Path(Line(1 + 1j, 1 + 1j))
|
||||
// self.assertEqual(only_line.point(0), 1 + 1j)
|
||||
// self.assertEqual(only_line.point(0.5), 1 + 1j)
|
||||
// self.assertEqual(only_line.point(1), 1 + 1j)
|
||||
// self.assertEqual(only_line.length(), 0)
|
||||
}
|
123
test/svg/tokenizer_test.dart
Normal file
123
test/svg/tokenizer_test.dart
Normal file
@ -0,0 +1,123 @@
|
||||
// import pytest
|
||||
// from svg.path import parser
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:kanimaji/common/point.dart';
|
||||
import 'package:kanimaji/svg/parser.dart' show Command, Token, commandifyPath, parsePath, tokenizePath;
|
||||
|
||||
class TokenizerTest {
|
||||
final String pathdef;
|
||||
final List<Command> commands;
|
||||
final List<Token> tokens;
|
||||
|
||||
const TokenizerTest({
|
||||
required this.pathdef,
|
||||
required this.commands,
|
||||
required this.tokens,
|
||||
});
|
||||
}
|
||||
|
||||
final List<TokenizerTest> tokenizerTests = [
|
||||
const TokenizerTest(
|
||||
pathdef: "M 100 100 L 300 100 L 200 300 z",
|
||||
commands: [
|
||||
Command(command: "M", args: "100 100"),
|
||||
Command(command: "L", args: "300 100"),
|
||||
Command(command: "L", args: "200 300"),
|
||||
Command(command: "z", args: ""),
|
||||
],
|
||||
tokens: [
|
||||
Token(command: "M", args: [Point(100, 100)]),
|
||||
Token(command: "L", args: [Point(300, 100)]),
|
||||
Token(command: "L", args: [Point(200, 300)]),
|
||||
Token(command: "z", args: [])
|
||||
],
|
||||
),
|
||||
const TokenizerTest(
|
||||
pathdef:
|
||||
"M 5 1 v 7.344 A 3.574 3.574 0 003.5 8 3.515 3.515 0 000 11.5 C 0 13.421 1.579 15 3.5 15 "
|
||||
"A 3.517 3.517 0 007 11.531 v -7.53 h 6 v 4.343 A 3.574 3.574 0 0011.5 8 3.515 3.515 0 008 11.5 "
|
||||
"c 0 1.921 1.579 3.5 3.5 3.5 1.9 0 3.465 -1.546 3.5 -3.437 V 1 z",
|
||||
commands: [
|
||||
Command(command: "M", args: "5 1"),
|
||||
Command(command: "v", args: "7.344"),
|
||||
Command(
|
||||
command: "A", args: "3.574 3.574 0 003.5 8 3.515 3.515 0 000 11.5"),
|
||||
Command(command: "C", args: "0 13.421 1.579 15 3.5 15"),
|
||||
Command(command: "A", args: "3.517 3.517 0 007 11.531"),
|
||||
Command(command: "v", args: "-7.53"),
|
||||
Command(command: "h", args: "6"),
|
||||
Command(command: "v", args: "4.343"),
|
||||
Command(
|
||||
command: "A", args: "3.574 3.574 0 0011.5 8 3.515 3.515 0 008 11.5"),
|
||||
Command(
|
||||
command: "c",
|
||||
args: "0 1.921 1.579 3.5 3.5 3.5 1.9 0 3.465 -1.546 3.5 -3.437"),
|
||||
Command(command: "V", args: "1"),
|
||||
Command(command: "z", args: ""),
|
||||
],
|
||||
tokens: [
|
||||
Token(command: "M", args: [Point(5, 1)]),
|
||||
Token(command: "v", args: [7.344]),
|
||||
Token(command: "A", args: [3.574, 3.574, 0, false, false, Point(3.5, 8)]),
|
||||
Token(
|
||||
command: "A", args: [3.515, 3.515, 0, false, false, Point(0, 11.5)]),
|
||||
Token(
|
||||
command: "C",
|
||||
args: [Point(0, 13.421), Point(1.579, 15), Point(3.5, 15)]),
|
||||
Token(
|
||||
command: "A",
|
||||
args: [3.517, 3.517, 0, false, false, Point(7, 11.531)]),
|
||||
Token(command: "v", args: [-7.53]),
|
||||
Token(command: "h", args: [6]),
|
||||
Token(command: "v", args: [4.343]),
|
||||
Token(
|
||||
command: "A", args: [3.574, 3.574, 0, false, false, Point(11.5, 8)]),
|
||||
Token(
|
||||
command: "A", args: [3.515, 3.515, 0, false, false, Point(8, 11.5)]),
|
||||
Token(
|
||||
command: "c",
|
||||
args: [Point(0, 1.921), Point(1.579, 3.5), Point(3.5, 3.5)]),
|
||||
Token(
|
||||
command: "c",
|
||||
args: [Point(1.9, 0), Point(3.465, -1.546), Point(3.5, -3.437)]),
|
||||
Token(command: "V", args: [1]),
|
||||
Token(command: "z", args: []),
|
||||
],
|
||||
),
|
||||
const TokenizerTest(
|
||||
pathdef: "M 600,350 L 650,325 A 25,25 -30 0,1 700,300 L 750,275",
|
||||
commands: [
|
||||
Command(command: "M", args: "600,350"),
|
||||
Command(command: "L", args: "650,325"),
|
||||
Command(command: "A", args: "25,25 -30 0,1 700,300"),
|
||||
Command(command: "L", args: "750,275"),
|
||||
],
|
||||
tokens: [
|
||||
Token(command: "M", args: [Point(600, 350)]),
|
||||
Token(command: "L", args: [Point(650, 325)]),
|
||||
Token(command: "A", args: [25, 25, -30, false, true, Point(700, 300)]),
|
||||
Token(command: "L", args: [Point(750, 275)]),
|
||||
],
|
||||
),
|
||||
];
|
||||
|
||||
void main() {
|
||||
test('Test commandifier', () {
|
||||
for (final tokenizerTest in tokenizerTests) {
|
||||
expect(commandifyPath(tokenizerTest.pathdef), tokenizerTest.commands);
|
||||
}
|
||||
});
|
||||
|
||||
test('Test tokenizer', () {
|
||||
for (final tokenizerTest in tokenizerTests) {
|
||||
expect(tokenizePath(tokenizerTest.pathdef), tokenizerTest.tokens);
|
||||
}
|
||||
});
|
||||
|
||||
test('Test parser', () {
|
||||
for (final tokenizerTest in tokenizerTests) {
|
||||
parsePath(tokenizerTest.pathdef);
|
||||
}
|
||||
});
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user