hanzi-chai 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/affine.d.ts +8 -0
  2. package/dist/affine.js +153 -0
  3. package/dist/assembly.d.ts +63 -0
  4. package/dist/assembly.js +286 -0
  5. package/dist/bezier.d.ts +74 -0
  6. package/dist/bezier.js +140 -0
  7. package/dist/classifier.d.ts +109 -0
  8. package/dist/classifier.js +108 -0
  9. package/dist/component.d.ts +115 -0
  10. package/dist/component.js +278 -0
  11. package/dist/compound.d.ts +64 -0
  12. package/dist/compound.js +652 -0
  13. package/dist/config.d.ts +236 -0
  14. package/dist/config.js +48 -0
  15. package/dist/data/cjk.txt +20902 -0
  16. package/dist/data/data/cjk.txt +20902 -0
  17. package/dist/data/data/dictionary.txt +69429 -0
  18. package/dist/data/data/frequency.txt +263137 -0
  19. package/dist/data/data/key_distribution.txt +46 -0
  20. package/dist/data/data/pair_equivalence.txt +2116 -0
  21. package/dist/data/data/repertoire.json.deflate +0 -0
  22. package/dist/data/data/tygf.txt +8105 -0
  23. package/dist/data/dictionary.txt +69429 -0
  24. package/dist/data/frequency.txt +263137 -0
  25. package/dist/data/key_distribution.txt +46 -0
  26. package/dist/data/pair_equivalence.txt +2116 -0
  27. package/dist/data/repertoire.json.deflate +0 -0
  28. package/dist/data/tygf.txt +8105 -0
  29. package/dist/data.d.ts +195 -0
  30. package/dist/data.js +18 -0
  31. package/dist/degenerator.d.ts +21 -0
  32. package/dist/degenerator.js +134 -0
  33. package/dist/element.d.ts +56 -0
  34. package/dist/element.js +225 -0
  35. package/dist/equivalence.d.ts +21 -0
  36. package/dist/equivalence.js +74 -0
  37. package/dist/index.d.ts +45 -0
  38. package/dist/index.js +126 -0
  39. package/dist/index.mjs +3553 -0
  40. package/dist/mathjs.d.ts +10 -0
  41. package/dist/mathjs.js +9 -0
  42. package/dist/repertoire.d.ts +75 -0
  43. package/dist/repertoire.js +406 -0
  44. package/dist/selector.d.ts +110 -0
  45. package/dist/selector.js +310 -0
  46. package/dist/topology.d.ts +41 -0
  47. package/dist/topology.js +209 -0
  48. package/dist/transformer.d.ts +18 -0
  49. package/dist/transformer.js +111 -0
  50. package/dist/utils.d.ts +107 -0
  51. package/dist/utils.js +546 -0
  52. package/package.json +34 -0
