@woosh/meep-engine 2.95.1 → 2.96.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.
- package/build/meep.cjs +172 -137
- package/build/meep.min.js +1 -1
- package/build/meep.module.js +172 -137
- package/package.json +1 -2
- package/src/core/geom/Quaternion.d.ts.map +1 -1
- package/src/core/geom/Quaternion.js +14 -8
- package/src/core/localization/Localization.d.ts.map +1 -1
- package/src/core/localization/Localization.js +11 -11
- package/src/core/math/epsilonEquals.d.ts.map +1 -1
- package/src/core/math/epsilonEquals.js +1 -0
- package/src/core/math/epsilonEquals.spec.d.ts +2 -0
- package/src/core/math/epsilonEquals.spec.d.ts.map +1 -0
- package/src/core/math/epsilonEquals.spec.js +17 -0
- package/src/core/primitives/strings/string_compute_similarity.d.ts +6 -0
- package/src/core/primitives/strings/string_compute_similarity.d.ts.map +1 -0
- package/src/core/primitives/strings/string_compute_similarity.js +7 -0
- package/src/core/primitives/strings/string_jaro_winkler.d.ts +8 -0
- package/src/core/primitives/strings/string_jaro_winkler.d.ts.map +1 -0
- package/src/core/primitives/strings/string_jaro_winkler.js +153 -0
- package/src/core/primitives/strings/string_jaro_winkler.spec.d.ts +2 -0
- package/src/core/primitives/strings/string_jaro_winkler.spec.d.ts.map +1 -0
- package/src/core/primitives/strings/string_jaro_winkler.spec.js +39 -0
- package/src/engine/graphics/ecs/mesh/SkeletonUtils.d.ts.map +1 -1
- package/src/engine/graphics/ecs/mesh/SkeletonUtils.js +5 -14
- package/src/engine/graphics/ecs/mesh/skeleton/BoneMapping.js +3 -3
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { BitSet } from "../../binary/BitSet.js";
|
|
2
|
+
import { max2 } from "../../math/max2.js";
|
|
3
|
+
import { min2 } from "../../math/min2.js";
|
|
4
|
+
import { min3 } from "../../math/min3.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Calculate the Jaro-Winkler distance between two strings
|
|
8
|
+
* @param {string} first The string to compare
|
|
9
|
+
* @param {string} second The string to compare with
|
|
10
|
+
* @returns {number} similarity score, higher value means strings are more similar
|
|
11
|
+
*/
|
|
12
|
+
export function string_jaro_winkler(first, second) {
|
|
13
|
+
const l1 = first.length;
|
|
14
|
+
const l2 = second.length;
|
|
15
|
+
|
|
16
|
+
if (l1 === 0 && l2 === 0) {
|
|
17
|
+
// special case for empty string
|
|
18
|
+
return 1;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const matches1 = BitSet.fixedSize(l1);
|
|
22
|
+
const matches2 = BitSet.fixedSize(l2);
|
|
23
|
+
|
|
24
|
+
const matches = getMatching(first, second, matches1, matches2);
|
|
25
|
+
|
|
26
|
+
if (matches <= 0) {
|
|
27
|
+
return 0;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Calculate the Jaro distance:
|
|
31
|
+
const transpositions = getTranspositions(first, second, matches1, matches2);
|
|
32
|
+
const similarity = (matches / l1 + matches / l2 + (matches - transpositions) / matches) / 3;
|
|
33
|
+
|
|
34
|
+
// Transform to Jaro-Winkler:
|
|
35
|
+
// Prefix scale gives more favorable ratings to strings that share common prefixes:
|
|
36
|
+
const prefix_scale = 0.1;
|
|
37
|
+
const prefix = getPrefix(first, second, min3(l1, l2, 4));
|
|
38
|
+
return similarity + prefix * prefix_scale * (1 - similarity);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Find matching characters in both strings according to Jaro algorithm
|
|
43
|
+
* @param {string} a1
|
|
44
|
+
* @param {string} a2
|
|
45
|
+
* @param {BitSet} matches1
|
|
46
|
+
* @param {BitSet} matches2
|
|
47
|
+
* @return {number}
|
|
48
|
+
*/
|
|
49
|
+
function getMatching(a1, a2, matches1, matches2) {
|
|
50
|
+
const a1_length = a1.length;
|
|
51
|
+
const a2_length = a2.length;
|
|
52
|
+
|
|
53
|
+
// Window is modified to work with string of length 1
|
|
54
|
+
const matchWindow = max2(
|
|
55
|
+
0,
|
|
56
|
+
Math.floor(max2(a1_length, a2_length) * 0.5) - 1
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
let matches = 0;
|
|
60
|
+
|
|
61
|
+
// Loop to find matched characters:
|
|
62
|
+
for (let index1 = 0; index1 < a1_length; index1++) {
|
|
63
|
+
|
|
64
|
+
// Use the highest of the window diff and the min of the window and string 2 length:
|
|
65
|
+
const start = max2(0, index1 - matchWindow);
|
|
66
|
+
const end = min2(index1 + matchWindow + 1, a2_length);
|
|
67
|
+
|
|
68
|
+
// Iterate second string index:
|
|
69
|
+
for (let index2 = start; index2 < end; index2++) {
|
|
70
|
+
|
|
71
|
+
// If second string character already matched, skip:
|
|
72
|
+
if (matches2.get(index2)) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// If the characters don't match, skip:
|
|
77
|
+
if (a1.charAt(index1) !== a2.charAt(index2)) {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Assume match if the above 2 checks don't continue:
|
|
82
|
+
matches1.set(index1, true);
|
|
83
|
+
matches2.set(index2, true);
|
|
84
|
+
|
|
85
|
+
// Add matches by 1, break inner loop:
|
|
86
|
+
++matches;
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return matches;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Calculate the number of transpositions between the two words
|
|
96
|
+
* @param {string} a1 The first string to compare
|
|
97
|
+
* @param {string} a2 The second string to compare
|
|
98
|
+
* @param {BitSet} matches1
|
|
99
|
+
* @param {BitSet} matches2
|
|
100
|
+
*/
|
|
101
|
+
function getTranspositions(a1, a2, matches1, matches2) {
|
|
102
|
+
let transpositions = 0;
|
|
103
|
+
|
|
104
|
+
// Loop to find transpositions:
|
|
105
|
+
const a1_length = a1.length;
|
|
106
|
+
const a2_length = a2.length;
|
|
107
|
+
|
|
108
|
+
for (let i1 = 0, i2 = 0; i1 < a1_length; i1++) {
|
|
109
|
+
// If a non-matching character was found, skip:
|
|
110
|
+
if (matches1.get(i1) === false) {
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Move i2 index to the next match:
|
|
115
|
+
while (
|
|
116
|
+
i2 < a2_length
|
|
117
|
+
&& matches2.get(i2) === false
|
|
118
|
+
) {
|
|
119
|
+
i2++;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// If the characters don't match, increase transposition:
|
|
123
|
+
if (a1.charAt(i1) !== a2.charAt(i2)) {
|
|
124
|
+
transpositions++;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Iterate i2 index normally:
|
|
128
|
+
i2++;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return Math.floor(transpositions * 0.5);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Counts the number of common characters at the beginning
|
|
136
|
+
* of each word up to a maximum of 4
|
|
137
|
+
* @param {string} a1 The first string to compare
|
|
138
|
+
* @param {string} a2 The second string to compare
|
|
139
|
+
* @param {number} character_limit
|
|
140
|
+
* @returns {number}
|
|
141
|
+
*/
|
|
142
|
+
function getPrefix(a1, a2, character_limit) {
|
|
143
|
+
|
|
144
|
+
let p = 0;
|
|
145
|
+
|
|
146
|
+
for (; p < character_limit; p++) {
|
|
147
|
+
if (a1.charAt(p) !== a2.charAt(p)) {
|
|
148
|
+
return p;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return ++p;
|
|
153
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"string_jaro_winkler.spec.d.ts","sourceRoot":"","sources":["../../../../../src/core/primitives/strings/string_jaro_winkler.spec.js"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { string_jaro_winkler } from "./string_jaro_winkler.js";
|
|
2
|
+
|
|
3
|
+
test("empty strings", () => {
|
|
4
|
+
|
|
5
|
+
expect(string_jaro_winkler("", "")).toBe(1)
|
|
6
|
+
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
test("whitespace", () => {
|
|
10
|
+
expect(string_jaro_winkler(" ", " ")).toBe(1);
|
|
11
|
+
|
|
12
|
+
expect(string_jaro_winkler("\n", "\n")).toBe(1);
|
|
13
|
+
expect(string_jaro_winkler("\t", "\t")).toBe(1);
|
|
14
|
+
|
|
15
|
+
expect(string_jaro_winkler(" ", " ")).toBeGreaterThan(0);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test("negative cases", () => {
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
expect(string_jaro_winkler("a", "")).toBe(0);
|
|
22
|
+
expect(string_jaro_winkler("", "a")).toBe(0);
|
|
23
|
+
|
|
24
|
+
expect(string_jaro_winkler("a", "b")).toBe(0);
|
|
25
|
+
expect(string_jaro_winkler("b", "a")).toBe(0);
|
|
26
|
+
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test("known cases", () => {
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
expect(string_jaro_winkler("a", "a")).toBeCloseTo(1);
|
|
33
|
+
expect(string_jaro_winkler("aa", "aa")).toBeCloseTo(1);
|
|
34
|
+
expect(string_jaro_winkler("abc", "abc")).toBeCloseTo(1);
|
|
35
|
+
|
|
36
|
+
expect(string_jaro_winkler("DwAyNE", "DuANE")).toBeCloseTo(0.84);
|
|
37
|
+
expect(string_jaro_winkler("TRATE", "TRACE")).toBeCloseTo(0.906667);
|
|
38
|
+
|
|
39
|
+
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SkeletonUtils.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/graphics/ecs/mesh/SkeletonUtils.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"SkeletonUtils.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/graphics/ecs/mesh/SkeletonUtils.js"],"names":[],"mappings":"AAOA;;;;;GAKG;AACH,2DAHW,MAAM,GACJ,UAAU,GAAG,IAAI,CAuC7B;AAiCD;;;;GAIG;AACH,mEAFa,WAAS,SAAS,CAgB9B;AAED;;;;;GAKG;AACH,qEAHW,yBAAuB,GACtB,IAAI,GAAC,IAAI,CAkCpB;AAED;;;;;GAKG;AACH,iEAHW,yBAAuB,GACrB,IAAI,GAAG,IAAI,CAUvB;AAGD;;;;;GAKG;AACH,0EAFa,IAAI,GAAG,IAAI,CA+BvB;AAID;;;;;GAKG;AACH,sEAFa,IAAI,GAAG,IAAI,CA8BvB;qBA1OoB,OAAO"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { distance as levenshtein_distance } from "fastest-levenshtein";
|
|
2
1
|
import { Bone } from "three";
|
|
3
|
-
import {
|
|
2
|
+
import { arrayPickBestElement } from "../../../../core/collection/array/arrayPickBestElement.js";
|
|
3
|
+
import { string_compute_similarity } from "../../../../core/primitives/strings/string_compute_similarity.js";
|
|
4
4
|
import { extractName } from "../../../../extractName.js";
|
|
5
|
+
import { BoneMapping } from "./skeleton/BoneMapping.js";
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
/**
|
|
@@ -42,20 +43,10 @@ export function getSkeletonBone(component, boneName) {
|
|
|
42
43
|
//bone not found
|
|
43
44
|
|
|
44
45
|
//try to find similar bones
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
return {
|
|
48
|
-
bone: bone,
|
|
49
|
-
distance: distance
|
|
50
|
-
};
|
|
46
|
+
const bestMatch = arrayPickBestElement(bones, function (bone) {
|
|
47
|
+
return string_compute_similarity(bone.name, boneName);
|
|
51
48
|
});
|
|
52
49
|
|
|
53
|
-
similarities.sort(function (a, b) {
|
|
54
|
-
return a.distance - b.distance;
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
const bestMatch = similarities[0].bone;
|
|
58
|
-
|
|
59
50
|
throw new Error("Bone '" + boneName + "' not found, did you mean '" + bestMatch.name + "'");
|
|
60
51
|
}
|
|
61
52
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { HumanoidBoneType } from "./HumanoidBoneType.js";
|
|
2
|
-
import { distance as levenshtein_distance } from "fastest-levenshtein";
|
|
3
1
|
import { assert } from "../../../../../core/assert.js";
|
|
4
2
|
import { string_compute_common_prefix } from "../../../../../core/primitives/strings/string_compute_common_prefix.js";
|
|
3
|
+
import { string_compute_similarity } from "../../../../../core/primitives/strings/string_compute_similarity.js";
|
|
4
|
+
import { HumanoidBoneType } from "./HumanoidBoneType.js";
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -40,7 +40,7 @@ export class BoneMapping {
|
|
|
40
40
|
boneValues.forEach(function (boneName, boneIndex) {
|
|
41
41
|
|
|
42
42
|
conditionedNames.forEach(function (name, inputIndex) {
|
|
43
|
-
const distance =
|
|
43
|
+
const distance = string_compute_similarity(name, boneName);
|
|
44
44
|
const match = {
|
|
45
45
|
boneIndex,
|
|
46
46
|
inputIndex,
|