@@ -0,0 +1,53 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
import 'kanjivg_path.dart';
|
||||
import 'kanjivg_path_group.dart';
|
||||
|
||||
/// A full KanjiVG entry for a single character.
|
||||
class KanjiVGEntry extends Equatable {
|
||||
/// The kanji or character this entry belongs to.
|
||||
final String character;
|
||||
|
||||
/// All stroke paths in drawing order.
|
||||
///
|
||||
/// Each path includes the rendered position of its stroke label.
|
||||
final List<KanjiVGPath> paths;
|
||||
|
||||
/// The hierarchical group structure of the entry.
|
||||
///
|
||||
/// These are not really used in mugiten at the moment, so querying them is optional.
|
||||
final List<KanjiVGPathGroup>? pathGroups;
|
||||
|
||||
KanjiVGEntry({
|
||||
required this.character,
|
||||
this.paths = const [],
|
||||
this.pathGroups = const [],
|
||||
}) : assert(
|
||||
paths.isEmpty ||
|
||||
(paths.first.pathId == 1 &&
|
||||
paths.last.pathId == paths.length &&
|
||||
paths.every((p) => p.pathId > 0)),
|
||||
'Paths must be listed in a strictly growing order without holes, starting from pathId 1.',
|
||||
);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [character, paths, pathGroups];
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'character': character,
|
||||
'paths': paths.map((e) => e.toJson()).toList(),
|
||||
'pathGroups': pathGroups?.map((e) => e.toJson()).toList(),
|
||||
};
|
||||
|
||||
factory KanjiVGEntry.fromJson(Map<String, dynamic> json) => KanjiVGEntry(
|
||||
character: json['character'] as String,
|
||||
paths: ((json['paths'] as List<dynamic>?) ?? const [])
|
||||
.map((e) => KanjiVGPath.fromJson(Map<String, dynamic>.from(e as Map)))
|
||||
.toList(),
|
||||
pathGroups: ((json['pathGroups'] as List<dynamic>?))
|
||||
?.map(
|
||||
(e) => KanjiVGPathGroup.fromJson(Map<String, dynamic>.from(e as Map)),
|
||||
)
|
||||
.toList(),
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// A stroke path from a KanjiVG entry.
|
||||
class KanjiVGPath extends Equatable {
|
||||
/// The path id within the KanjiVG entry.
|
||||
final int pathId;
|
||||
|
||||
/// The optional KanjiVG stroke type.
|
||||
final String? type;
|
||||
|
||||
/// The raw SVG `d` path string.
|
||||
final String svgPath;
|
||||
|
||||
/// The x-coordinate of the rendered stroke-label position.
|
||||
final double labelX;
|
||||
|
||||
/// The y-coordinate of the rendered stroke-label position.
|
||||
final double labelY;
|
||||
|
||||
KanjiVGPath({
|
||||
required this.pathId,
|
||||
required this.type,
|
||||
required this.svgPath,
|
||||
required this.labelX,
|
||||
required this.labelY,
|
||||
}) : assert(pathId > 0, 'pathId must be a positive integer. Found $pathId.'),
|
||||
assert(svgPath.isNotEmpty, 'svgPath cannot be empty.'),
|
||||
assert(
|
||||
labelX.isFinite,
|
||||
'labelX must be a finite number. Found $labelX.',
|
||||
),
|
||||
assert(
|
||||
labelY.isFinite,
|
||||
'labelY must be a finite number. Found $labelY.',
|
||||
);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [pathId, type, svgPath, labelX, labelY];
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'pathId': pathId,
|
||||
'type': type,
|
||||
'svgPath': svgPath,
|
||||
'labelX': labelX,
|
||||
'labelY': labelY,
|
||||
};
|
||||
|
||||
factory KanjiVGPath.fromJson(Map<String, dynamic> json) => KanjiVGPath(
|
||||
pathId: json['pathId'] as int,
|
||||
type: json['type'] as String?,
|
||||
svgPath: json['svgPath'] as String,
|
||||
labelX: (json['labelX'] as num).toDouble(),
|
||||
labelY: (json['labelY'] as num).toDouble(),
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:jadb/models/kanjivg/kanjivg_path.dart';
|
||||
|
||||
import 'kanjivg_path_group_position.dart';
|
||||
import 'kanjivg_radical.dart';
|
||||
|
||||
/// A hierarchical path-group from a KanjiVG entry.
|
||||
class KanjiVGPathGroup extends Equatable {
|
||||
/// The path-group id within the entry.
|
||||
final int groupId;
|
||||
|
||||
/// The paths directly contained in this group, in drawing order.
|
||||
final List<KanjiVGPath> paths;
|
||||
|
||||
/// Nested child groups.
|
||||
final List<KanjiVGPathGroup> children;
|
||||
|
||||
/// The value of the `kvg:element` attribute, if present.
|
||||
final String? element;
|
||||
|
||||
/// The original element before simplification, if present.
|
||||
final String? original;
|
||||
|
||||
/// Relative position of the group inside the character layout.
|
||||
final KanjiVGPathGroupPosition? position;
|
||||
|
||||
/// Radical classification for the group.
|
||||
final KanjiVGRadical? radical;
|
||||
|
||||
/// Part number for repeated elements, if present.
|
||||
final int? part;
|
||||
|
||||
KanjiVGPathGroup({
|
||||
required this.groupId,
|
||||
this.paths = const [],
|
||||
this.children = const [],
|
||||
this.element,
|
||||
this.original,
|
||||
this.position,
|
||||
this.radical,
|
||||
this.part,
|
||||
}) : assert(
|
||||
groupId >= 0,
|
||||
'groupId must be a non-negative integer. Found $groupId.',
|
||||
),
|
||||
assert(
|
||||
paths.isEmpty ||
|
||||
paths.fold<int>(
|
||||
0,
|
||||
(previousMax, path) => path.pathId > previousMax
|
||||
? path.pathId
|
||||
: throw ArgumentError(
|
||||
'Paths must be listed in a strictly growing order without holes. Found pathId ${path.pathId} after $previousMax.',
|
||||
),
|
||||
) ==
|
||||
paths.lastOrNull?.pathId,
|
||||
);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
groupId,
|
||||
paths,
|
||||
children,
|
||||
element,
|
||||
original,
|
||||
position,
|
||||
radical,
|
||||
part,
|
||||
];
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'groupId': groupId,
|
||||
'paths': paths.map((e) => e.toJson()).toList(),
|
||||
'children': children.map((e) => e.toJson()).toList(),
|
||||
'element': element,
|
||||
'original': original,
|
||||
'position': position?.toJson(),
|
||||
'radical': radical?.toJson(),
|
||||
'part': part,
|
||||
};
|
||||
|
||||
factory KanjiVGPathGroup.fromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => KanjiVGPathGroup(
|
||||
groupId: json['groupId'] as int,
|
||||
paths: ((json['paths'] as List<dynamic>?) ?? const [])
|
||||
.map((e) => KanjiVGPath.fromJson(Map<String, dynamic>.from(e as Map)))
|
||||
.toList(),
|
||||
children: ((json['children'] as List<dynamic>?) ?? const [])
|
||||
.map(
|
||||
(e) => KanjiVGPathGroup.fromJson(Map<String, dynamic>.from(e as Map)),
|
||||
)
|
||||
.toList(),
|
||||
element: json['element'] as String?,
|
||||
original: json['original'] as String?,
|
||||
position: KanjiVGPathGroupPosition.fromJson(json['position']),
|
||||
radical: KanjiVGRadical.fromJson(json['radical']),
|
||||
part: json['part'] as int?,
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/// Relative position tags used by KanjiVG path-groups.
|
||||
///
|
||||
/// In the original SVG files these come from the `kvg:position` attribute.
|
||||
/// The database stores the normalized enum name, while [svgValue] contains the
|
||||
/// raw KanjiVG attribute value.
|
||||
enum KanjiVGPathGroupPosition {
|
||||
upperA(svgValue: '⿵A'),
|
||||
upperB(svgValue: '⿵B'),
|
||||
lower1(svgValue: '⿶1'),
|
||||
lower2(svgValue: '⿶2'),
|
||||
bottom(svgValue: 'bottom'),
|
||||
kamae(svgValue: 'kamae'),
|
||||
kamaec(svgValue: 'kamaec'),
|
||||
left(svgValue: 'left'),
|
||||
middle(svgValue: 'middle'),
|
||||
nyo(svgValue: 'nyo'),
|
||||
nyoc(svgValue: 'nyoc'),
|
||||
right(svgValue: 'right'),
|
||||
tare(svgValue: 'tare'),
|
||||
tarec(svgValue: 'tarec'),
|
||||
top(svgValue: 'top');
|
||||
|
||||
final String svgValue;
|
||||
|
||||
const KanjiVGPathGroupPosition({required this.svgValue});
|
||||
|
||||
/// Parses either the normalized enum name stored in the database/JSON, or
|
||||
/// the raw KanjiVG SVG attribute value.
|
||||
static KanjiVGPathGroupPosition fromString(String value) => values.firstWhere(
|
||||
(e) => e.name == value || e.svgValue == value,
|
||||
orElse: () => throw Exception('Unknown position: $value'),
|
||||
);
|
||||
|
||||
static KanjiVGPathGroupPosition? fromNullableString(String? value) =>
|
||||
value == null ? null : fromString(value);
|
||||
|
||||
Object? toJson() => name;
|
||||
|
||||
static KanjiVGPathGroupPosition? fromJson(Object? json) =>
|
||||
fromNullableString(json as String?);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/// Radical classification tags used by KanjiVG path-groups.
|
||||
enum KanjiVGRadical {
|
||||
general,
|
||||
jis,
|
||||
nelson,
|
||||
tradit;
|
||||
|
||||
static KanjiVGRadical fromString(String value) => values.firstWhere(
|
||||
(e) => e.name == value,
|
||||
orElse: () => throw Exception('Unknown radical: $value'),
|
||||
);
|
||||
|
||||
static KanjiVGRadical? fromNullableString(String? value) =>
|
||||
value == null ? null : fromString(value);
|
||||
|
||||
Object? toJson() => name;
|
||||
|
||||
static KanjiVGRadical? fromJson(Object? json) =>
|
||||
fromNullableString(json as String?);
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:jadb/table_names/jmdict.dart';
|
||||
import 'package:jadb/table_names/kanjidic.dart';
|
||||
import 'package:jadb/table_names/kanjivg.dart';
|
||||
import 'package:jadb/table_names/radkfile.dart';
|
||||
import 'package:jadb/table_names/tanos_jlpt.dart';
|
||||
import 'package:sqflite_common/sqlite_api.dart';
|
||||
@@ -21,6 +22,7 @@ Future<void> verifyTablesWithDbConnection(DatabaseExecutor db) async {
|
||||
...KANJIDICTableNames.allTables,
|
||||
...RADKFILETableNames.allTables,
|
||||
...TanosJLPTTableNames.allTables,
|
||||
...KanjiVGTableNames.allTables,
|
||||
};
|
||||
|
||||
final missingTables = expectedTables.difference(tables);
|
||||
|
||||
Reference in New Issue
Block a user