@sliiu/present-renderer 0.0.0-stage → 0.1.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/COPYRIGHT +5 -0
- package/LICENSE +661 -0
- package/THIRD-PARTY-NOTICES.md +221 -0
- package/dist/cjs/arrow-geom.js +38 -0
- package/dist/cjs/canvas-backend.js +1368 -0
- package/dist/cjs/cell-borders.js +25 -0
- package/dist/cjs/chart-canvas.js +1059 -0
- package/dist/cjs/chart-ex-render.js +275 -0
- package/dist/cjs/chart-geom.js +313 -0
- package/dist/cjs/chart-svg.js +1110 -0
- package/dist/cjs/compose.js +74 -0
- package/dist/cjs/effect-clip.js +652 -0
- package/dist/cjs/entrance-hide.js +63 -0
- package/dist/cjs/font-stack.js +103 -0
- package/dist/cjs/hit-geom.js +50 -0
- package/dist/cjs/hybrid-backend.js +93 -0
- package/dist/cjs/hybrid-segments.js +54 -0
- package/dist/cjs/index.js +70 -0
- package/dist/cjs/linked-src.js +116 -0
- package/dist/cjs/metafile-preview.js +142 -0
- package/dist/cjs/model3d-frame.js +199 -0
- package/dist/cjs/model3d-render.js +78 -0
- package/dist/cjs/morph-transition.js +57 -0
- package/dist/cjs/node-affine.js +79 -0
- package/dist/cjs/ole-render.js +291 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/paint-canvas.js +701 -0
- package/dist/cjs/paint-svg.js +674 -0
- package/dist/cjs/scene-node.js +762 -0
- package/dist/cjs/selection.js +130 -0
- package/dist/cjs/shape-effects.js +66 -0
- package/dist/cjs/shape-front.js +38 -0
- package/dist/cjs/stage-chrome.js +15 -0
- package/dist/cjs/svg-backend.js +2172 -0
- package/dist/cjs/text-autofit.js +48 -0
- package/dist/cjs/text-hit.js +284 -0
- package/dist/cjs/text-layout.js +408 -0
- package/dist/cjs/text-shared.js +238 -0
- package/dist/cjs/text-stacked.js +82 -0
- package/dist/cjs/text-warp.js +377 -0
- package/dist/cjs/text-wordart.js +33 -0
- package/dist/cjs/video-frame.js +113 -0
- package/dist/cjs/webgl-model3d-backend.js +285 -0
- package/dist/cjs/webgl-shape-backend.js +360 -0
- package/dist/esm/arrow-geom.js +33 -0
- package/dist/esm/canvas-backend.js +1363 -0
- package/dist/esm/cell-borders.js +21 -0
- package/dist/esm/chart-canvas.js +1056 -0
- package/dist/esm/chart-ex-render.js +261 -0
- package/dist/esm/chart-geom.js +296 -0
- package/dist/esm/chart-svg.js +1107 -0
- package/dist/esm/compose.js +71 -0
- package/dist/esm/effect-clip.js +648 -0
- package/dist/esm/entrance-hide.js +59 -0
- package/dist/esm/font-stack.js +100 -0
- package/dist/esm/hit-geom.js +45 -0
- package/dist/esm/hybrid-backend.js +89 -0
- package/dist/esm/hybrid-segments.js +51 -0
- package/dist/esm/index.js +24 -0
- package/dist/esm/linked-src.js +109 -0
- package/dist/esm/metafile-preview.js +135 -0
- package/dist/esm/model3d-frame.js +158 -0
- package/dist/esm/model3d-render.js +73 -0
- package/dist/esm/morph-transition.js +54 -0
- package/dist/esm/node-affine.js +71 -0
- package/dist/esm/ole-render.js +286 -0
- package/dist/esm/paint-canvas.js +693 -0
- package/dist/esm/paint-svg.js +670 -0
- package/dist/esm/scene-node.js +750 -0
- package/dist/esm/selection.js +122 -0
- package/dist/esm/shape-effects.js +60 -0
- package/dist/esm/shape-front.js +35 -0
- package/dist/esm/stage-chrome.js +12 -0
- package/dist/esm/svg-backend.js +2168 -0
- package/dist/esm/text-autofit.js +44 -0
- package/dist/esm/text-hit.js +280 -0
- package/dist/esm/text-layout.js +400 -0
- package/dist/esm/text-shared.js +221 -0
- package/dist/esm/text-stacked.js +78 -0
- package/dist/esm/text-warp.js +371 -0
- package/dist/esm/text-wordart.js +30 -0
- package/dist/esm/video-frame.js +109 -0
- package/dist/esm/webgl-model3d-backend.js +248 -0
- package/dist/esm/webgl-shape-backend.js +323 -0
- package/dist/types/arrow-geom.d.ts +10 -0
- package/dist/types/canvas-backend.d.ts +55 -0
- package/dist/types/cell-borders.d.ts +9 -0
- package/dist/types/chart-canvas.d.ts +2 -0
- package/dist/types/chart-ex-render.d.ts +79 -0
- package/dist/types/chart-geom.d.ts +112 -0
- package/dist/types/chart-svg.d.ts +2 -0
- package/dist/types/compose.d.ts +2 -0
- package/dist/types/effect-clip.d.ts +10 -0
- package/dist/types/entrance-hide.d.ts +16 -0
- package/dist/types/font-stack.d.ts +9 -0
- package/dist/types/hit-geom.d.ts +15 -0
- package/dist/types/hybrid-backend.d.ts +21 -0
- package/dist/types/hybrid-segments.d.ts +11 -0
- package/dist/types/index.d.ts +24 -0
- package/dist/types/linked-src.d.ts +13 -0
- package/dist/types/metafile-preview.d.ts +11 -0
- package/dist/types/model3d-frame.d.ts +8 -0
- package/dist/types/model3d-render.d.ts +4 -0
- package/dist/types/morph-transition.d.ts +15 -0
- package/dist/types/node-affine.d.ts +22 -0
- package/dist/types/ole-render.d.ts +7 -0
- package/dist/types/paint-canvas.d.ts +22 -0
- package/dist/types/paint-svg.d.ts +19 -0
- package/dist/types/scene-node.d.ts +128 -0
- package/dist/types/selection.d.ts +22 -0
- package/dist/types/shape-effects.d.ts +13 -0
- package/dist/types/shape-front.d.ts +4 -0
- package/dist/types/stage-chrome.d.ts +8 -0
- package/dist/types/svg-backend.d.ts +88 -0
- package/dist/types/text-autofit.d.ts +7 -0
- package/dist/types/text-hit.d.ts +7 -0
- package/dist/types/text-layout.d.ts +35 -0
- package/dist/types/text-shared.d.ts +86 -0
- package/dist/types/text-stacked.d.ts +11 -0
- package/dist/types/text-warp.d.ts +37 -0
- package/dist/types/text-wordart.d.ts +18 -0
- package/dist/types/video-frame.d.ts +2 -0
- package/dist/types/webgl-model3d-backend.d.ts +25 -0
- package/dist/types/webgl-shape-backend.d.ts +33 -0
- package/package.json +33 -4
- package/README.md +0 -3
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.WebGLModel3DBackend = void 0;
|
|
37
|
+
const THREE = __importStar(require("three"));
|
|
38
|
+
const model3d_frame_1 = require("./model3d-frame");
|
|
39
|
+
let sharedRenderer = null;
|
|
40
|
+
let sharedRendererFailed = false;
|
|
41
|
+
function getSharedRenderer() {
|
|
42
|
+
if (sharedRenderer)
|
|
43
|
+
return sharedRenderer;
|
|
44
|
+
if (sharedRendererFailed)
|
|
45
|
+
return null;
|
|
46
|
+
try {
|
|
47
|
+
const r = new THREE.WebGLRenderer({ alpha: true, antialias: true, preserveDrawingBuffer: true });
|
|
48
|
+
r.setPixelRatio(1);
|
|
49
|
+
r.setClearColor(0x000000, 0);
|
|
50
|
+
r.outputColorSpace = THREE.SRGBColorSpace;
|
|
51
|
+
sharedRenderer = r;
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
sharedRendererFailed = true;
|
|
55
|
+
}
|
|
56
|
+
return sharedRenderer;
|
|
57
|
+
}
|
|
58
|
+
class WebGLModel3DBackend {
|
|
59
|
+
space;
|
|
60
|
+
managed = [];
|
|
61
|
+
dispose = [];
|
|
62
|
+
k = 1;
|
|
63
|
+
rafId = 0;
|
|
64
|
+
readyResolvers = [];
|
|
65
|
+
constructor(scene, container, opts = {}) {
|
|
66
|
+
const shapeFilter = opts.shapeFilter ?? (() => true);
|
|
67
|
+
const interactive = opts.interactive === true;
|
|
68
|
+
this.space = document.createElement("div");
|
|
69
|
+
this.space.style.cssText = "position:absolute;left:0;top:0;transform-origin:0 0;pointer-events:none";
|
|
70
|
+
this.space.style.width = `${scene.width}px`;
|
|
71
|
+
this.space.style.height = `${scene.height}px`;
|
|
72
|
+
container.appendChild(this.space);
|
|
73
|
+
scene.root.walk((n) => {
|
|
74
|
+
if (n.shape.kind !== "model3d")
|
|
75
|
+
return;
|
|
76
|
+
const s = n.shape;
|
|
77
|
+
if (!shapeFilter(s))
|
|
78
|
+
return;
|
|
79
|
+
if (!s.glbSrc)
|
|
80
|
+
return;
|
|
81
|
+
this.add(n, s, interactive);
|
|
82
|
+
});
|
|
83
|
+
const updateScale = () => {
|
|
84
|
+
const w = container.clientWidth || scene.width;
|
|
85
|
+
const k = w / scene.width;
|
|
86
|
+
if (!Number.isFinite(k) || k <= 0)
|
|
87
|
+
return;
|
|
88
|
+
this.k = k;
|
|
89
|
+
this.space.style.transform = `scale(${k})`;
|
|
90
|
+
this.scheduleRender();
|
|
91
|
+
};
|
|
92
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
93
|
+
const ro = new ResizeObserver(updateScale);
|
|
94
|
+
ro.observe(container);
|
|
95
|
+
this.dispose.push(() => ro.disconnect());
|
|
96
|
+
}
|
|
97
|
+
updateScale();
|
|
98
|
+
this.scheduleRender();
|
|
99
|
+
}
|
|
100
|
+
add(node, s, interactive) {
|
|
101
|
+
const wrapper = document.createElement("div");
|
|
102
|
+
wrapper.style.position = "absolute";
|
|
103
|
+
wrapper.style.overflow = "hidden";
|
|
104
|
+
const canvas = document.createElement("canvas");
|
|
105
|
+
canvas.style.width = "100%";
|
|
106
|
+
canvas.style.height = "100%";
|
|
107
|
+
canvas.style.display = "block";
|
|
108
|
+
wrapper.appendChild(canvas);
|
|
109
|
+
this.space.appendChild(wrapper);
|
|
110
|
+
const m = {
|
|
111
|
+
node,
|
|
112
|
+
bbox: { ...s.bbox },
|
|
113
|
+
wrapper,
|
|
114
|
+
canvas,
|
|
115
|
+
ctx: canvas.getContext("2d"),
|
|
116
|
+
scene: null,
|
|
117
|
+
camera: null,
|
|
118
|
+
lat: s.camera?.rot?.lat ?? 15,
|
|
119
|
+
lon: s.camera?.rot?.lon ?? 25,
|
|
120
|
+
lastW: 0,
|
|
121
|
+
lastH: 0,
|
|
122
|
+
};
|
|
123
|
+
this.managed.push(m);
|
|
124
|
+
if (interactive)
|
|
125
|
+
this.bindOrbit(m);
|
|
126
|
+
void (0, model3d_frame_1.loadGltfScene)(s.glbSrc).then((root) => {
|
|
127
|
+
const sc = new THREE.Scene();
|
|
128
|
+
const clone = root.clone(true);
|
|
129
|
+
(0, model3d_frame_1.applyModel3DTransform)(clone, s);
|
|
130
|
+
sc.add(clone);
|
|
131
|
+
const light = new THREE.DirectionalLight(0xffffff, 1.1);
|
|
132
|
+
light.position.set(2, 3, 4);
|
|
133
|
+
sc.add(light);
|
|
134
|
+
sc.add(new THREE.AmbientLight(0xffffff, 0.45));
|
|
135
|
+
const cam = new THREE.PerspectiveCamera(35, 1, 0.01, 100);
|
|
136
|
+
(0, model3d_frame_1.applyModel3DCameraPose)(cam, m.lat, m.lon);
|
|
137
|
+
m.scene = sc;
|
|
138
|
+
m.camera = cam;
|
|
139
|
+
this.syncWrapper(m);
|
|
140
|
+
this.scheduleRender();
|
|
141
|
+
this.resolveReady();
|
|
142
|
+
}).catch(() => this.resolveReady());
|
|
143
|
+
const off = node.on(() => this.syncWrapper(m));
|
|
144
|
+
this.dispose.push(off);
|
|
145
|
+
}
|
|
146
|
+
bindOrbit(m) {
|
|
147
|
+
const el = m.wrapper;
|
|
148
|
+
el.style.pointerEvents = "auto";
|
|
149
|
+
el.style.cursor = "grab";
|
|
150
|
+
el.style.touchAction = "none";
|
|
151
|
+
let dragging = false;
|
|
152
|
+
let lastX = 0;
|
|
153
|
+
let lastY = 0;
|
|
154
|
+
const stop = (e) => e.stopPropagation();
|
|
155
|
+
el.addEventListener("pointerdown", (e) => {
|
|
156
|
+
if (e.button !== 0)
|
|
157
|
+
return;
|
|
158
|
+
e.stopPropagation();
|
|
159
|
+
e.preventDefault();
|
|
160
|
+
dragging = true;
|
|
161
|
+
lastX = e.clientX;
|
|
162
|
+
lastY = e.clientY;
|
|
163
|
+
try {
|
|
164
|
+
el.setPointerCapture(e.pointerId);
|
|
165
|
+
}
|
|
166
|
+
catch { }
|
|
167
|
+
el.style.cursor = "grabbing";
|
|
168
|
+
});
|
|
169
|
+
el.addEventListener("pointermove", (e) => {
|
|
170
|
+
if (!dragging)
|
|
171
|
+
return;
|
|
172
|
+
e.stopPropagation();
|
|
173
|
+
const dx = e.clientX - lastX;
|
|
174
|
+
const dy = e.clientY - lastY;
|
|
175
|
+
lastX = e.clientX;
|
|
176
|
+
lastY = e.clientY;
|
|
177
|
+
this.orbitModel3D(m.node.shape.id, -dx * 0.45, dy * 0.45);
|
|
178
|
+
});
|
|
179
|
+
const end = (e) => {
|
|
180
|
+
if (!dragging)
|
|
181
|
+
return;
|
|
182
|
+
dragging = false;
|
|
183
|
+
el.style.cursor = "grab";
|
|
184
|
+
try {
|
|
185
|
+
el.releasePointerCapture(e.pointerId);
|
|
186
|
+
}
|
|
187
|
+
catch { }
|
|
188
|
+
e.stopPropagation();
|
|
189
|
+
};
|
|
190
|
+
el.addEventListener("pointerup", end);
|
|
191
|
+
el.addEventListener("pointercancel", end);
|
|
192
|
+
el.addEventListener("mousedown", stop);
|
|
193
|
+
el.addEventListener("click", stop);
|
|
194
|
+
el.addEventListener("dblclick", stop);
|
|
195
|
+
el.addEventListener("touchstart", stop, { passive: true });
|
|
196
|
+
}
|
|
197
|
+
syncWrapper(m) {
|
|
198
|
+
const n = m.node;
|
|
199
|
+
const s = n.shape;
|
|
200
|
+
const b = s.bbox;
|
|
201
|
+
m.bbox = { ...b };
|
|
202
|
+
m.wrapper.style.left = `${b.x}px`;
|
|
203
|
+
m.wrapper.style.top = `${b.y}px`;
|
|
204
|
+
m.wrapper.style.width = `${b.w}px`;
|
|
205
|
+
m.wrapper.style.height = `${b.h}px`;
|
|
206
|
+
m.wrapper.style.opacity = String(n.opacity * ((n.visibleOverride ?? s.visible) !== false ? 1 : 0));
|
|
207
|
+
m.wrapper.style.transform = `translate(${n.tx}px,${n.ty}px) rotate(${n.rotation}deg) scale(${n.scale})`;
|
|
208
|
+
}
|
|
209
|
+
scheduleRender() {
|
|
210
|
+
if (this.rafId)
|
|
211
|
+
return;
|
|
212
|
+
this.rafId = requestAnimationFrame(() => {
|
|
213
|
+
this.rafId = 0;
|
|
214
|
+
this.renderAll();
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
renderAll() {
|
|
218
|
+
const renderer = getSharedRenderer();
|
|
219
|
+
if (!renderer)
|
|
220
|
+
return;
|
|
221
|
+
const dpr = typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1;
|
|
222
|
+
for (const m of this.managed) {
|
|
223
|
+
if (!m.scene || !m.camera || !m.ctx)
|
|
224
|
+
continue;
|
|
225
|
+
const w = Math.max(1, Math.ceil(m.bbox.w * this.k * dpr));
|
|
226
|
+
const h = Math.max(1, Math.ceil(m.bbox.h * this.k * dpr));
|
|
227
|
+
m.lastW = w;
|
|
228
|
+
m.lastH = h;
|
|
229
|
+
if (m.canvas.width !== w)
|
|
230
|
+
m.canvas.width = w;
|
|
231
|
+
if (m.canvas.height !== h)
|
|
232
|
+
m.canvas.height = h;
|
|
233
|
+
m.camera.aspect = w / h;
|
|
234
|
+
m.camera.updateProjectionMatrix();
|
|
235
|
+
renderer.setSize(w, h, false);
|
|
236
|
+
renderer.render(m.scene, m.camera);
|
|
237
|
+
m.ctx.clearRect(0, 0, w, h);
|
|
238
|
+
m.ctx.drawImage(renderer.domElement, 0, 0, w, h);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
resolveReady() {
|
|
242
|
+
const rs = this.readyResolvers.splice(0);
|
|
243
|
+
for (const r of rs)
|
|
244
|
+
r();
|
|
245
|
+
}
|
|
246
|
+
orbitModel3D(shapeId, dLon, dLat) {
|
|
247
|
+
const m = this.managed.find((x) => x.node.shape.id === shapeId);
|
|
248
|
+
if (!m)
|
|
249
|
+
return false;
|
|
250
|
+
if (dLon === 0 && dLat === 0)
|
|
251
|
+
return true;
|
|
252
|
+
m.lon += dLon;
|
|
253
|
+
m.lat = Math.max(-85, Math.min(85, m.lat + dLat));
|
|
254
|
+
if (m.camera) {
|
|
255
|
+
(0, model3d_frame_1.applyModel3DCameraPose)(m.camera, m.lat, m.lon);
|
|
256
|
+
this.scheduleRender();
|
|
257
|
+
}
|
|
258
|
+
return true;
|
|
259
|
+
}
|
|
260
|
+
flush() {
|
|
261
|
+
if (this.rafId) {
|
|
262
|
+
cancelAnimationFrame(this.rafId);
|
|
263
|
+
this.rafId = 0;
|
|
264
|
+
}
|
|
265
|
+
for (const m of this.managed)
|
|
266
|
+
this.syncWrapper(m);
|
|
267
|
+
this.renderAll();
|
|
268
|
+
}
|
|
269
|
+
whenReady() {
|
|
270
|
+
return new Promise((res) => {
|
|
271
|
+
if (this.managed.every((m) => m.scene || !m.node.shape.glbSrc))
|
|
272
|
+
res();
|
|
273
|
+
else
|
|
274
|
+
this.readyResolvers.push(res);
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
destroy() {
|
|
278
|
+
for (const d of this.dispose)
|
|
279
|
+
d();
|
|
280
|
+
this.dispose = [];
|
|
281
|
+
this.space.remove();
|
|
282
|
+
this.managed = [];
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
exports.WebGLModel3DBackend = WebGLModel3DBackend;
|
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.WebGLShapeBackend = void 0;
|
|
37
|
+
const THREE = __importStar(require("three"));
|
|
38
|
+
const three_bake_1 = require("@sliiu/present-three-bake");
|
|
39
|
+
const node_affine_1 = require("./node-affine");
|
|
40
|
+
const shape_front_1 = require("./shape-front");
|
|
41
|
+
// ════════════════════════════════════════════════════════════════════════════
|
|
42
|
+
// Live WebGL 3D backend —— player / editor 的 hybrid 视图里,每个需要 3D 的
|
|
43
|
+
// image / 矩形 / 椭圆 / 路径 / 多边形用一块定位 canvas 实时渲染 three.js 3D
|
|
44
|
+
// (透视 / 倒角 / 光照 / 材质)。好处:
|
|
45
|
+
// - 缩放清晰(按显示像素重渲,不再是固定分辨率位图);
|
|
46
|
+
// - 动画(entrance/emphasis 的 tx/ty/scale/opacity)过程中 3D 形体保持立体、光照正确。
|
|
47
|
+
//
|
|
48
|
+
// 设计:
|
|
49
|
+
// - 一个 "slide-space" 容器(slide 像素尺寸)用 transform:scale(k) 适配 layer 实际像素,
|
|
50
|
+
// ResizeObserver 跟随更新;每个 shape 的 wrapper 用 slide 单位定位。
|
|
51
|
+
// - 共享一个 WebGLRenderer(单 GL context),逐 shape 渲染到其 backing 尺寸再 drawImage
|
|
52
|
+
// 到该 shape 的 2D 展示 canvas,避免 per-shape GL context 累积。
|
|
53
|
+
// - 几何/材质/相机全部复用 @sliiu/present-three-bake 的 buildThreeScene / createSceneCamera,
|
|
54
|
+
// 与离屏光栅(缩略图/截图)完全一致。
|
|
55
|
+
// - 每帧 2D 变换(tx/ty/rotation/scale/opacity)经 node.on() 同步到 wrapper CSS,
|
|
56
|
+
// 不重渲 GL(3D 相机不随动画变);仅 resize / 贴图加载完成时重渲 GL。
|
|
57
|
+
// ════════════════════════════════════════════════════════════════════════════
|
|
58
|
+
// ── 共享 WebGLRenderer(单 GL context,跨 backend 实例复用)──────────────────────
|
|
59
|
+
// 编辑器拖动时 drawSlide 每帧重建整个 backend(含 WebGLShapeBackend)。若每个实例都
|
|
60
|
+
// 自建/销毁一个 WebGLRenderer,会以 ~60fps 频繁创建 + forceContextLoss GL context:
|
|
61
|
+
// 浏览器对活动 context 数量有上限、create/loss 自身也有开销,会引发掉帧甚至闪烁。
|
|
62
|
+
// 这里改用一个 module 级单例 renderer,所有 WebGLShapeBackend 复用它(逐 shape
|
|
63
|
+
// setSize→render→drawImage 是同步串行的,复用安全)。backend.destroy() 不再销毁它。
|
|
64
|
+
let sharedRenderer = null;
|
|
65
|
+
let sharedRendererFailed = false;
|
|
66
|
+
function getSharedRenderer() {
|
|
67
|
+
if (sharedRenderer)
|
|
68
|
+
return sharedRenderer;
|
|
69
|
+
if (sharedRendererFailed)
|
|
70
|
+
return null;
|
|
71
|
+
try {
|
|
72
|
+
const r = new THREE.WebGLRenderer({
|
|
73
|
+
alpha: true,
|
|
74
|
+
antialias: true,
|
|
75
|
+
premultipliedAlpha: true,
|
|
76
|
+
preserveDrawingBuffer: true,
|
|
77
|
+
});
|
|
78
|
+
r.setPixelRatio(1);
|
|
79
|
+
r.setClearColor(0x000000, 0);
|
|
80
|
+
r.outputColorSpace = THREE.SRGBColorSpace;
|
|
81
|
+
sharedRenderer = r;
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
sharedRendererFailed = true;
|
|
85
|
+
sharedRenderer = null;
|
|
86
|
+
}
|
|
87
|
+
return sharedRenderer;
|
|
88
|
+
}
|
|
89
|
+
function paddedRect(bbox, pad) {
|
|
90
|
+
const padW = Math.ceil(bbox.w * (1 + pad));
|
|
91
|
+
const padH = Math.ceil(bbox.h * (1 + pad));
|
|
92
|
+
return {
|
|
93
|
+
x: bbox.x - (padW - bbox.w) / 2,
|
|
94
|
+
y: bbox.y - (padH - bbox.h) / 2,
|
|
95
|
+
w: padW,
|
|
96
|
+
h: padH,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
class WebGLShapeBackend {
|
|
100
|
+
scene;
|
|
101
|
+
space;
|
|
102
|
+
managed = [];
|
|
103
|
+
dispose = [];
|
|
104
|
+
dpr = typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1;
|
|
105
|
+
k = 1;
|
|
106
|
+
rafId = 0;
|
|
107
|
+
ro = null;
|
|
108
|
+
readyResolvers = [];
|
|
109
|
+
constructor(scene, container, opts = {}) {
|
|
110
|
+
this.scene = scene;
|
|
111
|
+
const filter = opts.shapeFilter ?? (() => true);
|
|
112
|
+
this.space = document.createElement("div");
|
|
113
|
+
this.space.style.position = "absolute";
|
|
114
|
+
this.space.style.left = "0";
|
|
115
|
+
this.space.style.top = "0";
|
|
116
|
+
this.space.style.width = `${scene.width}px`;
|
|
117
|
+
this.space.style.height = `${scene.height}px`;
|
|
118
|
+
this.space.style.transformOrigin = "0 0";
|
|
119
|
+
this.space.style.pointerEvents = "none";
|
|
120
|
+
container.appendChild(this.space);
|
|
121
|
+
scene.root.walk((n) => {
|
|
122
|
+
const s = n.shape;
|
|
123
|
+
if (!(0, three_bake_1.shapeSupports3D)(s))
|
|
124
|
+
return;
|
|
125
|
+
if (!filter(s))
|
|
126
|
+
return;
|
|
127
|
+
if (!(0, three_bake_1.needs3DRender)((0, three_bake_1.shape3DInfo)(s)))
|
|
128
|
+
return;
|
|
129
|
+
this.add(n);
|
|
130
|
+
});
|
|
131
|
+
// 适配 layer 实际像素:k = 实际宽 / slide 宽。
|
|
132
|
+
const updateScale = () => {
|
|
133
|
+
const w = container.clientWidth || scene.width;
|
|
134
|
+
const k = w / scene.width;
|
|
135
|
+
if (!Number.isFinite(k) || k <= 0)
|
|
136
|
+
return;
|
|
137
|
+
if (Math.abs(k - this.k) < 1e-4)
|
|
138
|
+
return;
|
|
139
|
+
this.k = k;
|
|
140
|
+
this.space.style.transform = `scale(${k})`;
|
|
141
|
+
this.scheduleGlRender();
|
|
142
|
+
};
|
|
143
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
144
|
+
this.ro = new ResizeObserver(updateScale);
|
|
145
|
+
this.ro.observe(container);
|
|
146
|
+
}
|
|
147
|
+
updateScale();
|
|
148
|
+
this.scheduleSync();
|
|
149
|
+
this.scheduleGlRender();
|
|
150
|
+
}
|
|
151
|
+
add(node) {
|
|
152
|
+
const s = node.shape;
|
|
153
|
+
const meta = (0, three_bake_1.shape3DInfo)(s);
|
|
154
|
+
if (!meta)
|
|
155
|
+
return;
|
|
156
|
+
const pad = (0, three_bake_1.shape3DPad)(s.kind);
|
|
157
|
+
const baseBBox = { x: s.bbox.x, y: s.bbox.y, w: s.bbox.w, h: s.bbox.h };
|
|
158
|
+
const paddedBBox = paddedRect(baseBBox, pad);
|
|
159
|
+
const resolved = (0, three_bake_1.resolveScene3D)(meta);
|
|
160
|
+
const wrapper = document.createElement("div");
|
|
161
|
+
wrapper.style.position = "absolute";
|
|
162
|
+
wrapper.style.left = `${paddedBBox.x}px`;
|
|
163
|
+
wrapper.style.top = `${paddedBBox.y}px`;
|
|
164
|
+
wrapper.style.width = `${paddedBBox.w}px`;
|
|
165
|
+
wrapper.style.height = `${paddedBBox.h}px`;
|
|
166
|
+
wrapper.style.pointerEvents = "none";
|
|
167
|
+
const canvas = document.createElement("canvas");
|
|
168
|
+
canvas.style.width = "100%";
|
|
169
|
+
canvas.style.height = "100%";
|
|
170
|
+
canvas.style.display = "block";
|
|
171
|
+
wrapper.appendChild(canvas);
|
|
172
|
+
this.space.appendChild(wrapper);
|
|
173
|
+
const m = {
|
|
174
|
+
node,
|
|
175
|
+
baseBBox,
|
|
176
|
+
paddedBBox,
|
|
177
|
+
pad,
|
|
178
|
+
resolved,
|
|
179
|
+
wrapper,
|
|
180
|
+
canvas,
|
|
181
|
+
ctx: canvas.getContext("2d"),
|
|
182
|
+
built: null,
|
|
183
|
+
camera: null,
|
|
184
|
+
texture: null,
|
|
185
|
+
lastBackingW: 0,
|
|
186
|
+
lastBackingH: 0,
|
|
187
|
+
disposed: false,
|
|
188
|
+
};
|
|
189
|
+
this.managed.push(m);
|
|
190
|
+
const src = s.kind === "image" ? s.src : s.kind === "text" ? (0, shape_front_1.shapeFrontImageSrc)(this.scene, s) : undefined;
|
|
191
|
+
// 贴图此前已解码过(典型:初次加载已经走过一遍)→ 同步构建场景并 built 就绪,
|
|
192
|
+
// 这样紧随其后的 flush() 能当帧首渲,drawSlide 双缓冲不会出现 3D 空一帧的闪屏。
|
|
193
|
+
const cachedTex = src ? (0, three_bake_1.getCachedImageTexture)(src) : null;
|
|
194
|
+
if (src ? cachedTex !== null : true) {
|
|
195
|
+
this.buildManaged(m, s, cachedTex);
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
// 首次(未缓存)走异步解码;解码完成后会缓存,后续重建即走上面的同步路径。
|
|
199
|
+
void (async () => {
|
|
200
|
+
const texture = src ? await (0, three_bake_1.loadImageTexture)(src) : null;
|
|
201
|
+
if (m.disposed) {
|
|
202
|
+
texture?.dispose();
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
this.buildManaged(m, s, texture);
|
|
206
|
+
})();
|
|
207
|
+
}
|
|
208
|
+
let cur = node;
|
|
209
|
+
while (cur) {
|
|
210
|
+
const off = cur.on(() => this.scheduleSync());
|
|
211
|
+
this.dispose.push(off);
|
|
212
|
+
cur = cur.parent;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
/** 用(可能为空的)贴图构建 three 场景 + 相机,写回 managed 并触发首渲。 */
|
|
216
|
+
buildManaged(m, s, texture) {
|
|
217
|
+
if (m.disposed) {
|
|
218
|
+
texture?.dispose();
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
const isPicture = s.kind === "image" || (s.kind === "text" && !!texture);
|
|
222
|
+
if (s.kind === "image" && texture)
|
|
223
|
+
(0, three_bake_1.applyTextureCrop)(texture, s.cropRect);
|
|
224
|
+
m.texture = texture;
|
|
225
|
+
m.built = (0, three_bake_1.buildThreeScene)({
|
|
226
|
+
baseW: Math.max(8, m.baseBBox.w),
|
|
227
|
+
baseH: Math.max(8, m.baseBBox.h),
|
|
228
|
+
pathD: isPicture ? undefined : (0, three_bake_1.shape3DPathD)(s),
|
|
229
|
+
fill: s.fill,
|
|
230
|
+
stroke: s.stroke,
|
|
231
|
+
resolved: m.resolved,
|
|
232
|
+
texture: isPicture ? texture : null,
|
|
233
|
+
isImage: isPicture,
|
|
234
|
+
});
|
|
235
|
+
m.camera = (0, three_bake_1.createSceneCamera)(m.resolved.camera, m.built.geoW, m.built.geoH, m.paddedBBox.w, m.paddedBBox.h, m.pad, m.resolved.backdrop);
|
|
236
|
+
this.scheduleGlRender();
|
|
237
|
+
this.checkReady();
|
|
238
|
+
}
|
|
239
|
+
getRenderer() {
|
|
240
|
+
return getSharedRenderer();
|
|
241
|
+
}
|
|
242
|
+
// ── 每帧 2D 变换同步(CSS,不重渲 GL)─────────────────────────────────────────
|
|
243
|
+
scheduleSync() {
|
|
244
|
+
if (this.rafId)
|
|
245
|
+
return;
|
|
246
|
+
this.rafId = requestAnimationFrame(() => {
|
|
247
|
+
this.rafId = 0;
|
|
248
|
+
this.syncTransforms();
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
syncTransforms() {
|
|
252
|
+
for (const m of this.managed)
|
|
253
|
+
this.syncOne(m);
|
|
254
|
+
}
|
|
255
|
+
syncOne(m) {
|
|
256
|
+
const node = m.node;
|
|
257
|
+
const visible = !(0, node_affine_1.chainHidden)(node);
|
|
258
|
+
m.wrapper.style.display = visible ? "block" : "none";
|
|
259
|
+
if (!visible)
|
|
260
|
+
return;
|
|
261
|
+
// wrapper 放在 paddedBBox。CSS 原点取左上角,矩阵里已经包含祖先组合的旋转。
|
|
262
|
+
const css = (0, node_affine_1.cssAffineForBox)((0, node_affine_1.accumulatedAffine)(node), m.paddedBBox.x, m.paddedBBox.y);
|
|
263
|
+
m.wrapper.style.transformOrigin = "0 0";
|
|
264
|
+
m.wrapper.style.transform = `matrix(${css.a}, ${css.b}, ${css.c}, ${css.d}, ${css.e}, ${css.f})`;
|
|
265
|
+
let alpha = (0, node_affine_1.chainOpacity)(node);
|
|
266
|
+
const clip = node.clip;
|
|
267
|
+
if (clip && clip.kind === "effect" && clip.filter === "fade") {
|
|
268
|
+
const tr = clip.transition ?? "none";
|
|
269
|
+
alpha *= tr === "none" ? 1 : Math.max(0, Math.min(1, clip.progress ?? 0));
|
|
270
|
+
}
|
|
271
|
+
m.wrapper.style.opacity = String(alpha);
|
|
272
|
+
}
|
|
273
|
+
// ── GL 重渲(仅 resize / 贴图加载完成时)──────────────────────────────────────
|
|
274
|
+
scheduleGlRender() {
|
|
275
|
+
if (typeof requestAnimationFrame === "undefined") {
|
|
276
|
+
this.glRenderAll();
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
requestAnimationFrame(() => this.glRenderAll());
|
|
280
|
+
}
|
|
281
|
+
glRenderAll() {
|
|
282
|
+
const renderer = this.getRenderer();
|
|
283
|
+
if (!renderer)
|
|
284
|
+
return;
|
|
285
|
+
for (const m of this.managed)
|
|
286
|
+
this.glRenderOne(renderer, m);
|
|
287
|
+
}
|
|
288
|
+
glRenderOne(renderer, m) {
|
|
289
|
+
if (m.disposed || !m.built || !m.camera || !m.ctx)
|
|
290
|
+
return;
|
|
291
|
+
const backingW = Math.max(1, Math.ceil(m.paddedBBox.w * this.k * this.dpr));
|
|
292
|
+
const backingH = Math.max(1, Math.ceil(m.paddedBBox.h * this.k * this.dpr));
|
|
293
|
+
if (backingW === m.lastBackingW && backingH === m.lastBackingH && m.canvas.width === backingW) {
|
|
294
|
+
// 尺寸没变也重渲一次(贴图刚加载完会走到这里);但避免无意义的频繁循环。
|
|
295
|
+
}
|
|
296
|
+
renderer.setSize(backingW, backingH, false);
|
|
297
|
+
renderer.render(m.built.scene, m.camera);
|
|
298
|
+
if (m.canvas.width !== backingW)
|
|
299
|
+
m.canvas.width = backingW;
|
|
300
|
+
if (m.canvas.height !== backingH)
|
|
301
|
+
m.canvas.height = backingH;
|
|
302
|
+
m.ctx.clearRect(0, 0, backingW, backingH);
|
|
303
|
+
try {
|
|
304
|
+
m.ctx.drawImage(renderer.domElement, 0, 0, backingW, backingH);
|
|
305
|
+
}
|
|
306
|
+
catch {
|
|
307
|
+
// drawImage 偶发失败(context lost)忽略,下次 resize 再试。
|
|
308
|
+
}
|
|
309
|
+
m.lastBackingW = backingW;
|
|
310
|
+
m.lastBackingH = backingH;
|
|
311
|
+
}
|
|
312
|
+
flush() {
|
|
313
|
+
if (this.rafId) {
|
|
314
|
+
cancelAnimationFrame(this.rafId);
|
|
315
|
+
this.rafId = 0;
|
|
316
|
+
}
|
|
317
|
+
this.syncTransforms();
|
|
318
|
+
this.glRenderAll();
|
|
319
|
+
}
|
|
320
|
+
/** 所有 3D shape 的贴图都加载完、场景已构建并首渲后 resolve(截图前等待用)。 */
|
|
321
|
+
whenReady() {
|
|
322
|
+
if (this.isReady()) {
|
|
323
|
+
this.flush();
|
|
324
|
+
return Promise.resolve();
|
|
325
|
+
}
|
|
326
|
+
return new Promise((res) => this.readyResolvers.push(res));
|
|
327
|
+
}
|
|
328
|
+
isReady() {
|
|
329
|
+
return this.managed.every((m) => m.built || m.disposed);
|
|
330
|
+
}
|
|
331
|
+
checkReady() {
|
|
332
|
+
if (!this.isReady())
|
|
333
|
+
return;
|
|
334
|
+
this.flush();
|
|
335
|
+
const rs = this.readyResolvers;
|
|
336
|
+
this.readyResolvers = [];
|
|
337
|
+
for (const r of rs)
|
|
338
|
+
r();
|
|
339
|
+
}
|
|
340
|
+
destroy() {
|
|
341
|
+
if (this.rafId)
|
|
342
|
+
cancelAnimationFrame(this.rafId);
|
|
343
|
+
this.rafId = 0;
|
|
344
|
+
for (const off of this.dispose)
|
|
345
|
+
off();
|
|
346
|
+
this.dispose = [];
|
|
347
|
+
this.ro?.disconnect();
|
|
348
|
+
this.ro = null;
|
|
349
|
+
for (const m of this.managed) {
|
|
350
|
+
m.disposed = true;
|
|
351
|
+
m.built?.dispose();
|
|
352
|
+
m.texture?.dispose();
|
|
353
|
+
}
|
|
354
|
+
this.managed = [];
|
|
355
|
+
// 不销毁 sharedRenderer:它是跨 backend 实例复用的单例 GL context(见顶部注释)。
|
|
356
|
+
// 编辑器拖动每帧重建 backend,这里若 forceContextLoss 会造成 context 抖动 / 闪屏。
|
|
357
|
+
this.space.remove();
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
exports.WebGLShapeBackend = WebGLShapeBackend;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// 线端箭头(headEnd / tailEnd)的几何,SVG 与 Canvas 两个 backend 共用,保证导出一致。
|
|
2
|
+
// 尺寸单位为 strokeWidth 的倍数(与 SVG markerUnits="strokeWidth" 一致)。
|
|
3
|
+
export function arrowSize(spec) {
|
|
4
|
+
const wMap = { sm: 3, med: 5, lg: 8 };
|
|
5
|
+
const lMap = { sm: 3, med: 5, lg: 8 };
|
|
6
|
+
return { w: wMap[spec.w ?? "med"], l: lMap[spec.len ?? "med"] };
|
|
7
|
+
}
|
|
8
|
+
export function arrowPathD(kind, w, l, side) {
|
|
9
|
+
if (kind === "oval") {
|
|
10
|
+
return `M 0 ${-w / 2} a ${l / 2} ${w / 2} 0 1 0 0 ${w} a ${l / 2} ${w / 2} 0 1 0 0 ${-w} z`;
|
|
11
|
+
}
|
|
12
|
+
if (kind === "diamond") {
|
|
13
|
+
return `M 0 0 L ${l / 2} ${-w / 2} L ${l} 0 L ${l / 2} ${w / 2} Z`;
|
|
14
|
+
}
|
|
15
|
+
if (kind === "stealth") {
|
|
16
|
+
return side === "end"
|
|
17
|
+
? `M 0 ${-w / 2} L ${l} 0 L 0 ${w / 2} L ${l * 0.3} 0 Z`
|
|
18
|
+
: `M ${l} ${-w / 2} L 0 0 L ${l} ${w / 2} L ${l * 0.7} 0 Z`;
|
|
19
|
+
}
|
|
20
|
+
return side === "end"
|
|
21
|
+
? `M 0 ${-w / 2} L ${l} 0 L 0 ${w / 2} Z`
|
|
22
|
+
: `M ${l} ${-w / 2} L 0 0 L ${l} ${w / 2} Z`;
|
|
23
|
+
}
|
|
24
|
+
// PowerPoint / WPS 约定下,三角 / 隐身箭头的"基底"锚定在线端点,"尖端"向外延伸;
|
|
25
|
+
// 圆 / 菱形等中心对称形状骑跨在线端点上(中心 = 端点)。orient="auto" 让 +X 沿线方向,
|
|
26
|
+
// 因此 end 侧 refX=0(基底在端点、尖端向 +X 外延),start 侧 path 已镜像、refX=l。
|
|
27
|
+
export function arrowRefX(kind, side, l) {
|
|
28
|
+
if (kind === "oval")
|
|
29
|
+
return 0;
|
|
30
|
+
if (kind === "diamond")
|
|
31
|
+
return l / 2;
|
|
32
|
+
return side === "end" ? 0 : l;
|
|
33
|
+
}
|