package/dist/bezier.js ADDED
@@ -0,0 +1,140 @@
1
+ import { add, subtract, mean, multiply, divide, distance, dot } from "./mathjs.js";
2
+ export const sortTwoNumbers = (ar) => ar.sort((a, b) => a - b);
3
+ const getBoundingBox = (a) => [a.controls[0], a.controls[a.controls.length - 1]];
4
+ const getIntervalOnOrientation = (a) => {
5
+ const start = a.controls[0];
6
+ const end = a.controls.at(-1);
7
+ const i1 = sortTwoNumbers([start[0], end[0]]);
8
+ const i2 = sortTwoNumbers([start[1], end[1]]);
9
+ if (a.orientation === "horizontal") {
10
+ return [i1, i2];
11
+ }
12
+ return [i2, i1];
13
+ };
14
+ const evaluate = (a, t) => {
15
+ if (a.type === "linear") {
16
+ return add(multiply(1 - t, a.controls[0]), multiply(t, a.controls[1]));
17
+ }
18
+ if (a.type === "arc") {
19
+ return [0, 0];
20
+ }
21
+ const v01 = add(multiply((1 - t) ** 3, a.controls[0]), multiply(3 * (1 - t) ** 2 * t, a.controls[1]));
22
+ const v23 = add(multiply(3 * (1 - t) * t ** 2, a.controls[2]), multiply(t ** 3, a.controls[3]));
23
+ return add(v01, v23);
24
+ };
25
+ const makeCurve = (start, { command, parameterList }) => {
26
+ if (command === "a") {
27
+ return {
28
+ type: "arc",
29
+ orientation: "horizontal",
30
+ controls: [start, start],
31
+ };
32
+ }
33
+ if (command === "c" || command === "z") {
34
+ const p1 = add(start, parameterList.slice(0, 2));
35
+ const p2 = add(start, parameterList.slice(2, 4));
36
+ const p3 = add(start, parameterList.slice(4));
37
+ return {
38
+ type: "cubic",
39
+ orientation: command === "c" ? "vertical" : "horizontal",
40
+ controls: [start, p1, p2, p3],
41
+ };
42
+ }
43
+ let p1;
44
+ switch (command) {
45
+ case "h":
46
+ p1 = add(start, [parameterList[0], 0]);
47
+ break;
48
+ case "v":
49
+ p1 = add(start, [0, parameterList[0]]);
50
+ break;
51
+ }
52
+ return {
53
+ type: "linear",
54
+ orientation: command === "v" ? "vertical" : "horizontal",
55
+ controls: [start, p1],
56
+ };
57
+ };
58
+ const render = ({ feature, start, curveList }) => {
59
+ const r = { feature, curveList: [] };
60
+ let previousPosition = start;
61
+ for (const draw of curveList) {
62
+ const curve = makeCurve(previousPosition, draw);
63
+ previousPosition =
64
+ curve.type === "linear"
65
+ ? curve.controls[1]
66
+ : curve.type === "arc"
67
+ ? curve.controls[1]
68
+ : curve.controls[3];
69
+ r.curveList.push(curve);
70
+ }
71
+ return r;
72
+ };
73
+ const area = (p, q) => p[0] * q[1] - p[1] * q[0];
74
+ const getIntervalPosition = (i, j) => {
75
+ const [imin, imax] = i.sort((a, b) => a - b);
76
+ const [jmin, jmax] = j.sort((a, b) => a - b);
77
+ // totally disjoint
78
+ if (imax < jmin)
79
+ return -1;
80
+ if (imin > jmax)
81
+ return 1;
82
+ // generally smaller or larger
83
+ if (imin < jmin && imax < jmax)
84
+ return -0.5;
85
+ if (imin > jmin && imax > jmax)
86
+ return +0.5;
87
+ return 0;
88
+ };
89
+ const findCrossPoint = (a, at, b, bt) => {
90
+ const [astart, aend] = [evaluate(a, at[0]), evaluate(a, at[1])];
91
+ const [bstart, bend] = [evaluate(b, bt[0]), evaluate(b, bt[1])];
92
+ const xposition = getIntervalPosition([astart[0], aend[0]], [bstart[0], bend[0]]);
93
+ const yposition = getIntervalPosition([astart[1], aend[1]], [bstart[1], bend[1]]);
94
+ const totallyDisjoint = [-1, 1];
95
+ if (totallyDisjoint.includes(xposition) ||
96
+ totallyDisjoint.includes(yposition))
97
+ return undefined;
98
+ const [alength, blength] = [distance(astart, aend), distance(bstart, bend)];
99
+ const threshold = 1;
100
+ if (alength < threshold && blength < threshold)
101
+ return divide(add(add(astart, aend), add(bstart, bend)), 4);
102
+ const [atmid, btmid] = [mean(at), mean(bt)];
103
+ const a_firsthalf = [at[0], atmid];
104
+ const a_secondhalf = [atmid, at[1]];
105
+ const b_firsthalf = [bt[0], btmid];
106
+ const b_secondhalf = [btmid, bt[1]];
107
+ return (findCrossPoint(a, a_firsthalf, b, b_firsthalf) ||
108
+ findCrossPoint(a, a_firsthalf, b, b_secondhalf) ||
109
+ findCrossPoint(a, a_secondhalf, b, b_firsthalf) ||
110
+ findCrossPoint(a, a_secondhalf, b, b_secondhalf));
111
+ };
112
+ /**
113
+ * Determine if the point is on a given segment
114
+ * @param from - starting point
115
+ * @param to - end point
116
+ * @param point - another point
117
+ * @returns
118
+ */
119
+ const isCollinear = (from, to, point) => {
120
+ const [u, v] = [subtract(to, point), subtract(from, point)];
121
+ return area(u, v) === 0 && dot(u, v) < 0;
122
+ };
123
+ const curveLength = (curve) => {
124
+ const [start, end] = getBoundingBox(curve);
125
+ return distance(start, end);
126
+ };
127
+ const sort = (a) => a.sort((a, b) => a - b);
128
+ const contains = (r1, r2) => {
129
+ return r1[0] <= r2[0] && r1[1] >= r2[1];
130
+ };
131
+ const _isBoundedBy = (curve, xrange, yrange) => {
132
+ const [start, end] = getBoundingBox(curve);
133
+ const thisXRange = sort([start[0], end[0]]);
134
+ const thisYRange = sort([start[1], end[1]]);
135
+ return contains(xrange, thisXRange) && contains(yrange, thisYRange);
136
+ };
137
+ const isBoundedBy = (stroke, xrange, yrange) => {
138
+ return stroke.curveList.every((x) => _isBoundedBy(x, xrange, yrange));
139
+ };
140
+ export { getBoundingBox, getIntervalOnOrientation, getIntervalPosition, render, makeCurve, area, curveLength, findCrossPoint, isCollinear, isBoundedBy, };
@@ -0,0 +1,109 @@
1
+ import type { Draw } from "./data.js";
2
+ /**
3
+ * 笔画名称参考 Unicode 中的 CJK 笔画块(共 38 种笔画)
4
+ * 本系统共 41 种笔画,和 CJK 笔画的区别在于:
5
+ * 1. U+31D2(P)称为平撇,而 U+31D3(SP)称为撇
6
+ * 2. 仿照撇,在捺中分出了平捺、点中分出了平点
7
+ * 3. 添加「特殊笔画」分类,用于表示一些无法归类的笔画
8
+ * https://en.wikipedia.org/wiki/CJK_Strokes_(Unicode_block)
9
+ * 分类参考 GF2001-2001 给出的笔画分类规范,将笔画分为 5 类,用数字表示
10
+ */
11
+ export declare const classifier: {
12
+ 横: number;
13
+ 提: number;
14
+ 竖: number;
15
+ 竖钩: number;
16
+ 撇: number;
17
+ 平撇: number;
18
+ 点: number;
19
+ 平点: number;
20
+ 捺: number;
21
+ 平捺: number;
22
+ 挑捺: number;
23
+ 横钩: number;
24
+ 横撇: number;
25
+ 横折: number;
26
+ 横折钩: number;
27
+ 横斜钩: number;
28
+ 横折提: number;
29
+ 横折折: number;
30
+ 横折弯: number;
31
+ 横撇弯钩: number;
32
+ 横折弯钩: number;
33
+ 横斜弯钩: number;
34
+ 横折折撇: number;
35
+ 横折折折: number;
36
+ 横折折折钩: number;
37
+ 竖提: number;
38
+ 竖折: number;
39
+ 竖弯: number;
40
+ 竖弯左: number;
41
+ 竖弯钩: number;
42
+ 竖折撇: number;
43
+ 竖折折钩: number;
44
+ 竖折折: number;
45
+ 撇点: number;
46
+ 撇折: number;
47
+ 弯钩: number;
48
+ 斜钩: number;
49
+ 撇钩: number;
50
+ 卧钩: number;
51
+ 圈: number;
52
+ 特殊笔画: number;
53
+ };
54
+ /**
55
+ * 给定方案配置文件中的不完整的分类器,将其与默认的分类器合并
56
+ * 得到一个完整的分类器
57
+ */
58
+ export declare const mergeClassifier: (partialClassifier?: Record<Feature, number>) => {
59
+ 横: number;
60
+ 提: number;
61
+ 竖: number;
62
+ 竖钩: number;
63
+ 撇: number;
64
+ 平撇: number;
65
+ 点: number;
66
+ 平点: number;
67
+ 捺: number;
68
+ 平捺: number;
69
+ 挑捺: number;
70
+ 横钩: number;
71
+ 横撇: number;
72
+ 横折: number;
73
+ 横折钩: number;
74
+ 横斜钩: number;
75
+ 横折提: number;
76
+ 横折折: number;
77
+ 横折弯: number;
78
+ 横撇弯钩: number;
79
+ 横折弯钩: number;
80
+ 横斜弯钩: number;
81
+ 横折折撇: number;
82
+ 横折折折: number;
83
+ 横折折折钩: number;
84
+ 竖提: number;
85
+ 竖折: number;
86
+ 竖弯: number;
87
+ 竖弯左: number;
88
+ 竖弯钩: number;
89
+ 竖折撇: number;
90
+ 竖折折钩: number;
91
+ 竖折折: number;
92
+ 撇点: number;
93
+ 撇折: number;
94
+ 弯钩: number;
95
+ 斜钩: number;
96
+ 撇钩: number;
97
+ 卧钩: number;
98
+ 圈: number;
99
+ 特殊笔画: number;
100
+ };
101
+ export type Feature = keyof typeof classifier;
102
+ export type Classifier = typeof classifier;
103
+ /**
104
+ * 特定类型的笔画在数据库中的表示所包含的 SVG 命令的类别和数量是固定的
105
+ * 比如,横折折折钩的表示一定是 h v h v,不会有其他可能性
106
+ * 另外,平撇、平点、平捺的命令是 z,这个不是规范的 SVG 命令,只是为了和撇、点、捺的 c 区分
107
+ * z 和 c 的实际效果是一样的
108
+ */
109
+ export declare const schema: Record<Feature, Draw["command"][]>;
@@ -0,0 +1,108 @@
1
+ /**
2
+ * 笔画名称参考 Unicode 中的 CJK 笔画块(共 38 种笔画)
3
+ * 本系统共 41 种笔画,和 CJK 笔画的区别在于:
4
+ * 1. U+31D2(P)称为平撇,而 U+31D3(SP)称为撇
5
+ * 2. 仿照撇,在捺中分出了平捺、点中分出了平点
6
+ * 3. 添加「特殊笔画」分类,用于表示一些无法归类的笔画
7
+ * https://en.wikipedia.org/wiki/CJK_Strokes_(Unicode_block)
8
+ * 分类参考 GF2001-2001 给出的笔画分类规范,将笔画分为 5 类,用数字表示
9
+ */
10
+ export const classifier = {
11
+ 横: 1,
12
+ 提: 1,
13
+ 竖: 2,
14
+ 竖钩: 2,
15
+ 撇: 3,
16
+ 平撇: 3,
17
+ 点: 4,
18
+ 平点: 4,
19
+ 捺: 4,
20
+ 平捺: 4,
21
+ 挑捺: 4,
22
+ 横钩: 5,
23
+ 横撇: 5,
24
+ 横折: 5,
25
+ 横折钩: 5,
26
+ 横斜钩: 5,
27
+ 横折提: 5,
28
+ 横折折: 5,
29
+ 横折弯: 5,
30
+ 横撇弯钩: 5,
31
+ 横折弯钩: 5,
32
+ 横斜弯钩: 5,
33
+ 横折折撇: 5,
34
+ 横折折折: 5,
35
+ 横折折折钩: 5,
36
+ 竖提: 5,
37
+ 竖折: 5,
38
+ 竖弯: 5,
39
+ 竖弯左: 5,
40
+ 竖弯钩: 5,
41
+ 竖折撇: 5,
42
+ 竖折折钩: 5,
43
+ 竖折折: 5,
44
+ 撇点: 5,
45
+ 撇折: 5,
46
+ 弯钩: 5,
47
+ 斜钩: 5,
48
+ 撇钩: 5,
49
+ 卧钩: 5,
50
+ 圈: 5,
51
+ 特殊笔画: 5,
52
+ };
53
+ /**
54
+ * 给定方案配置文件中的不完整的分类器,将其与默认的分类器合并
55
+ * 得到一个完整的分类器
56
+ */
57
+ export const mergeClassifier = (partialClassifier) => {
58
+ return { ...classifier, ...(partialClassifier ?? {}) };
59
+ };
60
+ /**
61
+ * 特定类型的笔画在数据库中的表示所包含的 SVG 命令的类别和数量是固定的
62
+ * 比如,横折折折钩的表示一定是 h v h v,不会有其他可能性
63
+ * 另外,平撇、平点、平捺的命令是 z,这个不是规范的 SVG 命令,只是为了和撇、点、捺的 c 区分
64
+ * z 和 c 的实际效果是一样的
65
+ */
66
+ export const schema = {
67
+ 横: ["h"],
68
+ 提: ["h"], // 美化:提
69
+ 竖: ["v"],
70
+ 竖钩: ["v"], // 美化:钩
71
+ 撇: ["c"],
72
+ 平撇: ["z"],
73
+ 点: ["c"],
74
+ 平点: ["z"],
75
+ 捺: ["c"],
76
+ 平捺: ["z"],
77
+ 挑捺: ["h", "c"],
78
+ 横钩: ["h"], // 美化:钩
79
+ 横撇: ["h", "c"],
80
+ 横折: ["h", "v"],
81
+ 横折钩: ["h", "v"], // 美化:钩
82
+ 横斜钩: ["h", "c"], // 美化:钩
83
+ 横折提: ["h", "v", "h"], // 美化:提
84
+ 横折折: ["h", "v", "h"],
85
+ 横折弯: ["h", "v", "h"],
86
+ 横撇弯钩: ["h", "c", "v"], // 美化:钩
87
+ 横折弯钩: ["h", "v", "h"], // 美化:钩
88
+ 横斜弯钩: ["h", "c"], // 美化:钩
89
+ 横折折撇: ["h", "v", "h", "c"],
90
+ 横折折折: ["h", "v", "h", "v"],
91
+ 横折折折钩: ["h", "v", "h", "v"], // 美化:钩
92
+ 竖提: ["v", "h"], // 美化:提
93
+ 竖折: ["v", "h"],
94
+ 竖弯: ["v", "h"],
95
+ 竖弯左: ["v", "h"],
96
+ 竖弯钩: ["v", "h"], // 美化:钩
97
+ 竖折撇: ["v", "h", "c"],
98
+ 竖折折钩: ["v", "h", "v"], // 美化:钩
99
+ 竖折折: ["v", "h", "v"],
100
+ 撇点: ["c", "c"],
101
+ 撇折: ["c", "h"],
102
+ 弯钩: ["v"], // 美化:钩
103
+ 斜钩: ["c"], // 美化:钩
104
+ 撇钩: ["c"], // 美化:钩
105
+ 卧钩: ["c"], // 美化:钩
106
+ 圈: ["a"],
107
+ 特殊笔画: ["c"],
108
+ };
@@ -0,0 +1,115 @@
1
+ import type { SieveName } from "./config.js";
2
+ import type { Repertoire, PrimitiveRepertoire, SVGGlyph, Component, Identity } from "./data.js";
3
+ import type { RenderedGlyph, Topology } from "./topology.js";
4
+ import { type Classifier } from "./classifier.js";
5
+ import type { AnalysisConfig } from "./repertoire.js";
6
+ export declare class InvalidGlyphError extends Error {
7
+ }
8
+ export declare const makeIntervalSum: (strokes: number, rootsSet: Set<number>) => Set<number>;
9
+ /**
10
+ * 根据一个部件中包含的所有字根的情况,导出所有可能的拆分方案
11
+ *
12
+ * @param strokes - 部件笔画数
13
+ * @param roots - 部件包含的字根列表,其中每个字根用二进制表示
14
+ *
15
+ * 函数通过递归的方式,每次选取剩余部分的第一笔,然后在字根列表中找到包含这个笔画的所有字根
16
+ * 将这些可能性与此前已经拆分的部分组合,得到新的拆分方案
17
+ * 直到所有的笔画都使用完毕
18
+ */
19
+ export declare const generateSchemes: (strokes: number, roots: number[], requiredRoots: Set<number>) => number[][];
20
+ /**
21
+ * 计算后的部件 ComputedComponent
22
+ *
23
+ * 在基本部件 BasicComponent 的基础上,将 SVG 命令转换为参数曲线
24
+ * 再基于参数曲线计算拓扑
25
+ */
26
+ export interface ComputedComponent {
27
+ name: string;
28
+ glyph: RenderedGlyph;
29
+ topology: Topology;
30
+ }
31
+ export declare class NoSchemeError extends Error {
32
+ }
33
+ export declare class MultipleSchemeError extends Error {
34
+ }
35
+ /**
36
+ * 通过自动拆分算法,给定字根列表,对部件进行拆分
37
+ * @param component - 部件
38
+ * @param rootData - 字根列表
39
+ * @param config - 配置
40
+ * @param classifier - 笔画分类器
41
+ *
42
+ * 如果拆分唯一,则返回拆分结果;否则返回错误
43
+ *
44
+ * @returns 拆分结果
45
+ * @throws NoSchemeError 无拆分方案
46
+ * @throws MultipleSchemeError 多个拆分方案
47
+ */
48
+ export declare const getComponentScheme: (component: ComputedComponent, rootData: ComputedComponent[], config: AnalysisConfig, classifier: Classifier) => ComponentAnalysis | NoSchemeError | MultipleSchemeError;
49
+ /**
50
+ * 部件的拆分结果
51
+ */
52
+ export type ComponentAnalysis = ComponentBasicAnalysis | ComponentGenuineAnalysis;
53
+ export interface SchemeWithData {
54
+ scheme: number[];
55
+ roots: string[];
56
+ evaluation: Map<SieveName, number | number[]>;
57
+ optional: boolean;
58
+ }
59
+ export interface CommonAnalysis {
60
+ sequence: string[];
61
+ sequencePartIndex?: number[];
62
+ full: string[];
63
+ }
64
+ /**
65
+ * 部件本身是字根字,或者是由自定义部件拆分指定的无理拆分,无拆分细节
66
+ */
67
+ export interface ComponentBasicAnalysis extends CommonAnalysis {
68
+ strokes: number;
69
+ operator: undefined;
70
+ }
71
+ /**
72
+ * 部件通过自动拆分算法分析得到的拆分结果的全部细节
73
+ */
74
+ export interface ComponentGenuineAnalysis extends ComponentBasicAnalysis {
75
+ map: Map<number, string>;
76
+ best: SchemeWithData;
77
+ schemes: SchemeWithData[];
78
+ }
79
+ export type ComponentResults = Map<string, ComponentAnalysis>;
80
+ /**
81
+ * 递归渲染一个部件(基本部件或者衍生部件)
82
+ * 如果是基本部件就直接返回,如果是衍生部件则先渲染源字的图形,然后解引用得到这个部件的图形
83
+ *
84
+ * @param component - 部件
85
+ * @param repertoire - 原始字符集
86
+ * @param glyphCache - 部件缓存
87
+ *
88
+ * @returns 部件的 SVG 图形
89
+ * @throws InvalidGlyphError 无法渲染
90
+ */
91
+ export declare const recursiveRenderComponent: (component: Component | Identity, repertoire: PrimitiveRepertoire, glyphCache?: Map<string, SVGGlyph>, depth?: number) => SVGGlyph | InvalidGlyphError;
92
+ /**
93
+ * 把一个基本部件从 SVG 图形出发,先渲染成 Bezier 图形,然后计算拓扑
94
+ *
95
+ * 这样,便于后续的字根认同计算
96
+ */
97
+ export declare const computeComponent: (name: string, svgglyph: SVGGlyph) => ComputedComponent;
98
+ /**
99
+ * 将所有的字根都计算成 ComputedComponent
100
+ *
101
+ * @param repertoire - 字符集
102
+ * @param config - 配置
103
+ *
104
+ * @returns 所有计算后字根的列表
105
+ */
106
+ export declare const renderRootList: (repertoire: Repertoire, elements: string[]) => Map<string, ComputedComponent>;
107
+ /**
108
+ * 对所有部件进行拆分
109
+ *
110
+ * @param repertoire - 字符集
111
+ * @param config - 配置
112
+ *
113
+ * @returns 拆分结果和无法拆分的汉字列表
114
+ */
115
+ export declare const disassembleComponents: (repertoire: Repertoire, config: AnalysisConfig, characters: string[], components: Set<string>) => [ComponentResults, string[]];