Compare commits
1 Commits
example-ap
...
wip
| Author | SHA1 | Date | |
|---|---|---|---|
|
779f119992
|
@@ -1,5 +1,10 @@
|
|||||||
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:math' show min, sqrt, pow;
|
import 'dart:math' show min, sqrt, pow;
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:flutter_svg/flutter_svg.dart' as svg;
|
||||||
|
import 'package:image/image.dart' as image;
|
||||||
|
|
||||||
import '../svg/parser.dart';
|
import '../svg/parser.dart';
|
||||||
import '../common/point.dart';
|
import '../common/point.dart';
|
||||||
@@ -144,31 +149,13 @@ void clearPreviousElements(XmlDocument doc) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Note: setting any color to transparent will result in a much bigger
|
XmlDocument fetchXML(String inputFile) {
|
||||||
/// filesize for GIFs.
|
|
||||||
void createAnimation({
|
//------------------------------
|
||||||
required String inputFile,
|
// FETCH DATA FILE
|
||||||
String? outputFile,
|
//------------------------------
|
||||||
TimingFunction timingFunction = TimingFunction.easeInOut,
|
|
||||||
double strokeBorderWidth = 4.5,
|
|
||||||
double strokeUnfilledWidth = 3,
|
|
||||||
double strokeFilledWidth = 3.1,
|
|
||||||
bool showBrush = true,
|
|
||||||
bool showBrushFrontBorder = true,
|
|
||||||
double brushWidth = 5.5,
|
|
||||||
double brushBorderWidth = 7,
|
|
||||||
double waitAfter = 1.5,
|
|
||||||
String strokeBorderColor = '#666',
|
|
||||||
String strokeUnfilledColor = '#EEE',
|
|
||||||
String strokeFillingColor = '#F00',
|
|
||||||
String strokeFilledColor = '#000',
|
|
||||||
String brushColor = '#F00',
|
|
||||||
String brushBorderColor = '#666',
|
|
||||||
}) {
|
|
||||||
print('processing $inputFile');
|
print('processing $inputFile');
|
||||||
final String filenameNoext = inputFile.replaceAll(RegExp(r'\.[^\.]+$'), '');
|
|
||||||
outputFile ??= '${filenameNoext}_anim.svg';
|
|
||||||
final String baseid = basename(filenameNoext);
|
|
||||||
|
|
||||||
// load xml
|
// load xml
|
||||||
final XmlDocument doc = XmlDocument.parse(File(inputFile).readAsStringSync());
|
final XmlDocument doc = XmlDocument.parse(File(inputFile).readAsStringSync());
|
||||||
@@ -178,6 +165,44 @@ void createAnimation({
|
|||||||
doc.rootElement.setAttribute('xlink:used', '');
|
doc.rootElement.setAttribute('xlink:used', '');
|
||||||
|
|
||||||
clearPreviousElements(doc);
|
clearPreviousElements(doc);
|
||||||
|
return doc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// void generateCssForPath(XmlNode p, void Function<String> addCss) {
|
||||||
|
// }
|
||||||
|
|
||||||
|
/// Note: setting any color to transparent will result in a much bigger
|
||||||
|
/// filesize for GIFs.
|
||||||
|
FutureOr<void> createAnimation({
|
||||||
|
required String kanji,
|
||||||
|
String? outputFile,
|
||||||
|
TimingFunction timingFunction = TimingFunction.easeInOut,
|
||||||
|
double strokeBorderWidth = 4.5,
|
||||||
|
double strokeUnfilledWidth = 3,
|
||||||
|
double strokeFilledWidth = 3.1,
|
||||||
|
bool showBrush = true,
|
||||||
|
// bool showNumbers = false,
|
||||||
|
bool showBrushFrontBorder = true,
|
||||||
|
double brushWidth = 5.5,
|
||||||
|
double brushBorderWidth = 7,
|
||||||
|
double waitAfter = 1.5,
|
||||||
|
int gifSize = 500,
|
||||||
|
String strokeBorderColor = '#666',
|
||||||
|
String strokeUnfilledColor = '#EEE',
|
||||||
|
String strokeFillingColor = '#F00',
|
||||||
|
String strokeFilledColor = '#000',
|
||||||
|
String brushColor = '#F00',
|
||||||
|
String brushBorderColor = '#666',
|
||||||
|
}) async {
|
||||||
|
final inputFile = 'assets/kanjivg/kanji/${kanji.codeUnitAt(0).toRadixString(16).padLeft(5, '0')}.svg';
|
||||||
|
final String filenameNoext = inputFile.replaceAll(RegExp(r'\.[^\.]+$'), '');
|
||||||
|
outputFile ??= '${filenameNoext}_anim.svg';
|
||||||
|
final String baseid = basename(filenameNoext);
|
||||||
|
final doc = fetchXML(inputFile);
|
||||||
|
|
||||||
|
//------------------------------
|
||||||
|
// CREATE SVG PATH GROUPS
|
||||||
|
//------------------------------
|
||||||
|
|
||||||
/// create groups with a copies (references actually) of the paths
|
/// create groups with a copies (references actually) of the paths
|
||||||
XmlDocumentFragment pathCopyGroup({
|
XmlDocumentFragment pathCopyGroup({
|
||||||
@@ -227,6 +252,10 @@ void createAnimation({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//------------------------------
|
||||||
|
// CALCULATE STROKE TIMES
|
||||||
|
//------------------------------
|
||||||
|
|
||||||
// compute total length and time, at first
|
// compute total length and time, at first
|
||||||
double totlen = 0;
|
double totlen = 0;
|
||||||
double tottime = 0;
|
double tottime = 0;
|
||||||
@@ -254,6 +283,10 @@ void createAnimation({
|
|||||||
final double actualAnimationTime = animationTime;
|
final double actualAnimationTime = animationTime;
|
||||||
animationTime += waitAfter;
|
animationTime += waitAfter;
|
||||||
|
|
||||||
|
//------------------------------
|
||||||
|
// START ADDING CSS
|
||||||
|
//------------------------------
|
||||||
|
|
||||||
final Map<int, String> staticCss = {};
|
final Map<int, String> staticCss = {};
|
||||||
late String animatedCss;
|
late String animatedCss;
|
||||||
|
|
||||||
@@ -286,6 +319,10 @@ void createAnimation({
|
|||||||
double elapsedlen = 0;
|
double elapsedlen = 0;
|
||||||
double elapsedtime = 0;
|
double elapsedtime = 0;
|
||||||
|
|
||||||
|
//------------------------------
|
||||||
|
// ADD CSS FOR STROKE STYLE
|
||||||
|
//------------------------------
|
||||||
|
|
||||||
// add css elements for all strokes
|
// add css elements for all strokes
|
||||||
for (final XmlNode g in doc
|
for (final XmlNode g in doc
|
||||||
.getElement('svg', namespace: namespaces['n'])!
|
.getElement('svg', namespace: namespaces['n'])!
|
||||||
@@ -326,7 +363,12 @@ void createAnimation({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//------------------------------
|
||||||
|
// ADD CSS FOR HREFS
|
||||||
|
//------------------------------
|
||||||
|
|
||||||
for (final p in g.findAllElements("path", namespace: namespaces['n'])) {
|
for (final p in g.findAllElements("path", namespace: namespaces['n'])) {
|
||||||
|
|
||||||
final pathid = p.getAttribute('id') as String;
|
final pathid = p.getAttribute('id') as String;
|
||||||
final pathidcss = pathid.replaceAll(':', '\\3a ');
|
final pathidcss = pathid.replaceAll(':', '\\3a ');
|
||||||
|
|
||||||
@@ -355,6 +397,10 @@ void createAnimation({
|
|||||||
brushBorderGroupElement = addHref('brush-brd', brushBrdGroup);
|
brushBorderGroupElement = addHref('brush-brd', brushBrdGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//------------------------------
|
||||||
|
// CALCULATE RELATIVE TIMING
|
||||||
|
//------------------------------
|
||||||
|
|
||||||
final pathname = pathid.replaceAll(RegExp(r'^kvg:'), '');
|
final pathname = pathid.replaceAll(RegExp(r'^kvg:'), '');
|
||||||
final pathlen = _computePathLength(p.getAttribute('d') as String);
|
final pathlen = _computePathLength(p.getAttribute('d') as String);
|
||||||
final duration = strokeLengthToDuration(pathlen);
|
final duration = strokeLengthToDuration(pathlen);
|
||||||
@@ -378,6 +424,10 @@ void createAnimation({
|
|||||||
final animStart = elapsedtime / tottime * 100;
|
final animStart = elapsedtime / tottime * 100;
|
||||||
final animEnd = newelapsedtime / tottime * 100;
|
final animEnd = newelapsedtime / tottime * 100;
|
||||||
|
|
||||||
|
//------------------------------
|
||||||
|
// GENERATE SVG SPECIFIC ANIMATION CSS
|
||||||
|
//------------------------------
|
||||||
|
|
||||||
if (GENERATE_SVG) {
|
if (GENERATE_SVG) {
|
||||||
// animation stroke progression
|
// animation stroke progression
|
||||||
animatedCss += '''
|
animatedCss += '''
|
||||||
@@ -418,6 +468,10 @@ void createAnimation({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//------------------------------
|
||||||
|
// GENERATE JS SVG SPECIFIC ANIMATION CSS
|
||||||
|
//------------------------------
|
||||||
|
|
||||||
if (GENERATE_JS_SVG) {
|
if (GENERATE_JS_SVG) {
|
||||||
jsAnimatedCss += '\n/* stroke $pathid */\n';
|
jsAnimatedCss += '\n/* stroke $pathid */\n';
|
||||||
|
|
||||||
@@ -468,6 +522,10 @@ void createAnimation({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//------------------------------
|
||||||
|
// GENERATE GIF SPECIFIC STATIC FRAME CSS
|
||||||
|
//------------------------------
|
||||||
|
|
||||||
if (GENERATE_GIF) {
|
if (GENERATE_GIF) {
|
||||||
for (final k in staticCss.keys) {
|
for (final k in staticCss.keys) {
|
||||||
final time = k * GIF_FRAME_DURATION;
|
final time = k * GIF_FRAME_DURATION;
|
||||||
@@ -539,17 +597,32 @@ void createAnimation({
|
|||||||
elapsedlen = newelapsedlen;
|
elapsedlen = newelapsedlen;
|
||||||
elapsedtime = newelapsedtime;
|
elapsedtime = newelapsedtime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// for (final p in g.findAllElements("path", namespace: namespaces['n'])) {
|
||||||
|
// generateCssForPath(p);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//------------------------------
|
||||||
|
// ADD CSS TO XML-OBJECT INSTANCE
|
||||||
|
//------------------------------
|
||||||
|
|
||||||
void addGroup(XmlDocumentFragment g) =>
|
void addGroup(XmlDocumentFragment g) =>
|
||||||
doc.root.firstElementChild?.children.add(g);
|
doc.root.firstElementChild?.children.add(g);
|
||||||
|
|
||||||
// insert groups
|
// insert groups
|
||||||
if (showBrush && !showBrushFrontBorder) addGroup(brushBrdGroup);
|
if (showBrush && !showBrushFrontBorder)
|
||||||
|
addGroup(brushBrdGroup);
|
||||||
addGroup(bgGroup);
|
addGroup(bgGroup);
|
||||||
if (showBrush && showBrushFrontBorder) addGroup(brushBrdGroup);
|
if (showBrush && showBrushFrontBorder)
|
||||||
|
addGroup(brushBrdGroup);
|
||||||
addGroup(animGroup);
|
addGroup(animGroup);
|
||||||
if (showBrush) addGroup(brushGroup);
|
if (showBrush)
|
||||||
|
addGroup(brushGroup);
|
||||||
|
|
||||||
|
//------------------------------
|
||||||
|
// PRODUCE SVG
|
||||||
|
//------------------------------
|
||||||
|
|
||||||
if (GENERATE_SVG) {
|
if (GENERATE_SVG) {
|
||||||
print(animatedCss);
|
print(animatedCss);
|
||||||
@@ -567,24 +640,51 @@ void createAnimation({
|
|||||||
print('written $outputFile');
|
print('written $outputFile');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GENERATE_GIF) {
|
//------------------------------
|
||||||
// svgframefiles = []
|
// PRODUCE GIF
|
||||||
// pngframefiles = []
|
//------------------------------
|
||||||
// svgexport_data = []
|
|
||||||
// for k in static_css:
|
|
||||||
// svgframefile = filename_noext_ascii + ("_frame%04d.svg"%k)
|
|
||||||
// pngframefile = filename_noext_ascii + ("_frame%04d.png"%k)
|
|
||||||
// svgframefiles.append(svgframefile)
|
|
||||||
// pngframefiles.append(pngframefile)
|
|
||||||
// svgexport_data.append({"input": [abspath(svgframefile)],
|
|
||||||
// "output": [[abspath(pngframefile),
|
|
||||||
// "%d:%d"% (GIF_SIZE, GIF_SIZE)]]})
|
|
||||||
|
|
||||||
// style = E.style(static_css[k], id="style-Kanimaji")
|
if (GENERATE_GIF) {
|
||||||
// doc.getroot().insert(0, style)
|
// var svgframefiles = [];
|
||||||
// doc.write(svgframefile, pretty_print=True)
|
// var pngframefiles = [];
|
||||||
// doc.getroot().remove(style)
|
// var svgexport_data = [];
|
||||||
// print 'written %s' % svgframefile
|
|
||||||
|
final encoder = image.GifEncoder(dither: 0 as DitherKernel);
|
||||||
|
|
||||||
|
for (var k in staticCss.keys) {
|
||||||
|
final style = XmlBuilder()..element(
|
||||||
|
'style',
|
||||||
|
attributes: {'id': 'style-Kanimaji'},
|
||||||
|
);
|
||||||
|
doc.children.insert(0, style.buildFragment()..innerXml = staticCss[k]!);
|
||||||
|
|
||||||
|
final svg.DrawableRoot svgRoot = await svg.svg.fromSvgString(doc.outerXml, doc.outerXml);
|
||||||
|
final picture = await svgRoot.toPicture().toImage(gifSize, gifSize);
|
||||||
|
final ByteData? bytes = await picture.toByteData();
|
||||||
|
final intBytes = bytes!.buffer.asInt32List().toList();
|
||||||
|
image.Image frame = image.Image.fromBytes(gifSize, gifSize, intBytes);
|
||||||
|
encoder.addFrame(frame);
|
||||||
|
|
||||||
|
doc.children.removeAt(0);
|
||||||
|
|
||||||
|
// svgframefile = filename_noext_ascii + ("_frame%04d.svg"%k);
|
||||||
|
// pngframefile = filename_noext_ascii + ("_frame%04d.png"%k);
|
||||||
|
// svgframefiles.append(svgframefile)
|
||||||
|
// pngframefiles.append(pngframefile)
|
||||||
|
// svgexport_data.append({"input": [svgframefile.abs()],
|
||||||
|
// "output": [[abspath(pngframefile),
|
||||||
|
// "%d:%d"% (GIF_SIZE, GIF_SIZE)]]})
|
||||||
|
|
||||||
|
// style = E.style(staticCss[k], id="style-Kanimaji")
|
||||||
|
// doc.getroot().insert(0, style)
|
||||||
|
// doc.write(svgframefile, pretty_print=True)
|
||||||
|
// doc.getroot().remove(style)
|
||||||
|
// print 'written %s' % svgframefile
|
||||||
|
}
|
||||||
|
|
||||||
|
// encoder.finish();
|
||||||
|
|
||||||
|
File(outputFile).writeAsBytesSync(encoder.finish()!);
|
||||||
|
|
||||||
// // create json file
|
// // create json file
|
||||||
// svgexport_datafile = filename_noext_ascii+"_export_data.json"
|
// svgexport_datafile = filename_noext_ascii+"_export_data.json"
|
||||||
@@ -676,27 +776,3 @@ void createAnimation({
|
|||||||
// print('written $svgfile');
|
// print('written $svgfile');
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
void main(List<String> args) {
|
|
||||||
// createAnimation('assets/kanjivg/kanji/0f9b1.svg');
|
|
||||||
|
|
||||||
const kanji = '実例';
|
|
||||||
final fileList = [];
|
|
||||||
for (int k = 0; k < kanji.length; k++) {
|
|
||||||
createAnimation(
|
|
||||||
inputFile: 'assets/kanjivg/kanji/${kanji.codeUnits[k].toRadixString(16).padLeft(5, '0')}.svg',
|
|
||||||
outputFile: '${k+1}.svg',
|
|
||||||
);
|
|
||||||
fileList.add('${k+1}.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
File('index.html').writeAsStringSync(
|
|
||||||
'<html>' +
|
|
||||||
fileList.map((e) => File(e).readAsStringSync().replaceAll(']>', '')).join('\n') +
|
|
||||||
'</html>'
|
|
||||||
);
|
|
||||||
// createAnimation(
|
|
||||||
// inputFile: 'assets/kanjivg/kanji/060c5.svg',
|
|
||||||
// outputFile: 'test.svg',
|
|
||||||
// );
|
|
||||||
}
|
|
||||||
|
|||||||
0
lib/kanimaji/commandline.dart
Normal file
0
lib/kanimaji/commandline.dart
Normal file
6
lib/kanimaji/gif.dart
Normal file
6
lib/kanimaji/gif.dart
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
import 'package:kanimaji/kanimaji/options.dart';
|
||||||
|
|
||||||
|
Future<void> createGif(String kanji, KanimajiOptions options) async {
|
||||||
|
|
||||||
|
}
|
||||||
0
lib/kanimaji/js_svg.dart
Normal file
0
lib/kanimaji/js_svg.dart
Normal file
24
lib/kanimaji/options.dart
Normal file
24
lib/kanimaji/options.dart
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import 'dart:core';
|
||||||
|
|
||||||
|
import 'animate_kanji.dart';
|
||||||
|
|
||||||
|
class KanimajiOptions {
|
||||||
|
// String inputFile;
|
||||||
|
// String? outputFile;
|
||||||
|
TimingFunction timingFunction = TimingFunction.easeInOut;
|
||||||
|
double strokeBorderWidth = 4.5;
|
||||||
|
double strokeUnfilledWidth = 3;
|
||||||
|
double strokeFilledWidth = 3.1;
|
||||||
|
bool showBrush = true;
|
||||||
|
bool showBrushFrontBorder = true;
|
||||||
|
double brushWidth = 5.5;
|
||||||
|
double brushBorderWidth = 7;
|
||||||
|
double waitAfter = 1.5;
|
||||||
|
int gifSize = 500;
|
||||||
|
String strokeBorderColor = '#666';
|
||||||
|
String strokeUnfilledColor = '#EEE';
|
||||||
|
String strokeFillingColor = '#F00';
|
||||||
|
String strokeFilledColor = '#000';
|
||||||
|
String brushColor = '#F00';
|
||||||
|
String brushBorderColor = '#666';
|
||||||
|
}
|
||||||
3
lib/kanimaji/svg.dart
Normal file
3
lib/kanimaji/svg.dart
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
// String calculate_css() {
|
||||||
|
|
||||||
|
// }
|
||||||
@@ -3,6 +3,9 @@ description: A new Flutter package project.
|
|||||||
version: 0.0.1
|
version: 0.0.1
|
||||||
homepage:
|
homepage:
|
||||||
|
|
||||||
|
executables:
|
||||||
|
kanimaji: lib/kanimaji/animate_kanji
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.12.0 <3.0.0"
|
sdk: ">=2.12.0 <3.0.0"
|
||||||
flutter: ">=1.17.0"
|
flutter: ">=1.17.0"
|
||||||
@@ -11,6 +14,9 @@ dependencies:
|
|||||||
bisection: ^0.4.3+1
|
bisection: ^0.4.3+1
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
flutter_svg: ^1.0.3
|
||||||
|
gifencoder: ^1.0.0
|
||||||
|
image: ^3.1.3
|
||||||
xml: ^5.3.1
|
xml: ^5.3.1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
|
|||||||
@@ -42,11 +42,14 @@ import 'package:kanimaji/svg/path.dart';
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
class MoreOrLessEqualsToPoint extends Matcher {
|
class MoreOrLessEqualsToPoint extends Matcher {
|
||||||
const MoreOrLessEqualsToPoint();
|
static const double threshold = 0.000001;
|
||||||
|
final Point _expected;
|
||||||
|
|
||||||
|
const MoreOrLessEqualsToPoint(this._expected);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool matches(covariant Finder finder, Map<dynamic, dynamic> matchState)
|
bool matches(covariant Object? object, Map<dynamic, dynamic> matchState)
|
||||||
=> finder is Point && finder.x
|
=> object is Point && (_expected.x - object.x).abs() >=threshold && (_expected.y - object.y).abs() >=threshold;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Description describe(Description description) => description.add('in card');
|
Description describe(Description description) => description.add('in card');
|
||||||
@@ -83,19 +86,19 @@ test('Test lines', () {
|
|||||||
expect(line3.size(), moreOrLessEquals(500));
|
expect(line3.size(), moreOrLessEquals(500));
|
||||||
});
|
});
|
||||||
|
|
||||||
def test_equality(self):
|
// def test_equality(self):
|
||||||
// This is to test the __eq__ and __ne__ methods, so we can't use
|
// // This is to test the __eq__ and __ne__ methods, so we can't use
|
||||||
// assertEqual and assertNotEqual
|
// // assertEqual and assertNotEqual
|
||||||
line = Line(0j, 400 + 0j)
|
// line = Line(0j, 400 + 0j)
|
||||||
self.assertTrue(line == Line(0, 400))
|
// self.assertTrue(line == Line(0, 400))
|
||||||
self.assertTrue(line != Line(100, 400))
|
// self.assertTrue(line != Line(100, 400))
|
||||||
self.assertFalse(line == str(line))
|
// self.assertFalse(line == str(line))
|
||||||
self.assertTrue(line != str(line))
|
// self.assertTrue(line != str(line))
|
||||||
self.assertFalse(
|
// self.assertFalse(
|
||||||
CubicBezier(600 + 500j, 600 + 350j, 900 + 650j, 900 + 500j) == line
|
// CubicBezier(600 + 500j, 600 + 350j, 900 + 650j, 900 + 500j) == line
|
||||||
)
|
// )
|
||||||
|
|
||||||
});
|
// });
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user