xl-public-utils 1.0.11 → 1.0.12
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/index.d.ts +52 -0
- package/index.js +2 -1
- package/package.json +4 -2
- package/src/threeFont/Curve/Curve.js +349 -0
- package/src/threeFont/Curve/CurvePath.js +202 -0
- package/src/threeFont/Curve/Interpolations.js +57 -0
- package/src/threeFont/Curve/Shape.js +77 -0
- package/src/threeFont/Math/MathUtils.js +526 -0
- package/src/threeFont/Math/Matrix4.js +863 -0
- package/src/threeFont/Math/Quaternion.js +584 -0
- package/src/threeFont/Math/Vector2.js +373 -0
- package/src/threeFont/Math/Vector3.js +617 -0
- package/src/threeFont/TextGeometry/Earcut.js +634 -0
- package/src/threeFont/TextGeometry/ExtrudeGeometry.js +647 -0
- package/src/threeFont/TextGeometry/FontLoader.js +114 -0
- package/src/threeFont/TextGeometry/Path.js +146 -0
- package/src/threeFont/TextGeometry/ShapePath.js +222 -0
- package/src/threeFont/TextGeometry/ShapeUtils.js +70 -0
- package/src/threeFont/curves/CubicBezierCurve.js +66 -0
- package/src/threeFont/curves/Curves.js +10 -0
- package/src/threeFont/curves/EllipseCurve.js +130 -0
- package/src/threeFont/curves/LineCurve.js +70 -0
- package/src/threeFont/curves/QuadraticBezierCurve.js +61 -0
- package/src/threeFont/curves/SplineCurve.js +76 -0
- package/src/threeFont/index.js +166 -0
- package/src/vtkUtils.js +55 -28
- package/tsconfig.json +1 -1
package/index.d.ts
CHANGED
|
@@ -979,4 +979,56 @@ declare module "xl-public-utils" {
|
|
|
979
979
|
): void;
|
|
980
980
|
}
|
|
981
981
|
}
|
|
982
|
+
export interface FontParameters {
|
|
983
|
+
font: Record<string, any>; // 字体数据由 FontLoader 加载,结构可进一步细化
|
|
984
|
+
size: number;
|
|
985
|
+
height: number;
|
|
986
|
+
curveSegments: number;
|
|
987
|
+
bevelThickness: number;
|
|
988
|
+
bevelSize: number;
|
|
989
|
+
bevelEnabled: boolean;
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
export class FontManager {
|
|
993
|
+
private constructor(); // 使用单例模式
|
|
994
|
+
static Mgr(): FontManager;
|
|
995
|
+
|
|
996
|
+
private cacheFontMap: Record<string, vtkPolyData>;
|
|
997
|
+
private cacheFontPointMap: Record<string, IndexData>;
|
|
998
|
+
private fontJsonMap: Record<string, any>;
|
|
999
|
+
private parameters: FontParameters & { font: Record<string, any> };
|
|
1000
|
+
|
|
1001
|
+
/**
|
|
1002
|
+
* 加载字体文件
|
|
1003
|
+
* @param url JSON 字体文件地址
|
|
1004
|
+
* @param fontName 字体名称
|
|
1005
|
+
* @param bevelEnabled 是否启用 bevel
|
|
1006
|
+
*/
|
|
1007
|
+
loadFontFile(url: string, fontName?: string, bevelEnabled?: boolean): Promise<void>;
|
|
1008
|
+
|
|
1009
|
+
/**
|
|
1010
|
+
* 生成文字的 PolyData 以及前后顶点索引映射数据
|
|
1011
|
+
* @param text 要生成的文字
|
|
1012
|
+
* @param fontName 字体名
|
|
1013
|
+
* @param scale 缩放向量
|
|
1014
|
+
*/
|
|
1015
|
+
generateShapes(
|
|
1016
|
+
text: string,
|
|
1017
|
+
fontName?: string,
|
|
1018
|
+
scale?: vec3
|
|
1019
|
+
): {
|
|
1020
|
+
polyData: vtkPolyData;
|
|
1021
|
+
indexData: IndexData;
|
|
1022
|
+
};
|
|
1023
|
+
|
|
1024
|
+
/**
|
|
1025
|
+
* 获取顶点索引映射
|
|
1026
|
+
*/
|
|
1027
|
+
getPointsMapData(polydata: vtkPolyData): IndexData;
|
|
1028
|
+
|
|
1029
|
+
/**
|
|
1030
|
+
* 创建 vtkPolyData 对象
|
|
1031
|
+
*/
|
|
1032
|
+
createPolyData(verts: any, faces: any): vtkPolyData;
|
|
1033
|
+
}
|
|
982
1034
|
}
|
package/index.js
CHANGED
|
@@ -3,4 +3,5 @@ import * as utils from './src/utils.js'
|
|
|
3
3
|
import * as qrcode from './src/qrcode.js'
|
|
4
4
|
import * as BwipJs from './src/bwip-js.js';
|
|
5
5
|
import * as drcUtils from './src/drcUtils.js';
|
|
6
|
-
|
|
6
|
+
import FontManager from './src/threeFont/index.js';
|
|
7
|
+
export { vtkUtils, utils, qrcode, BwipJs, drcUtils, FontManager };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xl-public-utils",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.12",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"module": "index.js",
|
|
@@ -30,7 +30,9 @@
|
|
|
30
30
|
"./qrcode": "./src/qrcode.js",
|
|
31
31
|
"./vtkUtils": "./src/vtkUtils.js",
|
|
32
32
|
"./utils": "./src/utils.js",
|
|
33
|
-
"./bwipJs": "./src/bwip-js.js"
|
|
33
|
+
"./bwipJs": "./src/bwip-js.js",
|
|
34
|
+
"./drcUtils": "./src/drcUtils.js",
|
|
35
|
+
"./threeFont": "./src/threeFont/index.js"
|
|
34
36
|
},
|
|
35
37
|
"scripts": {
|
|
36
38
|
"test": "karma start",
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
import { Vector2 } from '../Math/Vector2.js';
|
|
2
|
+
import { Vector3 } from '../Math/Vector3.js';
|
|
3
|
+
import { Matrix4 } from '../Math/Matrix4.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Extensible curve object.
|
|
7
|
+
*
|
|
8
|
+
* Some common of curve methods:
|
|
9
|
+
* .getPoint( t, optionalTarget ), .getTangent( t, optionalTarget )
|
|
10
|
+
* .getPointAt( u, optionalTarget ), .getTangentAt( u, optionalTarget )
|
|
11
|
+
* .getPoints(), .getSpacedPoints()
|
|
12
|
+
* .getLength()
|
|
13
|
+
* .updateArcLengths()
|
|
14
|
+
*
|
|
15
|
+
* This following curves inherit from THREE.Curve:
|
|
16
|
+
*
|
|
17
|
+
* -- 2D curves --
|
|
18
|
+
* THREE.ArcCurve
|
|
19
|
+
* THREE.CubicBezierCurve
|
|
20
|
+
* THREE.EllipseCurve
|
|
21
|
+
* THREE.LineCurve
|
|
22
|
+
* THREE.QuadraticBezierCurve
|
|
23
|
+
* THREE.SplineCurve
|
|
24
|
+
*
|
|
25
|
+
* -- 3D curves --
|
|
26
|
+
* THREE.CatmullRomCurve3
|
|
27
|
+
* THREE.CubicBezierCurve3
|
|
28
|
+
* THREE.LineCurve3
|
|
29
|
+
* THREE.QuadraticBezierCurve3
|
|
30
|
+
*
|
|
31
|
+
* A series of curves can be represented as a THREE.CurvePath.
|
|
32
|
+
*
|
|
33
|
+
**/
|
|
34
|
+
|
|
35
|
+
class Curve {
|
|
36
|
+
constructor() {
|
|
37
|
+
this.type = 'Curve';
|
|
38
|
+
this.arcLengthDivisions = 200;
|
|
39
|
+
this.cacheArcLengths = [];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Virtual base class method to overwrite and implement in subclasses
|
|
43
|
+
// - t [0 .. 1]
|
|
44
|
+
|
|
45
|
+
getPoint(/* t, optionalTarget */) {
|
|
46
|
+
console.warn('THREE.Curve: .getPoint() not implemented.');
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Get point at relative position in curve according to arc length
|
|
51
|
+
// - u [0 .. 1]
|
|
52
|
+
|
|
53
|
+
getPointAt(u, optionalTarget) {
|
|
54
|
+
const t = this.getUtoTmapping(u);
|
|
55
|
+
return this.getPoint(t, optionalTarget);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Get sequence of points using getPoint( t )
|
|
59
|
+
|
|
60
|
+
getPoints(divisions = 5) {
|
|
61
|
+
const points = [];
|
|
62
|
+
|
|
63
|
+
for (let d = 0; d <= divisions; d++) {
|
|
64
|
+
points.push(this.getPoint(d / divisions));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return points;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Get sequence of points using getPointAt( u )
|
|
71
|
+
|
|
72
|
+
getSpacedPoints(divisions = 5) {
|
|
73
|
+
const points = [];
|
|
74
|
+
|
|
75
|
+
for (let d = 0; d <= divisions; d++) {
|
|
76
|
+
points.push(this.getPointAt(d / divisions));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return points;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Get total curve arc length
|
|
83
|
+
|
|
84
|
+
getLength() {
|
|
85
|
+
const lengths = this.getLengths();
|
|
86
|
+
return lengths[lengths.length - 1];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Get list of cumulative segment lengths
|
|
90
|
+
|
|
91
|
+
getLengths(divisions = this.arcLengthDivisions) {
|
|
92
|
+
if (this.cacheArcLengths && this.cacheArcLengths.length === divisions + 1 && !this.needsUpdate) {
|
|
93
|
+
return this.cacheArcLengths;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
this.needsUpdate = false;
|
|
97
|
+
|
|
98
|
+
const cache = [];
|
|
99
|
+
let current,
|
|
100
|
+
last = this.getPoint(0);
|
|
101
|
+
let sum = 0;
|
|
102
|
+
|
|
103
|
+
cache.push(0);
|
|
104
|
+
|
|
105
|
+
for (let p = 1; p <= divisions; p++) {
|
|
106
|
+
current = this.getPoint(p / divisions);
|
|
107
|
+
sum += current.distanceTo(last);
|
|
108
|
+
cache.push(sum);
|
|
109
|
+
last = current;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
this.cacheArcLengths = cache;
|
|
113
|
+
|
|
114
|
+
return cache; // { sums: cache, sum: sum }; Sum is in the last element.
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
updateArcLengths() {
|
|
118
|
+
this.needsUpdate = true;
|
|
119
|
+
this.getLengths();
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant
|
|
123
|
+
|
|
124
|
+
getUtoTmapping(u, distance = null) {
|
|
125
|
+
const arcLengths = this.getLengths();
|
|
126
|
+
|
|
127
|
+
let i = 0;
|
|
128
|
+
const il = arcLengths.length;
|
|
129
|
+
|
|
130
|
+
let targetArcLength; // The targeted u distance value to get
|
|
131
|
+
|
|
132
|
+
if (distance) {
|
|
133
|
+
targetArcLength = distance;
|
|
134
|
+
} else {
|
|
135
|
+
targetArcLength = u * arcLengths[il - 1];
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// binary search for the index with largest value smaller than target u distance
|
|
139
|
+
|
|
140
|
+
let low = 0,
|
|
141
|
+
high = il - 1,
|
|
142
|
+
comparison;
|
|
143
|
+
|
|
144
|
+
while (low <= high) {
|
|
145
|
+
i = Math.floor(low + (high - low) / 2); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats
|
|
146
|
+
|
|
147
|
+
comparison = arcLengths[i] - targetArcLength;
|
|
148
|
+
|
|
149
|
+
if (comparison < 0) {
|
|
150
|
+
low = i + 1;
|
|
151
|
+
} else if (comparison > 0) {
|
|
152
|
+
high = i - 1;
|
|
153
|
+
} else {
|
|
154
|
+
high = i;
|
|
155
|
+
break;
|
|
156
|
+
|
|
157
|
+
// DONE
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
i = high;
|
|
162
|
+
|
|
163
|
+
if (arcLengths[i] === targetArcLength) {
|
|
164
|
+
return i / (il - 1);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// we could get finer grain at lengths, or use simple interpolation between two points
|
|
168
|
+
|
|
169
|
+
const lengthBefore = arcLengths[i];
|
|
170
|
+
const lengthAfter = arcLengths[i + 1];
|
|
171
|
+
|
|
172
|
+
const segmentLength = lengthAfter - lengthBefore;
|
|
173
|
+
|
|
174
|
+
// determine where we are between the 'before' and 'after' points
|
|
175
|
+
|
|
176
|
+
const segmentFraction = (targetArcLength - lengthBefore) / segmentLength;
|
|
177
|
+
|
|
178
|
+
// add that fractional amount to t
|
|
179
|
+
|
|
180
|
+
const t = (i + segmentFraction) / (il - 1);
|
|
181
|
+
|
|
182
|
+
return t;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Returns a unit vector tangent at t
|
|
186
|
+
// In case any sub curve does not implement its tangent derivation,
|
|
187
|
+
// 2 points a small delta apart will be used to find its gradient
|
|
188
|
+
// which seems to give a reasonable approximation
|
|
189
|
+
|
|
190
|
+
getTangent(t, optionalTarget) {
|
|
191
|
+
const delta = 0.0001;
|
|
192
|
+
let t1 = t - delta;
|
|
193
|
+
let t2 = t + delta;
|
|
194
|
+
|
|
195
|
+
// Capping in case of danger
|
|
196
|
+
|
|
197
|
+
if (t1 < 0) t1 = 0;
|
|
198
|
+
if (t2 > 1) t2 = 1;
|
|
199
|
+
|
|
200
|
+
const pt1 = this.getPoint(t1);
|
|
201
|
+
const pt2 = this.getPoint(t2);
|
|
202
|
+
|
|
203
|
+
const tangent = optionalTarget || (pt1.isVector2 ? new Vector2() : new Vector3());
|
|
204
|
+
|
|
205
|
+
tangent.copy(pt2).sub(pt1).normalize();
|
|
206
|
+
|
|
207
|
+
return tangent;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
getTangentAt(u, optionalTarget) {
|
|
211
|
+
const t = this.getUtoTmapping(u);
|
|
212
|
+
return this.getTangent(t, optionalTarget);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
computeFrenetFrames(segments, closed) {
|
|
216
|
+
// see http://www.cs.indiana.edu/pub/techreports/TR425.pdf
|
|
217
|
+
/**
|
|
218
|
+
* 将数值限制在范围 [min, max] 之间
|
|
219
|
+
* @param {number} number 要限制的数值
|
|
220
|
+
* @param {number} min 最小值
|
|
221
|
+
* @param {number} max 最大值
|
|
222
|
+
* @returns {number}
|
|
223
|
+
*/
|
|
224
|
+
function clamp(number, min, max) {
|
|
225
|
+
return Math.min(Math.max(number, min), max);
|
|
226
|
+
}
|
|
227
|
+
const normal = new Vector3();
|
|
228
|
+
|
|
229
|
+
const tangents = [];
|
|
230
|
+
const normals = [];
|
|
231
|
+
const binormals = [];
|
|
232
|
+
|
|
233
|
+
const vec = new Vector3();
|
|
234
|
+
const mat = new Matrix4();
|
|
235
|
+
|
|
236
|
+
// compute the tangent vectors for each segment on the curve
|
|
237
|
+
|
|
238
|
+
for (let i = 0; i <= segments; i++) {
|
|
239
|
+
const u = i / segments;
|
|
240
|
+
|
|
241
|
+
tangents[i] = this.getTangentAt(u, new Vector3());
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// select an initial normal vector perpendicular to the first tangent vector,
|
|
245
|
+
// and in the direction of the minimum tangent xyz component
|
|
246
|
+
|
|
247
|
+
normals[0] = new Vector3();
|
|
248
|
+
binormals[0] = new Vector3();
|
|
249
|
+
let min = Number.MAX_VALUE;
|
|
250
|
+
const tx = Math.abs(tangents[0].x);
|
|
251
|
+
const ty = Math.abs(tangents[0].y);
|
|
252
|
+
const tz = Math.abs(tangents[0].z);
|
|
253
|
+
|
|
254
|
+
if (tx <= min) {
|
|
255
|
+
min = tx;
|
|
256
|
+
normal.set(1, 0, 0);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (ty <= min) {
|
|
260
|
+
min = ty;
|
|
261
|
+
normal.set(0, 1, 0);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (tz <= min) {
|
|
265
|
+
normal.set(0, 0, 1);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
vec.crossVectors(tangents[0], normal).normalize();
|
|
269
|
+
|
|
270
|
+
normals[0].crossVectors(tangents[0], vec);
|
|
271
|
+
binormals[0].crossVectors(tangents[0], normals[0]);
|
|
272
|
+
|
|
273
|
+
// compute the slowly-varying normal and binormal vectors for each segment on the curve
|
|
274
|
+
|
|
275
|
+
for (let i = 1; i <= segments; i++) {
|
|
276
|
+
normals[i] = normals[i - 1].clone();
|
|
277
|
+
|
|
278
|
+
binormals[i] = binormals[i - 1].clone();
|
|
279
|
+
|
|
280
|
+
vec.crossVectors(tangents[i - 1], tangents[i]);
|
|
281
|
+
|
|
282
|
+
if (vec.length() > Number.EPSILON) {
|
|
283
|
+
vec.normalize();
|
|
284
|
+
|
|
285
|
+
const theta = Math.acos(clamp(tangents[i - 1].dot(tangents[i]), -1, 1)); // clamp for floating pt errors
|
|
286
|
+
|
|
287
|
+
normals[i].applyMatrix4(mat.makeRotationAxis(vec, theta));
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
binormals[i].crossVectors(tangents[i], normals[i]);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// if the curve is closed, postprocess the vectors so the first and last normal vectors are the same
|
|
294
|
+
|
|
295
|
+
if (closed === true) {
|
|
296
|
+
let theta = Math.acos(clamp(normals[0].dot(normals[segments]), -1, 1));
|
|
297
|
+
theta /= segments;
|
|
298
|
+
|
|
299
|
+
if (tangents[0].dot(vec.crossVectors(normals[0], normals[segments])) > 0) {
|
|
300
|
+
theta = -theta;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
for (let i = 1; i <= segments; i++) {
|
|
304
|
+
// twist a little...
|
|
305
|
+
normals[i].applyMatrix4(mat.makeRotationAxis(tangents[i], theta * i));
|
|
306
|
+
binormals[i].crossVectors(tangents[i], normals[i]);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return {
|
|
311
|
+
tangents: tangents,
|
|
312
|
+
normals: normals,
|
|
313
|
+
binormals: binormals,
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
clone() {
|
|
318
|
+
return new this.constructor().copy(this);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
copy(source) {
|
|
322
|
+
this.arcLengthDivisions = source.arcLengthDivisions;
|
|
323
|
+
|
|
324
|
+
return this;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
toJSON() {
|
|
328
|
+
const data = {
|
|
329
|
+
metadata: {
|
|
330
|
+
version: 4.5,
|
|
331
|
+
type: 'Curve',
|
|
332
|
+
generator: 'Curve.toJSON',
|
|
333
|
+
},
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
data.arcLengthDivisions = this.arcLengthDivisions;
|
|
337
|
+
data.type = this.type;
|
|
338
|
+
|
|
339
|
+
return data;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
fromJSON(json) {
|
|
343
|
+
this.arcLengthDivisions = json.arcLengthDivisions;
|
|
344
|
+
|
|
345
|
+
return this;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
export { Curve };
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { Curve } from './Curve.js';
|
|
2
|
+
import * as Curves from '../curves/Curves.js';
|
|
3
|
+
|
|
4
|
+
/**************************************************************
|
|
5
|
+
* Curved Path - a curve path is simply a array of connected
|
|
6
|
+
* curves, but retains the api of a curve
|
|
7
|
+
**************************************************************/
|
|
8
|
+
|
|
9
|
+
class CurvePath extends Curve {
|
|
10
|
+
constructor() {
|
|
11
|
+
super();
|
|
12
|
+
|
|
13
|
+
this.type = 'CurvePath';
|
|
14
|
+
|
|
15
|
+
this.curves = [];
|
|
16
|
+
this.autoClose = false; // Automatically closes the path
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
add(curve) {
|
|
20
|
+
this.curves.push(curve);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
closePath() {
|
|
24
|
+
// Add a line curve if start and end of lines are not connected
|
|
25
|
+
const startPoint = this.curves[0].getPoint(0);
|
|
26
|
+
const endPoint = this.curves[this.curves.length - 1].getPoint(1);
|
|
27
|
+
|
|
28
|
+
if (!startPoint.equals(endPoint)) {
|
|
29
|
+
this.curves.push(new Curves['LineCurve'](endPoint, startPoint));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// To get accurate point with reference to
|
|
34
|
+
// entire path distance at time t,
|
|
35
|
+
// following has to be done:
|
|
36
|
+
|
|
37
|
+
// 1. Length of each sub path have to be known
|
|
38
|
+
// 2. Locate and identify type of curve
|
|
39
|
+
// 3. Get t for the curve
|
|
40
|
+
// 4. Return curve.getPointAt(t')
|
|
41
|
+
|
|
42
|
+
getPoint(t, optionalTarget) {
|
|
43
|
+
const d = t * this.getLength();
|
|
44
|
+
const curveLengths = this.getCurveLengths();
|
|
45
|
+
let i = 0;
|
|
46
|
+
|
|
47
|
+
// To think about boundaries points.
|
|
48
|
+
|
|
49
|
+
while (i < curveLengths.length) {
|
|
50
|
+
if (curveLengths[i] >= d) {
|
|
51
|
+
const diff = curveLengths[i] - d;
|
|
52
|
+
const curve = this.curves[i];
|
|
53
|
+
|
|
54
|
+
const segmentLength = curve.getLength();
|
|
55
|
+
const u = segmentLength === 0 ? 0 : 1 - diff / segmentLength;
|
|
56
|
+
|
|
57
|
+
return curve.getPointAt(u, optionalTarget);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
i++;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return null;
|
|
64
|
+
|
|
65
|
+
// loop where sum != 0, sum > d , sum+1 <d
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// We cannot use the default THREE.Curve getPoint() with getLength() because in
|
|
69
|
+
// THREE.Curve, getLength() depends on getPoint() but in THREE.CurvePath
|
|
70
|
+
// getPoint() depends on getLength
|
|
71
|
+
|
|
72
|
+
getLength() {
|
|
73
|
+
const lens = this.getCurveLengths();
|
|
74
|
+
return lens[lens.length - 1];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// cacheLengths must be recalculated.
|
|
78
|
+
updateArcLengths() {
|
|
79
|
+
this.needsUpdate = true;
|
|
80
|
+
this.cacheLengths = null;
|
|
81
|
+
this.getCurveLengths();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Compute lengths and cache them
|
|
85
|
+
// We cannot overwrite getLengths() because UtoT mapping uses it.
|
|
86
|
+
|
|
87
|
+
getCurveLengths() {
|
|
88
|
+
// We use cache values if curves and cache array are same length
|
|
89
|
+
|
|
90
|
+
if (this.cacheLengths && this.cacheLengths.length === this.curves.length) {
|
|
91
|
+
return this.cacheLengths;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Get length of sub-curve
|
|
95
|
+
// Push sums into cached array
|
|
96
|
+
|
|
97
|
+
const lengths = [];
|
|
98
|
+
let sums = 0;
|
|
99
|
+
|
|
100
|
+
for (let i = 0, l = this.curves.length; i < l; i++) {
|
|
101
|
+
sums += this.curves[i].getLength();
|
|
102
|
+
lengths.push(sums);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
this.cacheLengths = lengths;
|
|
106
|
+
|
|
107
|
+
return lengths;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
getSpacedPoints(divisions = 40) {
|
|
111
|
+
const points = [];
|
|
112
|
+
|
|
113
|
+
for (let i = 0; i <= divisions; i++) {
|
|
114
|
+
points.push(this.getPoint(i / divisions));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (this.autoClose) {
|
|
118
|
+
points.push(points[0]);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return points;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
getPoints(divisions = 12) {
|
|
125
|
+
const points = [];
|
|
126
|
+
let last;
|
|
127
|
+
|
|
128
|
+
for (let i = 0, curves = this.curves; i < curves.length; i++) {
|
|
129
|
+
const curve = curves[i];
|
|
130
|
+
const resolution = curve.isEllipseCurve
|
|
131
|
+
? divisions * 2
|
|
132
|
+
: curve.isLineCurve || curve.isLineCurve3
|
|
133
|
+
? 1
|
|
134
|
+
: curve.isSplineCurve
|
|
135
|
+
? divisions * curve.points.length
|
|
136
|
+
: divisions;
|
|
137
|
+
|
|
138
|
+
const pts = curve.getPoints(resolution);
|
|
139
|
+
|
|
140
|
+
for (let j = 0; j < pts.length; j++) {
|
|
141
|
+
const point = pts[j];
|
|
142
|
+
|
|
143
|
+
if (last && last.equals(point)) continue; // ensures no consecutive points are duplicates
|
|
144
|
+
|
|
145
|
+
points.push(point);
|
|
146
|
+
last = point;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (this.autoClose && points.length > 1 && !points[points.length - 1].equals(points[0])) {
|
|
151
|
+
points.push(points[0]);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return points;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
copy(source) {
|
|
158
|
+
super.copy(source);
|
|
159
|
+
|
|
160
|
+
this.curves = [];
|
|
161
|
+
|
|
162
|
+
for (let i = 0, l = source.curves.length; i < l; i++) {
|
|
163
|
+
const curve = source.curves[i];
|
|
164
|
+
|
|
165
|
+
this.curves.push(curve.clone());
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
this.autoClose = source.autoClose;
|
|
169
|
+
|
|
170
|
+
return this;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
toJSON() {
|
|
174
|
+
const data = super.toJSON();
|
|
175
|
+
|
|
176
|
+
data.autoClose = this.autoClose;
|
|
177
|
+
data.curves = [];
|
|
178
|
+
|
|
179
|
+
for (let i = 0, l = this.curves.length; i < l; i++) {
|
|
180
|
+
const curve = this.curves[i];
|
|
181
|
+
data.curves.push(curve.toJSON());
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return data;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
fromJSON(json) {
|
|
188
|
+
super.fromJSON(json);
|
|
189
|
+
|
|
190
|
+
this.autoClose = json.autoClose;
|
|
191
|
+
this.curves = [];
|
|
192
|
+
|
|
193
|
+
for (let i = 0, l = json.curves.length; i < l; i++) {
|
|
194
|
+
const curve = json.curves[i];
|
|
195
|
+
this.curves.push(new Curves[curve.type]().fromJSON(curve));
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return this;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export { CurvePath };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bezier Curves formulas obtained from
|
|
3
|
+
* https://en.wikipedia.org/wiki/B%C3%A9zier_curve
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
function CatmullRom(t, p0, p1, p2, p3) {
|
|
7
|
+
const v0 = (p2 - p0) * 0.5;
|
|
8
|
+
const v1 = (p3 - p1) * 0.5;
|
|
9
|
+
const t2 = t * t;
|
|
10
|
+
const t3 = t * t2;
|
|
11
|
+
return (2 * p1 - 2 * p2 + v0 + v1) * t3 + (-3 * p1 + 3 * p2 - 2 * v0 - v1) * t2 + v0 * t + p1;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
//
|
|
15
|
+
|
|
16
|
+
function QuadraticBezierP0(t, p) {
|
|
17
|
+
const k = 1 - t;
|
|
18
|
+
return k * k * p;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function QuadraticBezierP1(t, p) {
|
|
22
|
+
return 2 * (1 - t) * t * p;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function QuadraticBezierP2(t, p) {
|
|
26
|
+
return t * t * p;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function QuadraticBezier(t, p0, p1, p2) {
|
|
30
|
+
return QuadraticBezierP0(t, p0) + QuadraticBezierP1(t, p1) + QuadraticBezierP2(t, p2);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
//
|
|
34
|
+
|
|
35
|
+
function CubicBezierP0(t, p) {
|
|
36
|
+
const k = 1 - t;
|
|
37
|
+
return k * k * k * p;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function CubicBezierP1(t, p) {
|
|
41
|
+
const k = 1 - t;
|
|
42
|
+
return 3 * k * k * t * p;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function CubicBezierP2(t, p) {
|
|
46
|
+
return 3 * (1 - t) * t * t * p;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function CubicBezierP3(t, p) {
|
|
50
|
+
return t * t * t * p;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function CubicBezier(t, p0, p1, p2, p3) {
|
|
54
|
+
return CubicBezierP0(t, p0) + CubicBezierP1(t, p1) + CubicBezierP2(t, p2) + CubicBezierP3(t, p3);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export { CatmullRom, QuadraticBezier, CubicBezier };
|