modern-text 1.11.1 → 2.0.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/README.md +283 -22
- package/dist/deformations/index.cjs +566 -0
- package/dist/deformations/index.d.cts +11 -0
- package/dist/deformations/index.d.mts +11 -0
- package/dist/deformations/index.d.ts +11 -0
- package/dist/deformations/index.mjs +563 -0
- package/dist/index.cjs +15 -3
- package/dist/index.d.cts +186 -6
- package/dist/index.d.mts +186 -6
- package/dist/index.d.ts +186 -6
- package/dist/index.js +6 -5
- package/dist/index.mjs +4 -2
- package/dist/shared/modern-text.B2xfrqDc.cjs +556 -0
- package/dist/shared/modern-text.BD7PBYt7.d.cts +100 -0
- package/dist/shared/modern-text.BxijkspX.d.ts +100 -0
- package/dist/shared/{modern-text.BKZQdmgG.cjs → modern-text.CBgc-cQ1.cjs} +288 -18
- package/dist/shared/modern-text.CYa4lfoG.d.mts +100 -0
- package/dist/shared/{modern-text.Dqw5Z6MV.mjs → modern-text.ChzjFjsk.mjs} +283 -13
- package/dist/shared/{modern-text.Db7Uoht6.d.cts → modern-text.D4WopQCu.d.cts} +56 -22
- package/dist/shared/{modern-text.Db7Uoht6.d.mts → modern-text.D4WopQCu.d.mts} +56 -22
- package/dist/shared/{modern-text.Db7Uoht6.d.ts → modern-text.D4WopQCu.d.ts} +56 -22
- package/dist/shared/modern-text.JF1ny7A-.mjs +550 -0
- package/dist/shared/modern-text.MC5bIC9E.cjs +316 -0
- package/dist/shared/modern-text.fT17R5HY.mjs +310 -0
- package/dist/web-components/index.cjs +2 -1
- package/dist/web-components/index.d.cts +1 -1
- package/dist/web-components/index.d.mts +1 -1
- package/dist/web-components/index.d.ts +1 -1
- package/dist/web-components/index.mjs +2 -1
- package/package.json +12 -7
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { T as Text } from './shared/modern-text.
|
|
2
|
-
export { C as Canvas2DRenderer, a as Character,
|
|
1
|
+
import { T as Text } from './shared/modern-text.ChzjFjsk.mjs';
|
|
2
|
+
export { C as Canvas2DRenderer, a as Character, D as DomMeasurer, F as FontMeasurer, b as Fragment, P as Paragraph, c as backgroundPlugin, d as createSvgLoader, e as createSvgParser, g as getEffectTransform2D, f as getHighlightStyle, h as highlightPlugin, i as isEqualObject, j as isEqualValue, l as listStylePlugin, o as outlinePlugin, p as parseColormap, k as parseTransformOrigin, m as parseValueNumber, r as renderPlugin, t as textDecorationPlugin, n as textDefaultStyle } from './shared/modern-text.ChzjFjsk.mjs';
|
|
3
|
+
export { d as defineDeformation, a as definePlugin, b as deformationPlugin, g as getDeformationNames, r as removeDeformation } from './shared/modern-text.JF1ny7A-.mjs';
|
|
4
|
+
export { C as CircleCurve, E as EllipseCurve, H as HeartCurve, P as PolygonCurve, R as RectangularCurve } from './shared/modern-text.fT17R5HY.mjs';
|
|
3
5
|
import 'modern-idoc';
|
|
4
6
|
import 'modern-path2d';
|
|
5
7
|
import 'modern-font';
|
|
@@ -0,0 +1,556 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const modernIdoc = require('modern-idoc');
|
|
4
|
+
const modernPath2d = require('modern-path2d');
|
|
5
|
+
|
|
6
|
+
function definePlugin(options) {
|
|
7
|
+
return options;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function splitCurve(curve, point) {
|
|
11
|
+
let { x, y } = point;
|
|
12
|
+
if (curve instanceof modernPath2d.QuadraticBezierCurve) {
|
|
13
|
+
const { p1: v0, cp: v1, p2: v2 } = curve;
|
|
14
|
+
const s = x !== void 0 && Math.abs(Math.sign(v0.x - x) + Math.sign(v1.x - x) + Math.sign(v2.x - x)) === 1;
|
|
15
|
+
const n = y !== void 0 && Math.abs(Math.sign(v0.y - y) + Math.sign(v1.y - y) + Math.sign(v2.y - y)) === 1;
|
|
16
|
+
let o, e, dist, c;
|
|
17
|
+
if (s) {
|
|
18
|
+
o = v0.x - 2 * v1.x + v2.x;
|
|
19
|
+
e = 2 * (-v0.x + v1.x);
|
|
20
|
+
dist = v0.x - x;
|
|
21
|
+
}
|
|
22
|
+
if (n) {
|
|
23
|
+
o = v0.y - 2 * v1.y + v2.y;
|
|
24
|
+
e = 2 * (-v0.y + v1.y);
|
|
25
|
+
dist = v0.y - y;
|
|
26
|
+
}
|
|
27
|
+
if (s || n) {
|
|
28
|
+
c = e * e - 4 * o * dist;
|
|
29
|
+
if (c > 0) {
|
|
30
|
+
const h = Math.sqrt(c);
|
|
31
|
+
const a = (-e + h) / 2 / o;
|
|
32
|
+
const b = (-e - h) / 2 / o;
|
|
33
|
+
const u = [a, b].find((v) => v > 0 && v < 1);
|
|
34
|
+
if (u) {
|
|
35
|
+
const r = modernPath2d.Vector2.lerp(v0, v1, u);
|
|
36
|
+
const d = modernPath2d.Vector2.lerp(v1, v2, u);
|
|
37
|
+
const B = modernPath2d.Vector2.lerp(r, d, u);
|
|
38
|
+
return [new modernPath2d.QuadraticBezierCurve(v0, r, B), new modernPath2d.QuadraticBezierCurve(B.clone(), d, v2)];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
} else if (curve instanceof modernPath2d.CubicBezierCurve) {
|
|
43
|
+
const { p1: v0, cp1: v1, cp2: v2, p2: v3 } = curve;
|
|
44
|
+
const { min, max } = curve.getMinMax();
|
|
45
|
+
const e = x !== void 0 && (min.x - x) * (max.x - x) < 0;
|
|
46
|
+
const l = y !== void 0 && (min.y - y) * (max.y - y) < 0;
|
|
47
|
+
if (e || l) {
|
|
48
|
+
x = 0.5 * v0.x + 0.5 * v1.x;
|
|
49
|
+
y = 0.5 * v0.y + 0.5 * v1.y;
|
|
50
|
+
const c = new modernPath2d.Vector2(x, y);
|
|
51
|
+
x = 0.25 * v0.x + 0.5 * v1.x + 0.25 * v2.x;
|
|
52
|
+
y = 0.25 * v0.y + 0.5 * v1.y + 0.25 * v2.y;
|
|
53
|
+
const h = new modernPath2d.Vector2(x, y);
|
|
54
|
+
x = 0.125 * v0.x + 0.375 * v1.x + 0.375 * v2.x + 0.125 * v3.x;
|
|
55
|
+
y = 0.125 * v0.y + 0.375 * v1.y + 0.375 * v2.y + 0.125 * v3.y;
|
|
56
|
+
const a = new modernPath2d.Vector2(x, y);
|
|
57
|
+
x = 0.25 * v1.x + 0.5 * v2.x + 0.25 * v3.x;
|
|
58
|
+
y = 0.25 * v1.y + 0.5 * v2.y + 0.25 * v3.y;
|
|
59
|
+
const b = new modernPath2d.Vector2(x, y);
|
|
60
|
+
x = 0.5 * v2.x + 0.5 * v3.x;
|
|
61
|
+
y = 0.5 * v2.y + 0.5 * v3.y;
|
|
62
|
+
const u = new modernPath2d.Vector2(x, y);
|
|
63
|
+
return [new modernPath2d.CubicBezierCurve(v0, c, h, a), new modernPath2d.CubicBezierCurve(a.clone(), b, u, v3)];
|
|
64
|
+
}
|
|
65
|
+
} else if (curve instanceof modernPath2d.LineCurve) {
|
|
66
|
+
const { p1: v1, p2: v2 } = curve;
|
|
67
|
+
const changedX = x !== void 0 && (v1.x - x) * (v2.x - x) < 0;
|
|
68
|
+
const changedY = y !== void 0 && (v1.y - y) * (v2.y - y) < 0;
|
|
69
|
+
if (changedX) {
|
|
70
|
+
y = v1.y + (v2.y - v1.y) / (v2.x - v1.x) * (x - v1.x);
|
|
71
|
+
}
|
|
72
|
+
if (changedY) {
|
|
73
|
+
x = v1.x + (v2.x - v1.x) / (v2.y - v1.y) * (y - v1.y);
|
|
74
|
+
}
|
|
75
|
+
if (changedX || changedY) {
|
|
76
|
+
return [new modernPath2d.LineCurve(v1, new modernPath2d.Vector2(x, y)), new modernPath2d.LineCurve(new modernPath2d.Vector2(x, y), v2)];
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return [curve];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
class Deformer {
|
|
83
|
+
get boundingBox() {
|
|
84
|
+
return this.text.lineBox;
|
|
85
|
+
}
|
|
86
|
+
get paragraphs() {
|
|
87
|
+
return this.text.paragraphs;
|
|
88
|
+
}
|
|
89
|
+
get isHorizontal() {
|
|
90
|
+
return this.text.computedStyle.writingMode.startsWith("horizontal");
|
|
91
|
+
}
|
|
92
|
+
get baseWidth() {
|
|
93
|
+
return this.isHorizontal ? this.boundingBox.width : this.boundingBox.height;
|
|
94
|
+
}
|
|
95
|
+
get baseHeight() {
|
|
96
|
+
return this.isHorizontal ? this.boundingBox.height : this.boundingBox.width;
|
|
97
|
+
}
|
|
98
|
+
get characters() {
|
|
99
|
+
return this.paragraphs.flatMap((p) => p.fragments.flatMap((f) => f.characters));
|
|
100
|
+
}
|
|
101
|
+
constructor({ text, intensities = [], maxFontSize = 100 }) {
|
|
102
|
+
this.text = text;
|
|
103
|
+
this.intensities = intensities.map((val) => val / 100);
|
|
104
|
+
this.lineHeight = maxFontSize;
|
|
105
|
+
}
|
|
106
|
+
_breakLine() {
|
|
107
|
+
const isVertical = !this.isHorizontal;
|
|
108
|
+
const { left, top, bottom, right } = this.boundingBox;
|
|
109
|
+
const x = 0.5 * (left + right);
|
|
110
|
+
const y = 0.5 * (top + bottom);
|
|
111
|
+
this.characters.forEach((character) => {
|
|
112
|
+
if (!character.glyphBox) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
character.path.curves.forEach((subPath) => {
|
|
116
|
+
subPath.curves = subPath.curves.flatMap((curve) => {
|
|
117
|
+
return splitCurve(curve, isVertical ? { y } : { x });
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
_makeTheJointSmooth() {
|
|
123
|
+
this.characters.forEach((character) => {
|
|
124
|
+
if (!character.glyphBox) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
character.path.getFlatCurves().forEach((curve) => {
|
|
128
|
+
if (curve instanceof modernPath2d.QuadraticBezierCurve && curve.isFromLine) {
|
|
129
|
+
const { p1, cp, p2 } = curve;
|
|
130
|
+
cp.x = 2 * cp.x - 0.5 * (p1.x + p2.x);
|
|
131
|
+
cp.y = 2 * cp.y - 0.5 * (p1.y + p2.y);
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
_lineToQuadraticBezier() {
|
|
137
|
+
this.characters.forEach((character) => {
|
|
138
|
+
if (!character.glyphBox) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
character.path.curves.forEach((subPath) => {
|
|
142
|
+
subPath.curves = subPath.curves.map((curve) => {
|
|
143
|
+
if (curve instanceof modernPath2d.LineCurve && curve.getLength() > 5) {
|
|
144
|
+
const { p1, p2 } = curve;
|
|
145
|
+
const res = new modernPath2d.QuadraticBezierCurve(
|
|
146
|
+
p1.clone(),
|
|
147
|
+
new modernPath2d.Vector2(0.5 * (p1.x + p2.x), 0.5 * (p1.y + p2.y)),
|
|
148
|
+
p2.clone()
|
|
149
|
+
);
|
|
150
|
+
res.isFromLine = true;
|
|
151
|
+
return res;
|
|
152
|
+
}
|
|
153
|
+
return curve;
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
_transform(transform, getArg1, getArg) {
|
|
159
|
+
const highlight = this.text.plugins.get("highlight");
|
|
160
|
+
const arg = getArg?.();
|
|
161
|
+
let i = 0;
|
|
162
|
+
const charactersLength = this.characters.filter((c) => c.glyphBox).length;
|
|
163
|
+
this.paragraphs.forEach((paragraph, paragraphIndex) => {
|
|
164
|
+
paragraph.fragments.forEach((fragment, fragmentIndex) => {
|
|
165
|
+
fragment.characters.forEach((character, characterIndex) => {
|
|
166
|
+
if (!character.glyphBox) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const arg1 = getArg1?.(
|
|
170
|
+
{
|
|
171
|
+
paragraphIndex,
|
|
172
|
+
fragmentIndex,
|
|
173
|
+
characterIndex,
|
|
174
|
+
character
|
|
175
|
+
},
|
|
176
|
+
arg
|
|
177
|
+
);
|
|
178
|
+
character.path.getControlPointRefs().forEach((point) => {
|
|
179
|
+
const [x, y] = transform(point, arg1);
|
|
180
|
+
point.set(x, y);
|
|
181
|
+
});
|
|
182
|
+
if (getArg1 && highlight?.pathSet?.paths) {
|
|
183
|
+
const step = highlight.pathSet.paths.length / charactersLength;
|
|
184
|
+
const start = i * step;
|
|
185
|
+
for (let _i = 0; _i < step; _i++) {
|
|
186
|
+
highlight?.pathSet?.paths[start + _i]?.getControlPointRefs().forEach((point) => {
|
|
187
|
+
const [x, y] = transform(point, arg1);
|
|
188
|
+
point.set(x, y);
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
i++;
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
this.paragraphs.forEach((paragraph) => {
|
|
197
|
+
paragraph.fragments.forEach((fragment) => {
|
|
198
|
+
fragment.characters.forEach((character) => {
|
|
199
|
+
character.glyphBox = character.getGlyphBoundingBox();
|
|
200
|
+
if (character.glyphBox) {
|
|
201
|
+
character.inlineBox = character.glyphBox;
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
if (!getArg1) {
|
|
207
|
+
highlight?.pathSet?.paths?.forEach((v) => {
|
|
208
|
+
v.getControlPointRefs().forEach((point) => {
|
|
209
|
+
const [x, y] = transform(point);
|
|
210
|
+
point.set(x, y);
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
class BendDeformer extends Deformer {
|
|
218
|
+
constructor(options, preset) {
|
|
219
|
+
super(options);
|
|
220
|
+
this.preset = preset;
|
|
221
|
+
}
|
|
222
|
+
deform() {
|
|
223
|
+
if (!Math.hypot(...this.intensities)) {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
this._lineToQuadraticBezier();
|
|
227
|
+
this._bend();
|
|
228
|
+
this._makeTheJointSmooth();
|
|
229
|
+
}
|
|
230
|
+
_bend() {
|
|
231
|
+
const { boundingBox, baseWidth, baseHeight, preset } = this;
|
|
232
|
+
const lineHeight = this.lineHeight * 2 / Math.sin(this.intensities[0] * Math.PI * 0.5);
|
|
233
|
+
const isVertical = preset.vertical === void 0 || preset.vertical === "auto" ? !this.isHorizontal : preset.vertical;
|
|
234
|
+
const { left, top, width, height } = boundingBox;
|
|
235
|
+
const size = (isVertical ? height : width) / 2 / lineHeight;
|
|
236
|
+
const center = {
|
|
237
|
+
x: left + width / 2,
|
|
238
|
+
y: top + height / 2
|
|
239
|
+
};
|
|
240
|
+
let centerDistAngle;
|
|
241
|
+
const centerDist = { x: center.x, y: center.y };
|
|
242
|
+
if (isVertical) {
|
|
243
|
+
centerDistAngle = 0;
|
|
244
|
+
centerDist.x -= baseHeight / 2 / Math.tan(size) + Math.sign(size) * width / 2;
|
|
245
|
+
} else {
|
|
246
|
+
centerDistAngle = 1.5 * Math.PI;
|
|
247
|
+
centerDist.y += baseWidth / 2 / Math.tan(size) + Math.sign(size) * height / 2;
|
|
248
|
+
}
|
|
249
|
+
const method = preset.transform({
|
|
250
|
+
lineHeight,
|
|
251
|
+
size,
|
|
252
|
+
center,
|
|
253
|
+
centerDist,
|
|
254
|
+
centerDistAngle,
|
|
255
|
+
width,
|
|
256
|
+
height,
|
|
257
|
+
isHorizontal: this.isHorizontal
|
|
258
|
+
});
|
|
259
|
+
this._transform(method);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
class FfdDeformer extends Deformer {
|
|
264
|
+
constructor(options, preset) {
|
|
265
|
+
super(options);
|
|
266
|
+
this.preset = preset;
|
|
267
|
+
}
|
|
268
|
+
deform() {
|
|
269
|
+
if (!Math.hypot(...this.intensities)) {
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
const { preset } = this;
|
|
273
|
+
if (preset.breakLine) {
|
|
274
|
+
this._breakLine();
|
|
275
|
+
}
|
|
276
|
+
if (preset.lineToQuad) {
|
|
277
|
+
this._lineToQuadraticBezier();
|
|
278
|
+
}
|
|
279
|
+
const [a, b] = this.intensities;
|
|
280
|
+
const points = this._createFFDControlPoints(preset.hBlocks, preset.vBlocks);
|
|
281
|
+
preset.build(points, {
|
|
282
|
+
a,
|
|
283
|
+
b,
|
|
284
|
+
baseWidth: this.baseWidth,
|
|
285
|
+
baseHeight: this.baseHeight,
|
|
286
|
+
lineHeight: this.lineHeight,
|
|
287
|
+
adjust: (point, dx, dy) => this._adjustControlPoints(point, { x: dx, y: dy })
|
|
288
|
+
});
|
|
289
|
+
if (preset.bezier) {
|
|
290
|
+
this._calculateForBezierFFD(points, preset.hBlocks, preset.vBlocks);
|
|
291
|
+
this._makeTheJointSmooth();
|
|
292
|
+
} else {
|
|
293
|
+
this._calculateForLinearFFD(points, preset.hBlocks, preset.vBlocks);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
_calculateForFFD(cb, points, hBlocks, vBlocks) {
|
|
297
|
+
const { left, top, right, width, height } = this.boundingBox;
|
|
298
|
+
this._transform(
|
|
299
|
+
this.isHorizontal ? (point) => {
|
|
300
|
+
const xProgress = (point.x - left) / width;
|
|
301
|
+
const yProgress = (point.y - top) / height;
|
|
302
|
+
let [x, y] = [0, 0];
|
|
303
|
+
for (let h = 0; h < hBlocks + 2; h++) {
|
|
304
|
+
const _h = cb(hBlocks, h, xProgress);
|
|
305
|
+
for (let v = 0; v < vBlocks + 2; v++) {
|
|
306
|
+
const _v = cb(vBlocks, v, yProgress);
|
|
307
|
+
const p = points[h * (vBlocks + 2) + v];
|
|
308
|
+
x += _h * _v * p.x;
|
|
309
|
+
y += _h * _v * p.y;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return [x, y];
|
|
313
|
+
} : (point) => {
|
|
314
|
+
const xProgress = (right - point.x) / width;
|
|
315
|
+
const yProgress = (point.y - top) / height;
|
|
316
|
+
let [x, y] = [0, 0];
|
|
317
|
+
for (let h = 0; h < hBlocks + 2; h++) {
|
|
318
|
+
const _h = cb(hBlocks, h, yProgress);
|
|
319
|
+
for (let v = 0; v < vBlocks + 2; v++) {
|
|
320
|
+
const _v = cb(vBlocks, v, xProgress);
|
|
321
|
+
const p = points[h * (vBlocks + 2) + v];
|
|
322
|
+
x += _h * _v * p.x;
|
|
323
|
+
y += _h * _v * p.y;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
return [x, y];
|
|
327
|
+
}
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
_createFFDControlPoints(hBlocks, vBlocks) {
|
|
331
|
+
const { left, top, right, width, height } = this.boundingBox;
|
|
332
|
+
const points = [];
|
|
333
|
+
if (this.isHorizontal) {
|
|
334
|
+
const avgWidth = width / (hBlocks + 1);
|
|
335
|
+
const avgHeight = height / (vBlocks + 1);
|
|
336
|
+
for (let h = 0; h < hBlocks + 2; h++) {
|
|
337
|
+
for (let v = 0; v < vBlocks + 2; v++) {
|
|
338
|
+
points.push(new modernPath2d.Vector2(left + h * avgWidth, top + v * avgHeight));
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
} else {
|
|
342
|
+
const avgWidth = width / (vBlocks + 1);
|
|
343
|
+
const avgHeight = height / (hBlocks + 1);
|
|
344
|
+
for (let h = 0; h < hBlocks + 2; h++) {
|
|
345
|
+
for (let v = 0; v < vBlocks + 2; v++) {
|
|
346
|
+
points.push(new modernPath2d.Vector2(right - v * avgWidth, top + h * avgHeight));
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
return points;
|
|
351
|
+
}
|
|
352
|
+
_adjustControlPoints(point1, point2) {
|
|
353
|
+
if (this.isHorizontal) {
|
|
354
|
+
point1.x += point2.x;
|
|
355
|
+
point1.y += point2.y;
|
|
356
|
+
} else {
|
|
357
|
+
point1.x -= point2.y;
|
|
358
|
+
point1.y += point2.x;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
_factorialForFFD(val) {
|
|
362
|
+
let result = 1;
|
|
363
|
+
for (let i = 2; i <= val; i++) {
|
|
364
|
+
result *= i;
|
|
365
|
+
}
|
|
366
|
+
return result;
|
|
367
|
+
}
|
|
368
|
+
_combineForFFD(total, current) {
|
|
369
|
+
return this._factorialForFFD(total) / this._factorialForFFD(total - current) / this._factorialForFFD(current);
|
|
370
|
+
}
|
|
371
|
+
_calculateForBezierFFD(points, hBlocks, vBlocks) {
|
|
372
|
+
this._calculateForFFD(
|
|
373
|
+
(count, current, progress) => {
|
|
374
|
+
return this._combineForFFD(count + 1, current) * (1 - progress) ** (count + 1 - current) * progress ** current;
|
|
375
|
+
},
|
|
376
|
+
points,
|
|
377
|
+
hBlocks,
|
|
378
|
+
vBlocks
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
_linearBasis(count, current, progress) {
|
|
382
|
+
const t = current < progress * count ? current + 1 - progress * count : progress * count - current + 1;
|
|
383
|
+
return Math.max(0, t);
|
|
384
|
+
}
|
|
385
|
+
_calculateForLinearFFD(points, hBlocks, vBlocks) {
|
|
386
|
+
this._calculateForFFD(
|
|
387
|
+
(count, current, progress) => {
|
|
388
|
+
return this._linearBasis(count + 1, current, progress);
|
|
389
|
+
},
|
|
390
|
+
points,
|
|
391
|
+
hBlocks,
|
|
392
|
+
vBlocks
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
class VerbatimDeformer extends Deformer {
|
|
398
|
+
constructor(options, preset) {
|
|
399
|
+
super(options);
|
|
400
|
+
this.preset = preset;
|
|
401
|
+
}
|
|
402
|
+
_context() {
|
|
403
|
+
return {
|
|
404
|
+
intensities: this.intensities,
|
|
405
|
+
baseWidth: this.baseWidth,
|
|
406
|
+
lineHeight: this.lineHeight,
|
|
407
|
+
isHorizontal: this.isHorizontal,
|
|
408
|
+
boundingBox: this.boundingBox
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
deform() {
|
|
412
|
+
if (!Math.hypot(...this.intensities)) {
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
const { preset } = this;
|
|
416
|
+
const ctx = this._context();
|
|
417
|
+
if (preset.engine === "offset") {
|
|
418
|
+
this._transform(
|
|
419
|
+
preset.point,
|
|
420
|
+
preset.perChar ? (info) => preset.perChar(ctx, info) : void 0
|
|
421
|
+
);
|
|
422
|
+
} else {
|
|
423
|
+
const followTangent = preset.followTangent ?? true;
|
|
424
|
+
const { funcPerChar, funcPerPoint } = this._getPerCharAndPointFunc(followTangent);
|
|
425
|
+
this._transform(funcPerPoint, funcPerChar, () => {
|
|
426
|
+
const { width, height, left, top } = this.boundingBox;
|
|
427
|
+
return {
|
|
428
|
+
width,
|
|
429
|
+
height,
|
|
430
|
+
left,
|
|
431
|
+
top,
|
|
432
|
+
curve: preset.makeCurve(ctx),
|
|
433
|
+
isHorizontal: this.isHorizontal,
|
|
434
|
+
needExpandAlongNormal: preset.expandAlongNormal ?? false
|
|
435
|
+
};
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
_getPerCharAndPointFunc(rotate = false) {
|
|
440
|
+
let funcPerChar;
|
|
441
|
+
let funcPerPoint;
|
|
442
|
+
if (rotate) {
|
|
443
|
+
funcPerPoint = this._resetPointPos;
|
|
444
|
+
funcPerChar = this._calculateNewCenter;
|
|
445
|
+
} else {
|
|
446
|
+
funcPerPoint = this._resetPointPosWithoutRotate;
|
|
447
|
+
funcPerChar = this._calculateNewCenterWithoutRotate;
|
|
448
|
+
}
|
|
449
|
+
return {
|
|
450
|
+
funcPerPoint,
|
|
451
|
+
funcPerChar
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
_calculateNewCenter({ character }, arg) {
|
|
455
|
+
const { width, height, left, top, curve, isHorizontal, needExpandAlongNormal = false } = arg;
|
|
456
|
+
const { center, centerDiviation } = character;
|
|
457
|
+
const pos = isHorizontal ? (center.x - left) / width : (center.y - top) / height;
|
|
458
|
+
const a = isHorizontal ? new modernPath2d.Vector2(0, center.y - top) : new modernPath2d.Vector2(center.x - left, 0);
|
|
459
|
+
if (needExpandAlongNormal) {
|
|
460
|
+
const p = curve.getNormal(pos);
|
|
461
|
+
a.x += p.x * -centerDiviation;
|
|
462
|
+
a.y += p.y * -centerDiviation;
|
|
463
|
+
}
|
|
464
|
+
const newCenter = curve.getPointAt(pos).add(a);
|
|
465
|
+
const tangent = curve.getTangent(pos);
|
|
466
|
+
const cos = tangent.x;
|
|
467
|
+
const sin = tangent.y;
|
|
468
|
+
return {
|
|
469
|
+
cos,
|
|
470
|
+
sin,
|
|
471
|
+
newCenter,
|
|
472
|
+
center
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
_calculateNewCenterWithoutRotate({ character }, arg) {
|
|
476
|
+
const { width, height, left, top, curve, isHorizontal } = arg;
|
|
477
|
+
const { center } = character;
|
|
478
|
+
const pos = isHorizontal ? (center.x - left) / width : (center.y - top) / height;
|
|
479
|
+
const offsetPoint = isHorizontal ? new modernPath2d.Vector2(0, center.y - top) : new modernPath2d.Vector2(center.x - left, 0);
|
|
480
|
+
return {
|
|
481
|
+
newCenter: curve.getPointAt(pos).add(offsetPoint),
|
|
482
|
+
center
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
_resetPointPos(point, arg) {
|
|
486
|
+
const { cos = 1, sin = 0, center, newCenter } = arg;
|
|
487
|
+
const dx = point.x - center.x;
|
|
488
|
+
const dy = point.y - center.y;
|
|
489
|
+
const x = newCenter.x + dx * cos - dy * sin;
|
|
490
|
+
const y = newCenter.y + dx * sin + dy * cos;
|
|
491
|
+
return [x, y];
|
|
492
|
+
}
|
|
493
|
+
_resetPointPosWithoutRotate(point, arg) {
|
|
494
|
+
const { center, newCenter } = arg;
|
|
495
|
+
const x = point.x - center.x + newCenter.x;
|
|
496
|
+
const y = point.y - center.y + newCenter.y;
|
|
497
|
+
return [x, y];
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
const deformationPresets = /* @__PURE__ */ new Map();
|
|
502
|
+
function defineDeformation(name, preset) {
|
|
503
|
+
deformationPresets.set(name, preset);
|
|
504
|
+
}
|
|
505
|
+
function removeDeformation(name) {
|
|
506
|
+
deformationPresets.delete(name);
|
|
507
|
+
}
|
|
508
|
+
function getDeformationNames() {
|
|
509
|
+
return [...deformationPresets.keys()];
|
|
510
|
+
}
|
|
511
|
+
function deformationPlugin() {
|
|
512
|
+
return definePlugin({
|
|
513
|
+
name: "deformation",
|
|
514
|
+
updateOrder: 2,
|
|
515
|
+
update: (text) => {
|
|
516
|
+
const config = text.deformation;
|
|
517
|
+
const type = config?.type;
|
|
518
|
+
if (modernIdoc.isNone(type) || !type) {
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
const preset = deformationPresets.get(type);
|
|
522
|
+
if (!preset) {
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
const options = {
|
|
526
|
+
text,
|
|
527
|
+
intensities: config.intensities ?? preset.defaultIntensities,
|
|
528
|
+
maxFontSize: config.maxFontSize
|
|
529
|
+
};
|
|
530
|
+
let deformer;
|
|
531
|
+
switch (preset.engine) {
|
|
532
|
+
case "ffd":
|
|
533
|
+
deformer = new FfdDeformer(options, preset);
|
|
534
|
+
break;
|
|
535
|
+
case "bend":
|
|
536
|
+
deformer = new BendDeformer(options, preset);
|
|
537
|
+
break;
|
|
538
|
+
case "curve":
|
|
539
|
+
case "offset":
|
|
540
|
+
deformer = new VerbatimDeformer(options, preset);
|
|
541
|
+
break;
|
|
542
|
+
}
|
|
543
|
+
deformer.deform();
|
|
544
|
+
const box = text.getGlyphBox();
|
|
545
|
+
text.rawGlyphBox = box;
|
|
546
|
+
text.glyphBox = box;
|
|
547
|
+
text.lineBox = box;
|
|
548
|
+
}
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
exports.defineDeformation = defineDeformation;
|
|
553
|
+
exports.definePlugin = definePlugin;
|
|
554
|
+
exports.deformationPlugin = deformationPlugin;
|
|
555
|
+
exports.getDeformationNames = getDeformationNames;
|
|
556
|
+
exports.removeDeformation = removeDeformation;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { Vector2Like, BoundingBox, Vector2 } from 'modern-path2d';
|
|
2
|
+
import { a as Character } from './modern-text.D4WopQCu.cjs';
|
|
3
|
+
|
|
4
|
+
/** 逐字沿形状排布所需的曲线最小接口(实现了这三个方法即可当作变形曲线) */
|
|
5
|
+
interface DeformationCurve {
|
|
6
|
+
getPointAt: (u: number, output?: Vector2) => Vector2;
|
|
7
|
+
getTangent: (t: number, output?: Vector2) => Vector2;
|
|
8
|
+
getNormal: (t: number, output?: Vector2) => Vector2;
|
|
9
|
+
}
|
|
10
|
+
/** FFD 预设 `build` 的上下文 */
|
|
11
|
+
interface FfdContext {
|
|
12
|
+
/** 强度 1(intensities[0],已 /100) */
|
|
13
|
+
a: number;
|
|
14
|
+
/** 强度 2(intensities[1],已 /100) */
|
|
15
|
+
b: number;
|
|
16
|
+
baseWidth: number;
|
|
17
|
+
baseHeight: number;
|
|
18
|
+
lineHeight: number;
|
|
19
|
+
/** 沿当前书写方向调整控制点(内部处理横/竖轴向互换) */
|
|
20
|
+
adjust: (point: Vector2Like, dx: number, dy: number) => void;
|
|
21
|
+
}
|
|
22
|
+
/** Bend 预设 `transform` 的上下文(引擎已算好弯曲几何) */
|
|
23
|
+
interface BendContext {
|
|
24
|
+
/** 弯曲半径 */
|
|
25
|
+
lineHeight: number;
|
|
26
|
+
size: number;
|
|
27
|
+
center: {
|
|
28
|
+
x: number;
|
|
29
|
+
y: number;
|
|
30
|
+
};
|
|
31
|
+
centerDist: {
|
|
32
|
+
x: number;
|
|
33
|
+
y: number;
|
|
34
|
+
};
|
|
35
|
+
/** 起始角 */
|
|
36
|
+
centerDistAngle: number;
|
|
37
|
+
width: number;
|
|
38
|
+
height: number;
|
|
39
|
+
isHorizontal: boolean;
|
|
40
|
+
}
|
|
41
|
+
/** 逐字预设(offset / curve)的上下文 */
|
|
42
|
+
interface VerbatimContext {
|
|
43
|
+
/** 强度数组(已 /100) */
|
|
44
|
+
intensities: number[];
|
|
45
|
+
baseWidth: number;
|
|
46
|
+
lineHeight: number;
|
|
47
|
+
isHorizontal: boolean;
|
|
48
|
+
boundingBox: BoundingBox;
|
|
49
|
+
}
|
|
50
|
+
/** 逐字 `perChar` 回调拿到的字符信息 */
|
|
51
|
+
interface DeformationCharInfo {
|
|
52
|
+
paragraphIndex: number;
|
|
53
|
+
fragmentIndex: number;
|
|
54
|
+
characterIndex: number;
|
|
55
|
+
character: Character;
|
|
56
|
+
}
|
|
57
|
+
interface PresetBase {
|
|
58
|
+
/** 未显式提供 intensities 时使用的默认值 */
|
|
59
|
+
defaultIntensities?: number[];
|
|
60
|
+
}
|
|
61
|
+
/** FFD(自由变形)预设:网格规格 + 预处理开关 + 控制点构造 */
|
|
62
|
+
interface FfdPreset extends PresetBase {
|
|
63
|
+
engine: 'ffd';
|
|
64
|
+
/** true=贝塞尔 FFD(需平滑),false=线性 FFD */
|
|
65
|
+
bezier: boolean;
|
|
66
|
+
hBlocks: number;
|
|
67
|
+
vBlocks: number;
|
|
68
|
+
/** 变形前先把字形直线拆段 */
|
|
69
|
+
breakLine?: boolean;
|
|
70
|
+
/** 变形前把直线转二次贝塞尔 */
|
|
71
|
+
lineToQuad?: boolean;
|
|
72
|
+
build: (points: Vector2[], ctx: FfdContext) => void;
|
|
73
|
+
}
|
|
74
|
+
/** Bend(整体弯曲)预设 */
|
|
75
|
+
interface BendPreset extends PresetBase {
|
|
76
|
+
engine: 'bend';
|
|
77
|
+
/** true=竖向弯曲,false=横向,'auto'/省略=跟随书写方向 */
|
|
78
|
+
vertical?: boolean | 'auto';
|
|
79
|
+
transform: (ctx: BendContext) => (point: Vector2Like) => readonly [number, number];
|
|
80
|
+
}
|
|
81
|
+
/** 逐字沿曲线排布预设 */
|
|
82
|
+
interface CurvePreset extends PresetBase {
|
|
83
|
+
engine: 'curve';
|
|
84
|
+
/** 字符是否沿切线旋转,默认 true */
|
|
85
|
+
followTangent?: boolean;
|
|
86
|
+
/** 是否沿法线方向按 centerDiviation 外扩(多边形需要) */
|
|
87
|
+
expandAlongNormal?: boolean;
|
|
88
|
+
makeCurve: (ctx: VerbatimContext) => DeformationCurve;
|
|
89
|
+
}
|
|
90
|
+
/** 逐字位移/旋转预设 */
|
|
91
|
+
interface OffsetPreset extends PresetBase {
|
|
92
|
+
engine: 'offset';
|
|
93
|
+
/** 逐控制点变换;arg 为 perChar 的返回值 */
|
|
94
|
+
point: (point: Vector2Like, arg: any) => readonly [number, number];
|
|
95
|
+
/** 逐字计算参数(透传给 point 的 arg) */
|
|
96
|
+
perChar?: (ctx: VerbatimContext, info: DeformationCharInfo) => any;
|
|
97
|
+
}
|
|
98
|
+
type DeformationPreset = FfdPreset | BendPreset | CurvePreset | OffsetPreset;
|
|
99
|
+
|
|
100
|
+
export type { BendContext as B, CurvePreset as C, DeformationCharInfo as D, FfdContext as F, OffsetPreset as O, VerbatimContext as V, BendPreset as a, DeformationCurve as b, DeformationPreset as c, FfdPreset as d };
|