@fengyizhi/scene-lib 0.0.0-stage → 1.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/assets/draco/encoder/draco_encoder.js +98 -0
- package/assets/draco/encoder/draco_encoder.wasm +0 -0
- package/assets/draco/gltf/draco_decoder.js +33 -0
- package/assets/draco/gltf/draco_decoder.wasm +0 -0
- package/assets/draco/gltf/draco_wasm_wrapper.js +116 -0
- package/fesm2022/fengyizhi-scene-lib.mjs +6838 -0
- package/fesm2022/fengyizhi-scene-lib.mjs.map +1 -0
- package/index.d.ts +1535 -0
- package/package.json +21 -3
- package/README.md +0 -3
package/index.d.ts
ADDED
|
@@ -0,0 +1,1535 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
import * as i0 from '@angular/core';
|
|
3
|
+
import { AfterViewInit, OnChanges, OnDestroy, ElementRef, EventEmitter, NgZone } from '@angular/core';
|
|
4
|
+
import { TransformControls } from 'three/examples/jsm/controls/TransformControls.js';
|
|
5
|
+
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
|
|
6
|
+
|
|
7
|
+
/** 看板数据点(value widget 用) */
|
|
8
|
+
interface DashboardDataPoint {
|
|
9
|
+
/** 标签 */
|
|
10
|
+
label: string;
|
|
11
|
+
/** 显示绑定点 ID */
|
|
12
|
+
bindingId: string;
|
|
13
|
+
/** 动画绑定点 ID */
|
|
14
|
+
animationBindingId?: string;
|
|
15
|
+
/** 单位 */
|
|
16
|
+
unit?: string;
|
|
17
|
+
/** 数据源(可选,如果不用绑定点) */
|
|
18
|
+
dataSource?: {
|
|
19
|
+
mode: 'static' | 'simulated' | 'api';
|
|
20
|
+
staticValue?: number | string;
|
|
21
|
+
simulated?: {
|
|
22
|
+
type: 'sine' | 'random';
|
|
23
|
+
min: number;
|
|
24
|
+
max: number;
|
|
25
|
+
interval: number;
|
|
26
|
+
};
|
|
27
|
+
api?: {
|
|
28
|
+
url: string;
|
|
29
|
+
field: string;
|
|
30
|
+
pollInterval: number;
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/** 看板 Widget */
|
|
35
|
+
interface DashboardWidget {
|
|
36
|
+
id: string;
|
|
37
|
+
type: 'value' | 'gauge' | 'progress' | 'status' | 'chart' | 'text';
|
|
38
|
+
/** 布局模式:screen=屏幕固定,world=绑定3D位置 */
|
|
39
|
+
mode: 'screen' | 'world';
|
|
40
|
+
/** screen 模式:百分比坐标 0-1 */
|
|
41
|
+
screen?: {
|
|
42
|
+
x: number;
|
|
43
|
+
y: number;
|
|
44
|
+
width: number;
|
|
45
|
+
height: number;
|
|
46
|
+
};
|
|
47
|
+
/** world 模式:3D 坐标 */
|
|
48
|
+
world?: {
|
|
49
|
+
position: Vec3;
|
|
50
|
+
};
|
|
51
|
+
/** 数据源(非 value 类型用) */
|
|
52
|
+
dataSource?: {
|
|
53
|
+
mode: 'static' | 'simulated' | 'api';
|
|
54
|
+
staticValue?: number | string;
|
|
55
|
+
simulated?: {
|
|
56
|
+
type: 'sine' | 'random';
|
|
57
|
+
min: number;
|
|
58
|
+
max: number;
|
|
59
|
+
interval: number;
|
|
60
|
+
};
|
|
61
|
+
api?: {
|
|
62
|
+
url: string;
|
|
63
|
+
field: string;
|
|
64
|
+
pollInterval: number;
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
/** 数据点列表(value 类型用,多值显示) */
|
|
68
|
+
dataPoints?: DashboardDataPoint[];
|
|
69
|
+
/** 显示标签 */
|
|
70
|
+
label?: string;
|
|
71
|
+
/** 单位 */
|
|
72
|
+
unit?: string;
|
|
73
|
+
/** 最小值(仪表/进度条用) */
|
|
74
|
+
min?: number;
|
|
75
|
+
/** 最大值(仪表/进度条用) */
|
|
76
|
+
max?: number;
|
|
77
|
+
/** 阈值变色 */
|
|
78
|
+
thresholds?: {
|
|
79
|
+
value: number;
|
|
80
|
+
color: string;
|
|
81
|
+
}[];
|
|
82
|
+
/** 小数位数 */
|
|
83
|
+
decimals?: number;
|
|
84
|
+
}
|
|
85
|
+
/** 看板配置 */
|
|
86
|
+
interface DashboardConfig {
|
|
87
|
+
/** 是否显示 */
|
|
88
|
+
visible: boolean;
|
|
89
|
+
/** Widget 列表 */
|
|
90
|
+
widgets: DashboardWidget[];
|
|
91
|
+
}
|
|
92
|
+
/** 告警配置 */
|
|
93
|
+
interface AlarmConfig {
|
|
94
|
+
/** 查询告警详情的 API URL */
|
|
95
|
+
apiUrl: string;
|
|
96
|
+
/** 弹框显示时长(毫秒) */
|
|
97
|
+
displayDuration: number;
|
|
98
|
+
}
|
|
99
|
+
/** 场景文档模型(scene.json v3)——纯数据,不依赖 three / Angular */
|
|
100
|
+
type Vec3 = [number, number, number];
|
|
101
|
+
type PrimitiveKind = 'box' | 'sphere' | 'cylinder' | 'group' | 'asset' | 'light' | 'label';
|
|
102
|
+
interface TransformState {
|
|
103
|
+
position: Vec3;
|
|
104
|
+
/** 欧拉角,单位:度(对 UI 友好) */
|
|
105
|
+
rotation: Vec3;
|
|
106
|
+
scale: Vec3;
|
|
107
|
+
}
|
|
108
|
+
interface NodeDoc extends TransformState {
|
|
109
|
+
id: string;
|
|
110
|
+
name: string;
|
|
111
|
+
primitive: PrimitiveKind;
|
|
112
|
+
visible: boolean;
|
|
113
|
+
children: string[];
|
|
114
|
+
/** 标签文字内容(仅 label 节点) */
|
|
115
|
+
text?: string;
|
|
116
|
+
/** 资产 ID(服务器分配的 assetId) */
|
|
117
|
+
assetId?: string;
|
|
118
|
+
/** 资产库 ID(如 '021000') */
|
|
119
|
+
assetLibraryId?: string;
|
|
120
|
+
/** 资产文件 URL(运行时生成,不持久化也可) */
|
|
121
|
+
assetUrl?: string;
|
|
122
|
+
/** 资产显示名称 */
|
|
123
|
+
assetName?: string;
|
|
124
|
+
/** 资产文件格式(如 'glb', 'gltf', 'obj') */
|
|
125
|
+
assetFormat?: string;
|
|
126
|
+
/** MTL 文件路径(仅 OBJ 格式时使用) */
|
|
127
|
+
assetMtlUrl?: string;
|
|
128
|
+
materialOverrides?: Record<string, MaterialOverride>;
|
|
129
|
+
animations?: AnimationRule[];
|
|
130
|
+
effects?: VisualEffect[];
|
|
131
|
+
lightType?: 'ambient' | 'directional' | 'point' | 'spot';
|
|
132
|
+
lightColor?: string;
|
|
133
|
+
lightIntensity?: number;
|
|
134
|
+
lightCastShadow?: boolean;
|
|
135
|
+
lightTarget?: Vec3;
|
|
136
|
+
/** 点光源/聚光灯照射距离(0 = 无限) */
|
|
137
|
+
lightDistance?: number;
|
|
138
|
+
/** 光照衰减率 */
|
|
139
|
+
lightDecay?: number;
|
|
140
|
+
/** 聚光灯锥角(弧度) */
|
|
141
|
+
lightAngle?: number;
|
|
142
|
+
/** 聚光灯边缘柔和度(0-1) */
|
|
143
|
+
lightPenumbra?: number;
|
|
144
|
+
}
|
|
145
|
+
/** 材质覆盖:场景级别,不修改资产本身 */
|
|
146
|
+
interface MaterialOverride {
|
|
147
|
+
color?: string;
|
|
148
|
+
opacity?: number;
|
|
149
|
+
roughness?: number;
|
|
150
|
+
metalness?: number;
|
|
151
|
+
emissive?: string;
|
|
152
|
+
visible?: boolean;
|
|
153
|
+
map?: string;
|
|
154
|
+
normalMap?: string;
|
|
155
|
+
roughnessMap?: string;
|
|
156
|
+
metalnessMap?: string;
|
|
157
|
+
emissiveMap?: string;
|
|
158
|
+
/** 基础色是否与贴图叠加(默认 true;关闭后基础色强制 #ffffff,贴图原样显示) */
|
|
159
|
+
useBaseColor?: boolean;
|
|
160
|
+
}
|
|
161
|
+
/** 动画/绑定规则 */
|
|
162
|
+
interface AnimationRule {
|
|
163
|
+
id: string;
|
|
164
|
+
targetMeshName: string;
|
|
165
|
+
property: string;
|
|
166
|
+
type: 'continuous' | 'oscillate' | 'data-driven' | 'trigger' | 'emissive' | 'emissive-constant' | 'emissive-temperature';
|
|
167
|
+
speed?: number;
|
|
168
|
+
from?: number;
|
|
169
|
+
to?: number;
|
|
170
|
+
duration?: number;
|
|
171
|
+
easing?: string;
|
|
172
|
+
triggerBy?: 'click' | 'manual';
|
|
173
|
+
dataSource?: string;
|
|
174
|
+
dataMin?: number;
|
|
175
|
+
dataMax?: number;
|
|
176
|
+
valueMin?: number;
|
|
177
|
+
valueMax?: number;
|
|
178
|
+
/** 告警颜色(emissive 类型用,如 '#ff0000') */
|
|
179
|
+
alarmColor?: string;
|
|
180
|
+
/** 高亮颜色(emissive-constant 用,如 '#00aaff') */
|
|
181
|
+
highlightColor?: string;
|
|
182
|
+
/** 点位注释(自动生成,人能看懂的描述,如"空调1号温度传感器") */
|
|
183
|
+
pointComment?: string;
|
|
184
|
+
/** 温度梯度颜色(emissive-temperature 用,如 ['#00ff00', '#ffff00', '#ff0000']) */
|
|
185
|
+
tempColors?: string[];
|
|
186
|
+
/** 唯一绑定 ID,用于关联实时数据点位(对应 point 表的 ElementIDName3d) */
|
|
187
|
+
bindingId?: string;
|
|
188
|
+
/** 预设动画名称(如'旋转风扇',由编辑器自动生成) */
|
|
189
|
+
presetName?: string;
|
|
190
|
+
}
|
|
191
|
+
type VisualEffectKind = 'particle' | 'uv-animation' | 'path-particle' | 'screen-content';
|
|
192
|
+
interface VisualEffectBase {
|
|
193
|
+
id: string;
|
|
194
|
+
kind: VisualEffectKind;
|
|
195
|
+
enabled: boolean;
|
|
196
|
+
/** 目标子网格名(可选) */
|
|
197
|
+
targetMeshName?: string;
|
|
198
|
+
}
|
|
199
|
+
/** UV 动画(管道液体流动、电缆电流) */
|
|
200
|
+
interface UvAnimationEffect extends VisualEffectBase {
|
|
201
|
+
kind: 'uv-animation';
|
|
202
|
+
/** U 方向滚动速度(UV 坐标/秒) */
|
|
203
|
+
speedU: number;
|
|
204
|
+
/** V 方向滚动速度 */
|
|
205
|
+
speedV: number;
|
|
206
|
+
}
|
|
207
|
+
/** 粒子效果(蒸汽、烟雾) */
|
|
208
|
+
interface ParticleEffect extends VisualEffectBase {
|
|
209
|
+
kind: 'particle';
|
|
210
|
+
/** 发射速率(粒子/秒) */
|
|
211
|
+
emissionRate: number;
|
|
212
|
+
/** 粒子最大数量 */
|
|
213
|
+
maxParticles: number;
|
|
214
|
+
/** 粒子生命周期(秒) */
|
|
215
|
+
lifetime: number;
|
|
216
|
+
/** 发射方向 */
|
|
217
|
+
direction: Vec3;
|
|
218
|
+
/** 发射锥角(弧度) */
|
|
219
|
+
spreadAngle: number;
|
|
220
|
+
/** 初速度 */
|
|
221
|
+
speed: number;
|
|
222
|
+
/** 速度衰减 */
|
|
223
|
+
damping: number;
|
|
224
|
+
/** 粒子大小 */
|
|
225
|
+
size: number;
|
|
226
|
+
/** 大小随机范围 */
|
|
227
|
+
sizeRandom: number;
|
|
228
|
+
startColor: string;
|
|
229
|
+
endColor: string;
|
|
230
|
+
startOpacity: number;
|
|
231
|
+
endOpacity: number;
|
|
232
|
+
/** 重力(0=无) */
|
|
233
|
+
gravity: number;
|
|
234
|
+
blendMode: 'normal' | 'additive';
|
|
235
|
+
}
|
|
236
|
+
/** 路径粒子效果(管道水流、光纤) */
|
|
237
|
+
interface PathParticleEffect extends VisualEffectBase {
|
|
238
|
+
kind: 'path-particle';
|
|
239
|
+
/** 路径点数组(世界坐标) */
|
|
240
|
+
pathPoints: Vec3[];
|
|
241
|
+
/** 段类型数组,长度 = pathPoints.length - 1
|
|
242
|
+
* - 'linear': 直线段
|
|
243
|
+
* - 'smooth': 平滑曲线段(CatmullRom)
|
|
244
|
+
*/
|
|
245
|
+
segmentTypes: ('linear' | 'smooth')[];
|
|
246
|
+
/** 粒子数量 */
|
|
247
|
+
particleCount: number;
|
|
248
|
+
/** 粒子大小 */
|
|
249
|
+
size: number;
|
|
250
|
+
/** 流动速度(路径进度/秒,0-1) */
|
|
251
|
+
speed: number;
|
|
252
|
+
/** 粒子颜色 */
|
|
253
|
+
color: string;
|
|
254
|
+
/** 粒子透明度 */
|
|
255
|
+
opacity: number;
|
|
256
|
+
/** 混合模式 */
|
|
257
|
+
blendMode: 'normal' | 'additive';
|
|
258
|
+
/** 是否循环 */
|
|
259
|
+
loop: boolean;
|
|
260
|
+
/** 粒子外观类型 */
|
|
261
|
+
textureType: 'dot' | 'arrow' | 'wave';
|
|
262
|
+
/** 拖尾效果 */
|
|
263
|
+
trail: boolean;
|
|
264
|
+
/** 发光效果 */
|
|
265
|
+
glow: boolean;
|
|
266
|
+
/** 管道透明度(0=完全透明 1=不透明,1 表示不隐藏管道) */
|
|
267
|
+
pipeOpacity: number;
|
|
268
|
+
/** 粒子分布半径(管道截面内随机偏移范围) */
|
|
269
|
+
pipeRadius?: number;
|
|
270
|
+
}
|
|
271
|
+
/** 屏幕内容切换效果(大屏贴图轮播) */
|
|
272
|
+
interface ScreenContentEffect extends VisualEffectBase {
|
|
273
|
+
kind: 'screen-content';
|
|
274
|
+
/** 图片 URL 数组(base64 或 http) */
|
|
275
|
+
images: string[];
|
|
276
|
+
/** 轮播间隔(毫秒) */
|
|
277
|
+
interval: number;
|
|
278
|
+
/** 是否自动播放 */
|
|
279
|
+
autoPlay: boolean;
|
|
280
|
+
/** 目标网格名称(为空则应用到所有网格) */
|
|
281
|
+
targetMeshName?: string;
|
|
282
|
+
/** 当前图片索引(运行时使用,不持久化) */
|
|
283
|
+
currentIndex?: number;
|
|
284
|
+
}
|
|
285
|
+
type VisualEffect = UvAnimationEffect | ParticleEffect | PathParticleEffect | ScreenContentEffect;
|
|
286
|
+
/** 飞行路线关键帧 */
|
|
287
|
+
interface CameraKeyframe {
|
|
288
|
+
/** 相机位置 */
|
|
289
|
+
position: Vec3;
|
|
290
|
+
/** 相机目标点 */
|
|
291
|
+
target: Vec3;
|
|
292
|
+
/** 到达此关键帧的时间(秒,相对于路径起点) */
|
|
293
|
+
time: number;
|
|
294
|
+
}
|
|
295
|
+
/** 飞行路线 */
|
|
296
|
+
interface FlyPath {
|
|
297
|
+
id: string;
|
|
298
|
+
name: string;
|
|
299
|
+
keyframes: CameraKeyframe[];
|
|
300
|
+
/** 是否循环播放 */
|
|
301
|
+
loop: boolean;
|
|
302
|
+
/** 总时长(秒,若未指定则从关键帧计算) */
|
|
303
|
+
duration?: number;
|
|
304
|
+
/** 关键帧默认间隔(秒),默认 2 */
|
|
305
|
+
interval?: number;
|
|
306
|
+
}
|
|
307
|
+
interface CameraDoc {
|
|
308
|
+
position: Vec3;
|
|
309
|
+
target: Vec3;
|
|
310
|
+
}
|
|
311
|
+
/** 环境配置(场景级) */
|
|
312
|
+
interface EnvironmentConfig {
|
|
313
|
+
/** 背景色 */
|
|
314
|
+
background?: string;
|
|
315
|
+
/** 半球光 */
|
|
316
|
+
hemisphere?: {
|
|
317
|
+
/** 是否启用(默认 true) */
|
|
318
|
+
enabled?: boolean;
|
|
319
|
+
skyColor?: string;
|
|
320
|
+
groundColor?: string;
|
|
321
|
+
intensity?: number;
|
|
322
|
+
};
|
|
323
|
+
/** 默认平行光 */
|
|
324
|
+
directional?: {
|
|
325
|
+
/** 是否启用(默认 true) */
|
|
326
|
+
enabled?: boolean;
|
|
327
|
+
color?: string;
|
|
328
|
+
intensity?: number;
|
|
329
|
+
position?: Vec3;
|
|
330
|
+
};
|
|
331
|
+
/** HDRI 环境贴图(base64 data URL),优先于 background 纯色背景 */
|
|
332
|
+
hdriDataUrl?: string;
|
|
333
|
+
/** 阴影配置 */
|
|
334
|
+
shadow?: {
|
|
335
|
+
/** 是否启用阴影(默认 true) */
|
|
336
|
+
enabled?: boolean;
|
|
337
|
+
/** 阴影贴图分辨率(默认 2048) */
|
|
338
|
+
mapSize?: number;
|
|
339
|
+
/** 阴影相机范围(默认 15,对应 -range ~ +range) */
|
|
340
|
+
cameraRange?: number;
|
|
341
|
+
};
|
|
342
|
+
/** 场景空间配置 */
|
|
343
|
+
room?: {
|
|
344
|
+
/** 是否启用 */
|
|
345
|
+
enabled?: boolean;
|
|
346
|
+
/** 宽度(X 轴),默认 30 */
|
|
347
|
+
width?: number;
|
|
348
|
+
/** 深度(Z 轴),默认 30 */
|
|
349
|
+
depth?: number;
|
|
350
|
+
/** 高度(Y 轴),默认 10 */
|
|
351
|
+
height?: number;
|
|
352
|
+
/** 地板颜色 */
|
|
353
|
+
floorColor?: string;
|
|
354
|
+
/** 墙壁颜色 */
|
|
355
|
+
wallColor?: string;
|
|
356
|
+
/** 天花板颜色 */
|
|
357
|
+
ceilingColor?: string;
|
|
358
|
+
/** 地板漫反射贴图(base64 data URL) */
|
|
359
|
+
floorTexture?: string;
|
|
360
|
+
/** 地板法线贴图 */
|
|
361
|
+
floorNormalTexture?: string;
|
|
362
|
+
/** 地板粗糙度贴图 */
|
|
363
|
+
floorRoughnessTexture?: string;
|
|
364
|
+
/** 墙壁漫反射贴图 */
|
|
365
|
+
wallTexture?: string;
|
|
366
|
+
/** 墙壁法线贴图 */
|
|
367
|
+
wallNormalTexture?: string;
|
|
368
|
+
/** 墙壁粗糙度贴图 */
|
|
369
|
+
wallRoughnessTexture?: string;
|
|
370
|
+
/** 天花板漫反射贴图 */
|
|
371
|
+
ceilingTexture?: string;
|
|
372
|
+
/** 天花板法线贴图 */
|
|
373
|
+
ceilingNormalTexture?: string;
|
|
374
|
+
/** 天花板粗糙度贴图 */
|
|
375
|
+
ceilingRoughnessTexture?: string;
|
|
376
|
+
/** 基础色是否与贴图叠加(默认 true;关闭后基础色强制 #ffffff,贴图原样显示) */
|
|
377
|
+
floorUseBaseColor?: boolean;
|
|
378
|
+
wallUseBaseColor?: boolean;
|
|
379
|
+
ceilingUseBaseColor?: boolean;
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
interface SceneDoc {
|
|
383
|
+
schemaVersion: 3;
|
|
384
|
+
/** 场景 ID(服务器分配) */
|
|
385
|
+
id?: string;
|
|
386
|
+
/** 场景名称 */
|
|
387
|
+
name?: string;
|
|
388
|
+
nodes: NodeDoc[];
|
|
389
|
+
/** 相机位置 */
|
|
390
|
+
camera?: CameraDoc;
|
|
391
|
+
/** 环境配置 */
|
|
392
|
+
environment?: EnvironmentConfig;
|
|
393
|
+
/** 飞行路线 */
|
|
394
|
+
flyPaths?: FlyPath[];
|
|
395
|
+
/** 大屏看板 */
|
|
396
|
+
dashboard?: DashboardConfig;
|
|
397
|
+
/** 告警配置 */
|
|
398
|
+
alarm?: AlarmConfig;
|
|
399
|
+
}
|
|
400
|
+
/** 场景元数据(列表用,不包含完整 SceneDoc) */
|
|
401
|
+
interface SceneMeta {
|
|
402
|
+
id: string;
|
|
403
|
+
name: string;
|
|
404
|
+
createdAt: number;
|
|
405
|
+
updatedAt: number;
|
|
406
|
+
}
|
|
407
|
+
declare function nextId(prefix?: string): string;
|
|
408
|
+
declare function createNode(primitive: PrimitiveKind, name: string): NodeDoc;
|
|
409
|
+
declare function createSceneDoc(): SceneDoc;
|
|
410
|
+
declare function clone<T>(value: T): T;
|
|
411
|
+
/** 告警弹窗数据 */
|
|
412
|
+
interface AlarmPopupData {
|
|
413
|
+
deviceName: string;
|
|
414
|
+
alarmType: string;
|
|
415
|
+
currentValue: number;
|
|
416
|
+
threshold: number;
|
|
417
|
+
priority: 'high' | 'medium' | 'low';
|
|
418
|
+
suggestion: string;
|
|
419
|
+
timestamp: number;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/** 命令:一切场景修改的唯一入口,天然可撤销 */
|
|
423
|
+
interface Command {
|
|
424
|
+
label: string;
|
|
425
|
+
do(store: SceneStore): void;
|
|
426
|
+
undo(store: SceneStore): void;
|
|
427
|
+
}
|
|
428
|
+
declare class History {
|
|
429
|
+
private readonly store;
|
|
430
|
+
private undoStack;
|
|
431
|
+
private redoStack;
|
|
432
|
+
constructor(store: SceneStore);
|
|
433
|
+
execute(cmd: Command): void;
|
|
434
|
+
undo(): void;
|
|
435
|
+
redo(): void;
|
|
436
|
+
get canUndo(): boolean;
|
|
437
|
+
get canRedo(): boolean;
|
|
438
|
+
/** 清空所有历史记录 */
|
|
439
|
+
clear(): void;
|
|
440
|
+
/** 检查撤销栈中是否有指定节点/mesh 的材质命令 */
|
|
441
|
+
hasMaterialCmdFor(nodeId: string, meshName: string): boolean;
|
|
442
|
+
/** 更新栈顶最后一个指定节点/mesh 的材质命令的 after 状态(用于贴图上传完成后替换临时 URL)
|
|
443
|
+
* 返回 true 表示找到并更新了,false 表示栈中没有匹配的命令 */
|
|
444
|
+
updateLastMaterialAfter(nodeId: string, meshName: string, newAfter: MaterialOverride): boolean;
|
|
445
|
+
/** 直接推入命令(用于已手动应用变更的场景,不再执行 do) */
|
|
446
|
+
pushCommand(cmd: Command): void;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
type Listener = () => void;
|
|
450
|
+
/** 单一数据源:场景文档 + 选择 + 历史。不依赖 Angular / three */
|
|
451
|
+
declare class SceneStore {
|
|
452
|
+
readonly doc: SceneDoc;
|
|
453
|
+
readonly history: History;
|
|
454
|
+
private docListeners;
|
|
455
|
+
private selectionListeners;
|
|
456
|
+
private _selection;
|
|
457
|
+
get selection(): readonly string[];
|
|
458
|
+
node(id: string): NodeDoc | undefined;
|
|
459
|
+
parentOf(id: string): NodeDoc | undefined;
|
|
460
|
+
/** 收集节点自身及全部后代 */
|
|
461
|
+
subtree(id: string): NodeDoc[];
|
|
462
|
+
roots(): NodeDoc[];
|
|
463
|
+
setSelection(ids: string[]): void;
|
|
464
|
+
onDocChange(l: Listener): () => void;
|
|
465
|
+
onSelectionChange(l: Listener): () => void;
|
|
466
|
+
/** History 与直接修改(如选择无关的文档变更)后调用 */
|
|
467
|
+
notifyChanged(): void;
|
|
468
|
+
/** 从 JSON 加载场景文档(替换当前文档,兼容 v2 和 v3) */
|
|
469
|
+
loadDoc(doc: SceneDoc): void;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
declare class AddNodeCommand implements Command {
|
|
473
|
+
private readonly node;
|
|
474
|
+
private readonly parentId;
|
|
475
|
+
readonly label: string;
|
|
476
|
+
constructor(node: NodeDoc, parentId?: string | null);
|
|
477
|
+
do(store: SceneStore): void;
|
|
478
|
+
undo(store: SceneStore): void;
|
|
479
|
+
}
|
|
480
|
+
declare class RemoveNodeCommand implements Command {
|
|
481
|
+
private readonly id;
|
|
482
|
+
readonly label = "\u5220\u9664\u8282\u70B9";
|
|
483
|
+
private removed;
|
|
484
|
+
private parentId;
|
|
485
|
+
constructor(id: string);
|
|
486
|
+
do(store: SceneStore): void;
|
|
487
|
+
undo(store: SceneStore): void;
|
|
488
|
+
}
|
|
489
|
+
declare class TransformCommand implements Command {
|
|
490
|
+
private readonly id;
|
|
491
|
+
private readonly before;
|
|
492
|
+
private readonly after;
|
|
493
|
+
readonly label = "\u53D8\u6362";
|
|
494
|
+
constructor(id: string, before: TransformState, after: TransformState);
|
|
495
|
+
do(store: SceneStore): void;
|
|
496
|
+
undo(store: SceneStore): void;
|
|
497
|
+
}
|
|
498
|
+
/** 合并选中节点为一个 group */
|
|
499
|
+
declare class GroupCommand implements Command {
|
|
500
|
+
private readonly selectedIds;
|
|
501
|
+
readonly label = "\u5408\u5E76\u4E3A\u7EC4";
|
|
502
|
+
private groupId;
|
|
503
|
+
private originalParents;
|
|
504
|
+
private originalPositions;
|
|
505
|
+
private removedEmptyGroups;
|
|
506
|
+
constructor(selectedIds: string[]);
|
|
507
|
+
do(store: SceneStore): void;
|
|
508
|
+
undo(store: SceneStore): void;
|
|
509
|
+
}
|
|
510
|
+
/** 解除合并:将 group 的子节点提升到 group 的父节点下,删除 group */
|
|
511
|
+
declare class UngroupCommand implements Command {
|
|
512
|
+
readonly label = "\u89E3\u9664\u5408\u5E76";
|
|
513
|
+
private groupId;
|
|
514
|
+
private children;
|
|
515
|
+
private groupParentId;
|
|
516
|
+
private groupName;
|
|
517
|
+
constructor(groupId: string);
|
|
518
|
+
do(store: SceneStore): void;
|
|
519
|
+
undo(store: SceneStore): void;
|
|
520
|
+
}
|
|
521
|
+
/** 材质覆盖命令:记录 before/after,支持 undo/redo */
|
|
522
|
+
declare class MaterialOverrideCommand implements Command {
|
|
523
|
+
private readonly nodeId;
|
|
524
|
+
private readonly meshName;
|
|
525
|
+
private readonly before;
|
|
526
|
+
private readonly after;
|
|
527
|
+
readonly label = "\u4FEE\u6539\u6750\u8D28";
|
|
528
|
+
constructor(nodeId: string, meshName: string, before: MaterialOverride | undefined, after: MaterialOverride);
|
|
529
|
+
do(store: SceneStore): void;
|
|
530
|
+
undo(store: SceneStore): void;
|
|
531
|
+
}
|
|
532
|
+
/** 同步材质到同资产实例命令(批量) */
|
|
533
|
+
declare class SyncMaterialCommand implements Command {
|
|
534
|
+
private readonly sourceNodeId;
|
|
535
|
+
private readonly targetNodeIds;
|
|
536
|
+
private readonly beforeMap;
|
|
537
|
+
private readonly after;
|
|
538
|
+
readonly label = "\u540C\u6B65\u6750\u8D28\u5230\u540C\u8D44\u4EA7\u5B9E\u4F8B";
|
|
539
|
+
constructor(sourceNodeId: string, targetNodeIds: string[], beforeMap: Map<string, Record<string, MaterialOverride> | undefined>, after: Record<string, MaterialOverride> | undefined);
|
|
540
|
+
do(store: SceneStore): void;
|
|
541
|
+
undo(store: SceneStore): void;
|
|
542
|
+
}
|
|
543
|
+
/** 看板配置命令 */
|
|
544
|
+
declare class SetDashboardCommand implements Command {
|
|
545
|
+
private readonly before;
|
|
546
|
+
private readonly after;
|
|
547
|
+
readonly label = "\u4FEE\u6539\u770B\u677F";
|
|
548
|
+
constructor(before: DashboardConfig | undefined, after: DashboardConfig);
|
|
549
|
+
do(store: SceneStore): void;
|
|
550
|
+
undo(store: SceneStore): void;
|
|
551
|
+
}
|
|
552
|
+
/** 告警配置命令 */
|
|
553
|
+
declare class SetAlarmCommand implements Command {
|
|
554
|
+
private readonly before;
|
|
555
|
+
private readonly after;
|
|
556
|
+
readonly label = "\u4FEE\u6539\u544A\u8B66";
|
|
557
|
+
constructor(before: AlarmConfig | undefined, after: AlarmConfig);
|
|
558
|
+
do(store: SceneStore): void;
|
|
559
|
+
undo(store: SceneStore): void;
|
|
560
|
+
}
|
|
561
|
+
/** 拖动节点改变父节点 */
|
|
562
|
+
declare class ReparentCommand implements Command {
|
|
563
|
+
private readonly nodeId;
|
|
564
|
+
private readonly newParentId;
|
|
565
|
+
readonly label = "\u79FB\u52A8\u8282\u70B9";
|
|
566
|
+
private oldParentId;
|
|
567
|
+
private oldIndex;
|
|
568
|
+
constructor(nodeId: string, newParentId: string | null);
|
|
569
|
+
do(store: SceneStore): void;
|
|
570
|
+
undo(store: SceneStore): void;
|
|
571
|
+
}
|
|
572
|
+
/** 环境配置命令:背景色、半球光、平行光、HDRI、场景空间 */
|
|
573
|
+
declare class SetEnvironmentCommand implements Command {
|
|
574
|
+
private readonly before;
|
|
575
|
+
private readonly after;
|
|
576
|
+
readonly label = "\u4FEE\u6539\u73AF\u5883";
|
|
577
|
+
constructor(before: EnvironmentConfig | undefined, after: EnvironmentConfig);
|
|
578
|
+
do(store: SceneStore): void;
|
|
579
|
+
undo(store: SceneStore): void;
|
|
580
|
+
}
|
|
581
|
+
/** 灯光属性修改命令 */
|
|
582
|
+
declare class SetLightPropertyCommand implements Command {
|
|
583
|
+
private readonly nodeId;
|
|
584
|
+
private readonly prop;
|
|
585
|
+
private readonly newValue;
|
|
586
|
+
readonly label = "\u4FEE\u6539\u706F\u5149";
|
|
587
|
+
private oldValue;
|
|
588
|
+
constructor(nodeId: string, prop: string, newValue: unknown);
|
|
589
|
+
do(store: SceneStore): void;
|
|
590
|
+
undo(store: SceneStore): void;
|
|
591
|
+
/** 执行前保存旧值(必须在 do 之前调用) */
|
|
592
|
+
captureOldValue(store: SceneStore): void;
|
|
593
|
+
}
|
|
594
|
+
/** 节点重命名命令 */
|
|
595
|
+
declare class SetNodeNameCommand implements Command {
|
|
596
|
+
private readonly nodeId;
|
|
597
|
+
private readonly newName;
|
|
598
|
+
readonly label = "\u91CD\u547D\u540D";
|
|
599
|
+
private oldName;
|
|
600
|
+
constructor(nodeId: string, newName: string);
|
|
601
|
+
do(store: SceneStore): void;
|
|
602
|
+
undo(store: SceneStore): void;
|
|
603
|
+
}
|
|
604
|
+
declare class SetNodeTextCommand implements Command {
|
|
605
|
+
private readonly nodeId;
|
|
606
|
+
private readonly newText;
|
|
607
|
+
readonly label = "\u6807\u7B7E\u6587\u5B57";
|
|
608
|
+
private oldText;
|
|
609
|
+
constructor(nodeId: string, newText: string);
|
|
610
|
+
do(store: SceneStore): void;
|
|
611
|
+
undo(store: SceneStore): void;
|
|
612
|
+
}
|
|
613
|
+
/** 批量删除命令(多选删除合并为单个撤销步骤) */
|
|
614
|
+
declare class BatchRemoveCommand implements Command {
|
|
615
|
+
private readonly ids;
|
|
616
|
+
readonly label = "\u5220\u9664\u8282\u70B9";
|
|
617
|
+
private removedGroups;
|
|
618
|
+
constructor(ids: string[]);
|
|
619
|
+
do(store: SceneStore): void;
|
|
620
|
+
undo(store: SceneStore): void;
|
|
621
|
+
}
|
|
622
|
+
/** 动画添加命令 */
|
|
623
|
+
declare class AddAnimationCommand implements Command {
|
|
624
|
+
private readonly nodeId;
|
|
625
|
+
private readonly rule;
|
|
626
|
+
readonly label = "\u6DFB\u52A0\u52A8\u753B";
|
|
627
|
+
constructor(nodeId: string, rule: AnimationRule);
|
|
628
|
+
do(store: SceneStore): void;
|
|
629
|
+
undo(store: SceneStore): void;
|
|
630
|
+
}
|
|
631
|
+
/** 动画更新命令 */
|
|
632
|
+
declare class UpdateAnimationCommand implements Command {
|
|
633
|
+
private readonly nodeId;
|
|
634
|
+
private readonly index;
|
|
635
|
+
private readonly newRule;
|
|
636
|
+
readonly label = "\u4FEE\u6539\u52A8\u753B";
|
|
637
|
+
private oldRule;
|
|
638
|
+
constructor(nodeId: string, index: number, newRule: AnimationRule);
|
|
639
|
+
do(store: SceneStore): void;
|
|
640
|
+
undo(store: SceneStore): void;
|
|
641
|
+
}
|
|
642
|
+
/** 动画删除命令 */
|
|
643
|
+
declare class RemoveAnimationCommand implements Command {
|
|
644
|
+
private readonly nodeId;
|
|
645
|
+
private readonly index;
|
|
646
|
+
readonly label = "\u5220\u9664\u52A8\u753B";
|
|
647
|
+
private oldRule;
|
|
648
|
+
private oldIndex;
|
|
649
|
+
constructor(nodeId: string, index: number);
|
|
650
|
+
do(store: SceneStore): void;
|
|
651
|
+
undo(store: SceneStore): void;
|
|
652
|
+
}
|
|
653
|
+
/** 飞行路线操作命令(创建/更新/删除合并为一个命令类) */
|
|
654
|
+
declare class FlyPathCommand implements Command {
|
|
655
|
+
private readonly action;
|
|
656
|
+
private readonly pathId;
|
|
657
|
+
private readonly pathData?;
|
|
658
|
+
private readonly updates?;
|
|
659
|
+
readonly label: string;
|
|
660
|
+
private beforePaths;
|
|
661
|
+
constructor(action: 'create' | 'update' | 'delete', pathId: string, pathData?: FlyPath | undefined, updates?: Partial<FlyPath> | undefined);
|
|
662
|
+
do(store: SceneStore): void;
|
|
663
|
+
undo(store: SceneStore): void;
|
|
664
|
+
}
|
|
665
|
+
/** 关键帧操作命令(添加/更新/删除/插入/覆盖合并为一个命令类) */
|
|
666
|
+
declare class KeyframeCommand implements Command {
|
|
667
|
+
private readonly action;
|
|
668
|
+
private readonly pathId;
|
|
669
|
+
private readonly index?;
|
|
670
|
+
private readonly keyframe?;
|
|
671
|
+
readonly label: string;
|
|
672
|
+
private beforePaths;
|
|
673
|
+
constructor(action: 'add' | 'update' | 'delete' | 'insert' | 'overwrite', pathId: string, index?: number | undefined, keyframe?: CameraKeyframe | undefined);
|
|
674
|
+
do(store: SceneStore): void;
|
|
675
|
+
undo(store: SceneStore): void;
|
|
676
|
+
}
|
|
677
|
+
/** 添加视觉效果 */
|
|
678
|
+
declare class AddEffectCommand implements Command {
|
|
679
|
+
private readonly nodeId;
|
|
680
|
+
private readonly effect;
|
|
681
|
+
readonly label = "\u6DFB\u52A0\u7279\u6548";
|
|
682
|
+
constructor(nodeId: string, effect: VisualEffect);
|
|
683
|
+
do(store: SceneStore): void;
|
|
684
|
+
undo(store: SceneStore): void;
|
|
685
|
+
}
|
|
686
|
+
/** 更新视觉效果 */
|
|
687
|
+
declare class UpdateEffectCommand implements Command {
|
|
688
|
+
private readonly nodeId;
|
|
689
|
+
private readonly index;
|
|
690
|
+
private readonly newEffect;
|
|
691
|
+
readonly label = "\u4FEE\u6539\u7279\u6548";
|
|
692
|
+
private oldEffect;
|
|
693
|
+
constructor(nodeId: string, index: number, newEffect: VisualEffect);
|
|
694
|
+
do(store: SceneStore): void;
|
|
695
|
+
undo(store: SceneStore): void;
|
|
696
|
+
}
|
|
697
|
+
/** 删除视觉效果 */
|
|
698
|
+
declare class RemoveEffectCommand implements Command {
|
|
699
|
+
private readonly nodeId;
|
|
700
|
+
private readonly index;
|
|
701
|
+
readonly label = "\u5220\u9664\u7279\u6548";
|
|
702
|
+
private oldEffect;
|
|
703
|
+
private oldIndex;
|
|
704
|
+
constructor(nodeId: string, index: number);
|
|
705
|
+
do(store: SceneStore): void;
|
|
706
|
+
undo(store: SceneStore): void;
|
|
707
|
+
}
|
|
708
|
+
/** 更新路径点位置 */
|
|
709
|
+
declare class UpdatePathPointsCommand implements Command {
|
|
710
|
+
private readonly nodeId;
|
|
711
|
+
private readonly effectIndex;
|
|
712
|
+
private readonly newPoints;
|
|
713
|
+
readonly label = "\u4FEE\u6539\u8DEF\u5F84\u70B9";
|
|
714
|
+
private oldPoints;
|
|
715
|
+
constructor(nodeId: string, effectIndex: number, newPoints: Vec3[]);
|
|
716
|
+
do(store: SceneStore): void;
|
|
717
|
+
undo(store: SceneStore): void;
|
|
718
|
+
}
|
|
719
|
+
declare class InsertPathPointCommand implements Command {
|
|
720
|
+
private readonly nodeId;
|
|
721
|
+
private readonly effectIndex;
|
|
722
|
+
private readonly insertAfter;
|
|
723
|
+
private readonly position;
|
|
724
|
+
readonly label = "\u6DFB\u52A0\u8DEF\u5F84\u70B9";
|
|
725
|
+
private oldPoints;
|
|
726
|
+
private oldSegmentTypes;
|
|
727
|
+
constructor(nodeId: string, effectIndex: number, insertAfter: number, position: Vec3);
|
|
728
|
+
do(store: SceneStore): void;
|
|
729
|
+
undo(store: SceneStore): void;
|
|
730
|
+
}
|
|
731
|
+
/** 删除路径点(最少保留 2 个) */
|
|
732
|
+
declare class RemovePathPointCommand implements Command {
|
|
733
|
+
private readonly nodeId;
|
|
734
|
+
private readonly effectIndex;
|
|
735
|
+
private readonly pointIndex;
|
|
736
|
+
readonly label = "\u5220\u9664\u8DEF\u5F84\u70B9";
|
|
737
|
+
private oldPoints;
|
|
738
|
+
private oldSegmentTypes;
|
|
739
|
+
constructor(nodeId: string, effectIndex: number, pointIndex: number);
|
|
740
|
+
do(store: SceneStore): void;
|
|
741
|
+
undo(store: SceneStore): void;
|
|
742
|
+
}
|
|
743
|
+
/** 切换段类型(直线/曲线) */
|
|
744
|
+
declare class ToggleSegmentTypeCommand implements Command {
|
|
745
|
+
private readonly nodeId;
|
|
746
|
+
private readonly effectIndex;
|
|
747
|
+
private readonly segmentIndex;
|
|
748
|
+
readonly label = "\u5207\u6362\u6BB5\u7C7B\u578B";
|
|
749
|
+
private oldType;
|
|
750
|
+
constructor(nodeId: string, effectIndex: number, segmentIndex: number);
|
|
751
|
+
do(store: SceneStore): void;
|
|
752
|
+
undo(store: SceneStore): void;
|
|
753
|
+
}
|
|
754
|
+
/** 移动控制点到数组开头或结尾 */
|
|
755
|
+
declare class MovePathPointToEndCommand implements Command {
|
|
756
|
+
private readonly nodeId;
|
|
757
|
+
private readonly effectIndex;
|
|
758
|
+
private readonly pointIndex;
|
|
759
|
+
private readonly toStart;
|
|
760
|
+
readonly label = "\u79FB\u52A8\u63A7\u5236\u70B9";
|
|
761
|
+
private oldPoints;
|
|
762
|
+
private oldSegmentTypes;
|
|
763
|
+
constructor(nodeId: string, effectIndex: number, pointIndex: number, toStart: boolean);
|
|
764
|
+
do(store: SceneStore): void;
|
|
765
|
+
undo(store: SceneStore): void;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
type TransformMode = 'translate' | 'rotate' | 'scale';
|
|
769
|
+
interface PickEvent {
|
|
770
|
+
id: string | null;
|
|
771
|
+
/** 是否按住 Ctrl/Cmd 追加(true = 切换;false = 排他选中) */
|
|
772
|
+
additive: boolean;
|
|
773
|
+
}
|
|
774
|
+
interface ViewerCallbacks {
|
|
775
|
+
onSelect(ev: PickEvent): void;
|
|
776
|
+
onTransformEnd(id: string, before: TransformState, after: TransformState): void;
|
|
777
|
+
/** 资产加载完成后生成缩略图 */
|
|
778
|
+
onThumbnailGenerated?(assetId: string, dataUrl: string): void;
|
|
779
|
+
/** 路径点移动 */
|
|
780
|
+
onPathPointMoved?(nodeId: string, effectIndex: number, pointIndex: number, newPosition: Vec3): void;
|
|
781
|
+
/** 添加路径点 */
|
|
782
|
+
onAddPathPoint?(nodeId: string, effectIndex: number, insertAfter: number, position: Vec3): void;
|
|
783
|
+
/** 删除路径点 */
|
|
784
|
+
onDeletePathPoint?(nodeId: string, effectIndex: number, pointIndex: number): void;
|
|
785
|
+
/** 路径编辑模式变化 */
|
|
786
|
+
onPathEditingChanged?(isEditing: boolean): void;
|
|
787
|
+
/** 控制点右键点击 */
|
|
788
|
+
onPathPointRightClick?(nodeId: string, effectIndex: number, pointIndex: number, position: Vec3, event: MouseEvent): void;
|
|
789
|
+
/** 线段右键点击 */
|
|
790
|
+
onSegmentRightClick?(nodeId: string, effectIndex: number, segmentIndex: number, position: Vec3, event: MouseEvent): void;
|
|
791
|
+
/** 模型表面点击(左键) */
|
|
792
|
+
onModelSurfaceClick?(nodeId: string, effectIndex: number, position: Vec3, event: PointerEvent): void;
|
|
793
|
+
/** 移动控制点到数组开头/结尾 */
|
|
794
|
+
onMovePathPointToEnd?(nodeId: string, effectIndex: number, pointIndex: number, toStart: boolean): void;
|
|
795
|
+
}
|
|
796
|
+
/** three.js 视口:只认 SceneStore 这份单一数据源,sync() 是唯一写 three 对象的入口 */
|
|
797
|
+
declare class SceneViewer {
|
|
798
|
+
private host;
|
|
799
|
+
private store;
|
|
800
|
+
private cb;
|
|
801
|
+
private renderer;
|
|
802
|
+
/** 后处理合成器 */
|
|
803
|
+
private composer;
|
|
804
|
+
private scene;
|
|
805
|
+
private camera;
|
|
806
|
+
private orbit;
|
|
807
|
+
private gizmo;
|
|
808
|
+
private hemiLight;
|
|
809
|
+
private dirLight;
|
|
810
|
+
private objects;
|
|
811
|
+
private helpers;
|
|
812
|
+
private lightTargets;
|
|
813
|
+
private lightHelpers;
|
|
814
|
+
private lightPickers;
|
|
815
|
+
private assetCache;
|
|
816
|
+
/** 正在等待加载的节点(URL → 等待中的 nodeId 集合),处理 undo→redo 竞态 */
|
|
817
|
+
private pendingAssetNodes;
|
|
818
|
+
/** 动画基础变换(nodeId → 数据模型原始变换) */
|
|
819
|
+
private animBases;
|
|
820
|
+
/** 缩略图渲染器(离屏) */
|
|
821
|
+
private thumbRenderer?;
|
|
822
|
+
private thumbScene;
|
|
823
|
+
private thumbCamera;
|
|
824
|
+
/** 动画时间 */
|
|
825
|
+
private animTime;
|
|
826
|
+
/** 上一帧时间戳 */
|
|
827
|
+
private lastFrameTime;
|
|
828
|
+
/** 拖拽中冻结动画 */
|
|
829
|
+
private animationFrozen;
|
|
830
|
+
/** 相机是否已从文档恢复(只在首次 sync 时恢复一次) */
|
|
831
|
+
private cameraRestored;
|
|
832
|
+
/** 保存原始材质参数(覆盖前),用于撤销覆盖时恢复 */
|
|
833
|
+
private originalMaterials;
|
|
834
|
+
/** undo/redo 期间为 true,sync() 中据此传 isUndoRedoing 给 applyMaterialOverrides */
|
|
835
|
+
private _isUndoRedoing;
|
|
836
|
+
/** 贴图缓存(dataURL → THREE.Texture) */
|
|
837
|
+
private textureCache;
|
|
838
|
+
/** 标签对象缓存(nodeId → label mesh) */
|
|
839
|
+
private labelObjects;
|
|
840
|
+
private textureLoader;
|
|
841
|
+
/** HDRI 环境贴图缓存 */
|
|
842
|
+
private hdriTexture;
|
|
843
|
+
/** 当前 HDRI data URL(用于判断是否需要重新加载) */
|
|
844
|
+
private hdriCurrentUrl;
|
|
845
|
+
/** 网格辅助线 */
|
|
846
|
+
private gridHelper;
|
|
847
|
+
/** 场景空间网格 */
|
|
848
|
+
private roomMesh;
|
|
849
|
+
/** 缓存上次的房间配置,避免每次 state 变化都重建 */
|
|
850
|
+
private lastRoomKey;
|
|
851
|
+
private gltfLoader;
|
|
852
|
+
/** 正在加载中的资产 URL(防止重复加载) */
|
|
853
|
+
private loadingAssetUrls;
|
|
854
|
+
private dragBefore;
|
|
855
|
+
private downPos;
|
|
856
|
+
private ro;
|
|
857
|
+
private raycaster;
|
|
858
|
+
private unsubDoc;
|
|
859
|
+
private unsubSel;
|
|
860
|
+
/** 看板 overlay 元素 */
|
|
861
|
+
private dashboardOverlayEl?;
|
|
862
|
+
/** 看板 widget DOM 元素缓存 (widgetId → HTMLDivElement) */
|
|
863
|
+
private dashboardElements;
|
|
864
|
+
/** 看板数据源定时器 */
|
|
865
|
+
private dashboardIntervals;
|
|
866
|
+
/** 看板当前数据值 (widgetId → value) */
|
|
867
|
+
private dashboardValues;
|
|
868
|
+
/** 当前播放的飞行路线 */
|
|
869
|
+
private flyPathPlaying;
|
|
870
|
+
/** 播放开始的真实时间戳 */
|
|
871
|
+
private flyPathStartTime;
|
|
872
|
+
/** 告警弹窗 (nodeId → mesh) */
|
|
873
|
+
private alarmPopups;
|
|
874
|
+
/** 告警弹窗创建时间 (nodeId → timestamp) */
|
|
875
|
+
private alarmCreationTime;
|
|
876
|
+
/** 暂停时已经过的时间(秒) */
|
|
877
|
+
private flyPathPausedElapsed;
|
|
878
|
+
/** 是否暂停 */
|
|
879
|
+
private flyPathPaused;
|
|
880
|
+
/** 是否刚从暂停恢复(需要在下一帧重算 startTime) */
|
|
881
|
+
private flyPathResuming;
|
|
882
|
+
/** 飞行路线回调 */
|
|
883
|
+
private flyPathCallbacks;
|
|
884
|
+
private boxSelecting;
|
|
885
|
+
private boxStart;
|
|
886
|
+
private boxEl;
|
|
887
|
+
private keysPressed;
|
|
888
|
+
private moveSpeed;
|
|
889
|
+
/** 复用的临时向量,避免每帧 GC */
|
|
890
|
+
private _tmpVec;
|
|
891
|
+
private _tmpVec2;
|
|
892
|
+
private _tmpVec3;
|
|
893
|
+
private _upVector;
|
|
894
|
+
private onKeyDown;
|
|
895
|
+
private onKeyUp;
|
|
896
|
+
/** nodeId → effectId → THREE.Object3D(粒子系统等) */
|
|
897
|
+
private effectObjects;
|
|
898
|
+
/** `${nodeId}:${effectId}` → ParticleRuntime */
|
|
899
|
+
private particleSystems;
|
|
900
|
+
/** `${nodeId}:${effectId}` → PathParticleRuntime */
|
|
901
|
+
private pathParticleSystems;
|
|
902
|
+
/** UV 动画状态:`${nodeId}:${effectId}` → { offsetU, offsetV } */
|
|
903
|
+
private uvAnimStates;
|
|
904
|
+
/** 屏幕内容切换状态:`${nodeId}:${effectId}` → { intervalId, currentIndex, targetMeshName } */
|
|
905
|
+
private screenContentStates;
|
|
906
|
+
/** 管道水流效果导致的材质半透明:nodeId → [{ mesh, material, originalOpacity, originalDepthWrite }] */
|
|
907
|
+
private pipeTransparencyBackup;
|
|
908
|
+
private pathEditor;
|
|
909
|
+
/** 是否为编辑器模式(编辑器模式下动画默认播放,不依赖数据) */
|
|
910
|
+
private isEditorMode;
|
|
911
|
+
constructor(host: HTMLElement, store: SceneStore, cb: ViewerCallbacks, dashboardOverlayEl?: HTMLDivElement, isEditorMode?: boolean);
|
|
912
|
+
setMode(mode: TransformMode): void;
|
|
913
|
+
/** 设置路径编辑的活动效果 */
|
|
914
|
+
setPathEditingEffect(effectKey: string | null): void;
|
|
915
|
+
/** 是否正在编辑路径 */
|
|
916
|
+
isPathEditing(): boolean;
|
|
917
|
+
/** 设置路径编辑控制点大小 */
|
|
918
|
+
setPathHandleSize(size: number): void;
|
|
919
|
+
/** 将当前相机状态写入 SceneDoc(保存前调用) */
|
|
920
|
+
saveCamera(): void;
|
|
921
|
+
/** 从 SceneDoc 恢复相机位置 */
|
|
922
|
+
private restoreCamera;
|
|
923
|
+
/** 导出场景为 GLB 文件 */
|
|
924
|
+
exportScene(): void;
|
|
925
|
+
/** 按文档重建/更新/删除 three 对象 */
|
|
926
|
+
sync(): void;
|
|
927
|
+
/** 强制恢复相机(草稿恢复后调用) */
|
|
928
|
+
forceRestoreCamera(): void;
|
|
929
|
+
/** 创建标签 mesh */
|
|
930
|
+
private createLabel;
|
|
931
|
+
/** 应用场景环境配置(背景色、半球光、平行光、HDRI) */
|
|
932
|
+
private applyEnvironment;
|
|
933
|
+
/** 确保半球光存在并加入场景 */
|
|
934
|
+
private ensureHemiLight;
|
|
935
|
+
/** 移除半球光 */
|
|
936
|
+
private removeHemiLight;
|
|
937
|
+
/** 确保平行光存在并加入场景 */
|
|
938
|
+
private ensureDirLight;
|
|
939
|
+
/** 移除平行光 */
|
|
940
|
+
private removeDirLight;
|
|
941
|
+
/** 应用场景空间配置 */
|
|
942
|
+
private applyRoom;
|
|
943
|
+
/** 构建房间 6 面材质 */
|
|
944
|
+
private buildRoomMaterials;
|
|
945
|
+
/**
|
|
946
|
+
* 修正 BoxGeometry + BackSide 的 UV 镜像问题
|
|
947
|
+
* BackSide 从内部看盒子时,+X、+Z 面 U 反向,+Y 天花板 UV 全反,-Y 地板 V 反向
|
|
948
|
+
*/
|
|
949
|
+
private fixBoxBackSideUV;
|
|
950
|
+
/** 销毁房间网格和材质 */
|
|
951
|
+
private disposeRoom;
|
|
952
|
+
/** 处理键盘移动(WASD + Q/E)—— 仅当场景空间启用时生效 */
|
|
953
|
+
private processKeyboardMovement;
|
|
954
|
+
/** 场景空间开启时,限制 orbit 目标和相机在房间内部 */
|
|
955
|
+
private constrainCameraToRoom;
|
|
956
|
+
/** 清除 HDRI 环境贴图 */
|
|
957
|
+
private clearHdri;
|
|
958
|
+
/** 初始化后处理效果链:SSAO + Bloom + 色调映射输出 */
|
|
959
|
+
private initPostProcessing;
|
|
960
|
+
/** 播放飞行路线 */
|
|
961
|
+
playFlyPath(path: FlyPath, callbacks?: {
|
|
962
|
+
onComplete?: () => void;
|
|
963
|
+
}): void;
|
|
964
|
+
/** 停止飞行路线 */
|
|
965
|
+
stopFlyPath(): void;
|
|
966
|
+
/** 暂停飞行路线 */
|
|
967
|
+
pauseFlyPath(): void;
|
|
968
|
+
/** 恢复播放(从暂停处继续) */
|
|
969
|
+
resumeFlyPath(): void;
|
|
970
|
+
/** 是否正在播放飞行路线 */
|
|
971
|
+
isFlyPathPlaying(): boolean;
|
|
972
|
+
/** 更新飞行路线相机(在渲染循环中调用) */
|
|
973
|
+
private updateFlyPath;
|
|
974
|
+
/** 从关键帧设置相机 */
|
|
975
|
+
private setCameraFromKeyframe;
|
|
976
|
+
/** 保存节点的基础变换(数据模型值) */
|
|
977
|
+
private saveBaseTransforms;
|
|
978
|
+
/** 每帧更新所有动画 */
|
|
979
|
+
private updateAnimations;
|
|
980
|
+
/** 将动画值应用到对象的指定属性 */
|
|
981
|
+
private applyAnimValue;
|
|
982
|
+
/** 应用发射光动画规则 */
|
|
983
|
+
private applyEmissiveRule;
|
|
984
|
+
/** 多色梯度插值 */
|
|
985
|
+
private interpolateColorGradient;
|
|
986
|
+
/** 计算单条动画规则的当前值 */
|
|
987
|
+
private evaluateAnimation;
|
|
988
|
+
/** 全局模拟数据(0-100),可由外部设置 */
|
|
989
|
+
dataSignal: number;
|
|
990
|
+
/** 每节点实时数据:nodeId → { dataKey: value } */
|
|
991
|
+
private nodeDataMap;
|
|
992
|
+
/** 按 bindingId 存储实时数据:bindingId → value */
|
|
993
|
+
private bindingDataMap;
|
|
994
|
+
/** 设置模拟数据值(用于数据驱动动画测试) */
|
|
995
|
+
setDataSignal(value: number): void;
|
|
996
|
+
/** 设置节点实时数据(用于 per-node 数据驱动动画) */
|
|
997
|
+
setData(nodeId: string, data: Record<string, number>): void;
|
|
998
|
+
/** 获取节点实时数据值 */
|
|
999
|
+
getNodeData(nodeId: string, key: string): number | undefined;
|
|
1000
|
+
/** 按 bindingId 设置实时数据(用于点位绑定) */
|
|
1001
|
+
setBindingData(bindingId: string, value: number): void;
|
|
1002
|
+
/** 获取 bindingId 对应的数据值 */
|
|
1003
|
+
getBindingData(bindingId: string): number | undefined;
|
|
1004
|
+
/** 为资产生成缩略图(离屏渲染 512×384) */
|
|
1005
|
+
generateThumbnail(assetId: string, model: THREE.Object3D): void;
|
|
1006
|
+
/** 标记正在 undo/redo,sync() 中据此传 isUndoRedoing 给 applyMaterialOverrides */
|
|
1007
|
+
setUndoRedoing(value: boolean): void;
|
|
1008
|
+
/** 显示告警弹窗 */
|
|
1009
|
+
showAlarm(nodeId: string, data: AlarmPopupData): void;
|
|
1010
|
+
/** 隐藏告警弹窗 */
|
|
1011
|
+
hideAlarm(nodeId: string): void;
|
|
1012
|
+
/** 每帧更新告警弹窗(自动消失 + billboard 朝向) */
|
|
1013
|
+
private updateAlarmSprites;
|
|
1014
|
+
/** 同步看板 widget DOM 与数据模型 */
|
|
1015
|
+
private syncDashboard;
|
|
1016
|
+
/** 创建单个看板 widget */
|
|
1017
|
+
private createDashboardWidget;
|
|
1018
|
+
/** 应用 widget 样式 */
|
|
1019
|
+
private applyWidgetStyle;
|
|
1020
|
+
/** 更新 widget 内容 */
|
|
1021
|
+
private updateWidgetContent;
|
|
1022
|
+
/** 初始化 widget 数据源 */
|
|
1023
|
+
private initWidgetDataSource;
|
|
1024
|
+
/** 格式化显示值 */
|
|
1025
|
+
private formatValue;
|
|
1026
|
+
/** 获取状态灯颜色 */
|
|
1027
|
+
private getStatusColor;
|
|
1028
|
+
/** 计算进度百分比 */
|
|
1029
|
+
private calcProgress;
|
|
1030
|
+
/** 每帧更新看板 widget 位置(world 模式) */
|
|
1031
|
+
private updateDashboard;
|
|
1032
|
+
/** 同步视觉效果:创建/更新/销毁 effect 对应的 three.js 对象 */
|
|
1033
|
+
private syncEffects;
|
|
1034
|
+
/** 清理节点的所有效果 */
|
|
1035
|
+
private clearNodeEffects;
|
|
1036
|
+
/** 设置管道半透明(水流效果激活时调用) */
|
|
1037
|
+
private setPipeTransparency;
|
|
1038
|
+
/** 恢复管道原始不透明度 */
|
|
1039
|
+
private restorePipeTransparency;
|
|
1040
|
+
/** 确保目标 mesh 有流动贴图(程序化生成) */
|
|
1041
|
+
private ensureFlowTexture;
|
|
1042
|
+
/** 更新屏幕内容贴图 */
|
|
1043
|
+
private updateScreenContent;
|
|
1044
|
+
/** 每帧更新所有视觉效果 */
|
|
1045
|
+
private updateEffects;
|
|
1046
|
+
dispose(): void;
|
|
1047
|
+
private createMesh;
|
|
1048
|
+
/** 判断对象是否为灯光 */
|
|
1049
|
+
private isLight;
|
|
1050
|
+
/** 根据节点数据创建灯光对象 */
|
|
1051
|
+
private createLight;
|
|
1052
|
+
/** 更新灯光属性(不改变位置) */
|
|
1053
|
+
private updateLightProperties;
|
|
1054
|
+
/** 异步加载资产(从 URL,包括 blob URL) */
|
|
1055
|
+
private loadAsset;
|
|
1056
|
+
/** 创建红色占位线框 */
|
|
1057
|
+
private createPlaceholder;
|
|
1058
|
+
/** 创建带 Draco 解码器的 GLTFLoader */
|
|
1059
|
+
private createGltfLoader;
|
|
1060
|
+
/** 加载 GLTF/GLB */
|
|
1061
|
+
private loadGltf;
|
|
1062
|
+
/** 加载 OBJ + 可选 MTL */
|
|
1063
|
+
private loadObj;
|
|
1064
|
+
/** 解码文本,自动检测 UTF-8 或 GBK 编码(处理中文 mesh 名称乱码) */
|
|
1065
|
+
private decodeText;
|
|
1066
|
+
/** 将加载的资产模型应用到节点 */
|
|
1067
|
+
private applyAssetToNode;
|
|
1068
|
+
/** 确保所有 mesh 都有 name(供其他方法调用) */
|
|
1069
|
+
private ensureMeshNames;
|
|
1070
|
+
/** 获取资产节点的 mesh 名称列表 */
|
|
1071
|
+
getMeshNames(nodeId: string): string[];
|
|
1072
|
+
/** 获取节点的包围盒信息(世界坐标) */
|
|
1073
|
+
getNodeBounds(nodeId: string): {
|
|
1074
|
+
center: [number, number, number];
|
|
1075
|
+
size: [number, number, number];
|
|
1076
|
+
} | null;
|
|
1077
|
+
/** 应用材质覆盖到指定节点(含处理覆盖移除 — 恢复原始材质) */
|
|
1078
|
+
applyMaterialOverrides(nodeId: string, isUndoRedoing?: boolean): void;
|
|
1079
|
+
/** 加载或获取缓存的贴图,应用到材质 */
|
|
1080
|
+
private applyTexture;
|
|
1081
|
+
/** 保存 mesh 的原始材质(仅首次保存,后续调用不覆盖) */
|
|
1082
|
+
private saveOriginalMaterials;
|
|
1083
|
+
/** 隔离显示子部件:其他部件隐藏 */
|
|
1084
|
+
isolateSubMesh(nodeId: string, meshName: string): void;
|
|
1085
|
+
/** 退出隔离显示:恢复所有部件到正常状态 */
|
|
1086
|
+
exitIsolation(nodeId: string): void;
|
|
1087
|
+
/** 保存节点所有 mesh 的原始材质(模型加载后调用) */
|
|
1088
|
+
private saveAllOriginalMaterials;
|
|
1089
|
+
/** 恢复 mesh 到原始材质 */
|
|
1090
|
+
private resetMaterialToOriginal;
|
|
1091
|
+
/** 获取 mesh 的原始材质(未覆盖前的值) */
|
|
1092
|
+
getOriginalMaterial(nodeId: string, meshName: string): {
|
|
1093
|
+
color: string;
|
|
1094
|
+
opacity: number;
|
|
1095
|
+
roughness: number;
|
|
1096
|
+
metalness: number;
|
|
1097
|
+
emissive: string;
|
|
1098
|
+
visible: boolean;
|
|
1099
|
+
map: THREE.Texture | null;
|
|
1100
|
+
} | undefined;
|
|
1101
|
+
private refreshSelection;
|
|
1102
|
+
/** 根据 Box3 创建包围盒的几何体 */
|
|
1103
|
+
private createBoxGeometry;
|
|
1104
|
+
/** 计算 group 的所有后代的包围盒(本地空间,相对于组的 Object3D) */
|
|
1105
|
+
private computeGroupLocalBounds;
|
|
1106
|
+
/** 计算 group 的所有后代的包围盒(世界空间) */
|
|
1107
|
+
private computeGroupBounds;
|
|
1108
|
+
private snapshot;
|
|
1109
|
+
private pick;
|
|
1110
|
+
private createBoxEl;
|
|
1111
|
+
private updateBoxEl;
|
|
1112
|
+
private finishBoxSelect;
|
|
1113
|
+
/** 双击拉近视角:将相机平滑移动到目标对象前方 */
|
|
1114
|
+
private onDblClick;
|
|
1115
|
+
/** 平滑聚焦到指定节点:将相机移动到对象前方 */
|
|
1116
|
+
focusOnNode(nodeId: string): void;
|
|
1117
|
+
private resize;
|
|
1118
|
+
/** 屏幕坐标 → 地面(y=0)世界坐标 */
|
|
1119
|
+
screenToGround(viewportX: number, viewportY: number): Vec3 | null;
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
/**
|
|
1123
|
+
* 场景视口组件:three.js 全部跑在 NgZone 之外,
|
|
1124
|
+
* 只有对外事件才回到 zone 内触发变更检测。
|
|
1125
|
+
*/
|
|
1126
|
+
declare class SceneViewComponent implements AfterViewInit, OnChanges, OnDestroy {
|
|
1127
|
+
private zone;
|
|
1128
|
+
host: ElementRef<HTMLDivElement>;
|
|
1129
|
+
dashboardOverlay: ElementRef<HTMLDivElement>;
|
|
1130
|
+
store: SceneStore;
|
|
1131
|
+
mode: TransformMode;
|
|
1132
|
+
isEditorMode: boolean;
|
|
1133
|
+
select: EventEmitter<PickEvent>;
|
|
1134
|
+
transformEnd: EventEmitter<{
|
|
1135
|
+
id: string;
|
|
1136
|
+
before: TransformState;
|
|
1137
|
+
after: TransformState;
|
|
1138
|
+
}>;
|
|
1139
|
+
dropAsset: EventEmitter<{
|
|
1140
|
+
x: number;
|
|
1141
|
+
y: number;
|
|
1142
|
+
assetId: string;
|
|
1143
|
+
assetName: string;
|
|
1144
|
+
assetFormat: string;
|
|
1145
|
+
assetLibraryId: string;
|
|
1146
|
+
}>;
|
|
1147
|
+
thumbnailGenerated: EventEmitter<{
|
|
1148
|
+
assetId: string;
|
|
1149
|
+
dataUrl: string;
|
|
1150
|
+
}>;
|
|
1151
|
+
pathPointMoved: EventEmitter<{
|
|
1152
|
+
nodeId: string;
|
|
1153
|
+
effectIndex: number;
|
|
1154
|
+
pointIndex: number;
|
|
1155
|
+
position: Vec3;
|
|
1156
|
+
}>;
|
|
1157
|
+
addPathPoint: EventEmitter<{
|
|
1158
|
+
nodeId: string;
|
|
1159
|
+
effectIndex: number;
|
|
1160
|
+
insertAfter: number;
|
|
1161
|
+
position: Vec3;
|
|
1162
|
+
}>;
|
|
1163
|
+
deletePathPoint: EventEmitter<{
|
|
1164
|
+
nodeId: string;
|
|
1165
|
+
effectIndex: number;
|
|
1166
|
+
pointIndex: number;
|
|
1167
|
+
}>;
|
|
1168
|
+
pathEditingChanged: EventEmitter<boolean>;
|
|
1169
|
+
pathPointRightClick: EventEmitter<{
|
|
1170
|
+
nodeId: string;
|
|
1171
|
+
effectIndex: number;
|
|
1172
|
+
pointIndex: number;
|
|
1173
|
+
position: Vec3;
|
|
1174
|
+
event: MouseEvent;
|
|
1175
|
+
}>;
|
|
1176
|
+
segmentRightClick: EventEmitter<{
|
|
1177
|
+
nodeId: string;
|
|
1178
|
+
effectIndex: number;
|
|
1179
|
+
segmentIndex: number;
|
|
1180
|
+
position: Vec3;
|
|
1181
|
+
event: MouseEvent;
|
|
1182
|
+
}>;
|
|
1183
|
+
modelSurfaceClick: EventEmitter<{
|
|
1184
|
+
nodeId: string;
|
|
1185
|
+
effectIndex: number;
|
|
1186
|
+
position: Vec3;
|
|
1187
|
+
event: PointerEvent;
|
|
1188
|
+
}>;
|
|
1189
|
+
movePathPointToEnd: EventEmitter<{
|
|
1190
|
+
nodeId: string;
|
|
1191
|
+
effectIndex: number;
|
|
1192
|
+
pointIndex: number;
|
|
1193
|
+
toStart: boolean;
|
|
1194
|
+
}>;
|
|
1195
|
+
private viewer;
|
|
1196
|
+
constructor(zone: NgZone);
|
|
1197
|
+
ngAfterViewInit(): void;
|
|
1198
|
+
ngOnChanges(changes: {
|
|
1199
|
+
[key: string]: any;
|
|
1200
|
+
}): void;
|
|
1201
|
+
ngOnDestroy(): void;
|
|
1202
|
+
/** 导出场景为 GLB */
|
|
1203
|
+
exportScene(): void;
|
|
1204
|
+
/** 屏幕坐标 → 地面世界坐标 */
|
|
1205
|
+
screenToGround(screenX: number, screenY: number): [number, number, number] | null;
|
|
1206
|
+
/** 获取资产节点的 mesh 名称列表 */
|
|
1207
|
+
getMeshNames(nodeId: string): string[];
|
|
1208
|
+
/** 获取节点的包围盒信息(世界坐标) */
|
|
1209
|
+
getNodeBounds(nodeId: string): {
|
|
1210
|
+
center: [number, number, number];
|
|
1211
|
+
size: [number, number, number];
|
|
1212
|
+
} | null;
|
|
1213
|
+
/** 重新应用材质覆盖(材质参数变化后调用) */
|
|
1214
|
+
applyMaterialOverrides(nodeId: string): void;
|
|
1215
|
+
/** 获取 mesh 的原始材质(未覆盖前的值) */
|
|
1216
|
+
getOriginalMaterial(nodeId: string, meshName: string): {
|
|
1217
|
+
color: string;
|
|
1218
|
+
opacity: number;
|
|
1219
|
+
roughness: number;
|
|
1220
|
+
metalness: number;
|
|
1221
|
+
emissive: string;
|
|
1222
|
+
visible: boolean;
|
|
1223
|
+
} | undefined;
|
|
1224
|
+
/** 隔离显示子部件:其他部件变为半透明 */
|
|
1225
|
+
isolateSubMesh(nodeId: string, meshName: string): void;
|
|
1226
|
+
/** 退出隔离显示:恢复所有部件到原始状态 */
|
|
1227
|
+
exitIsolation(nodeId: string): void;
|
|
1228
|
+
/** 保存当前相机状态到 SceneDoc */
|
|
1229
|
+
saveCamera(): void;
|
|
1230
|
+
/** 强制恢复相机(草稿恢复后调用) */
|
|
1231
|
+
forceRestoreCamera(): void;
|
|
1232
|
+
/** 设置模拟数据信号(用于数据驱动动画测试) */
|
|
1233
|
+
setDataSignal(value: number): void;
|
|
1234
|
+
/** 设置节点实时数据(用于 per-node 数据驱动动画) */
|
|
1235
|
+
setData(nodeId: string, data: Record<string, number>): void;
|
|
1236
|
+
/** 按 bindingId 设置实时数据(用于点位绑定) */
|
|
1237
|
+
setBindingData(bindingId: string, value: number): void;
|
|
1238
|
+
/** 标记正在 undo/redo */
|
|
1239
|
+
setUndoRedoing(value: boolean): void;
|
|
1240
|
+
/** 播放飞行路线 */
|
|
1241
|
+
playFlyPath(path: FlyPath, callbacks?: {
|
|
1242
|
+
onComplete?: () => void;
|
|
1243
|
+
}): void;
|
|
1244
|
+
/** 停止飞行路线 */
|
|
1245
|
+
stopFlyPath(): void;
|
|
1246
|
+
/** 暂停飞行路线 */
|
|
1247
|
+
pauseFlyPath(): void;
|
|
1248
|
+
/** 恢复播放 */
|
|
1249
|
+
resumeFlyPath(): void;
|
|
1250
|
+
/** 是否正在播放飞行路线 */
|
|
1251
|
+
isFlyPathPlaying(): boolean;
|
|
1252
|
+
/** 获取当前相机位置(用于添加关键帧) */
|
|
1253
|
+
getCameraState(): {
|
|
1254
|
+
position: [number, number, number];
|
|
1255
|
+
target: [number, number, number];
|
|
1256
|
+
} | null;
|
|
1257
|
+
/** 设置相机位置(跳转到指定视角) */
|
|
1258
|
+
setCameraState(state: {
|
|
1259
|
+
position: [number, number, number];
|
|
1260
|
+
target: [number, number, number];
|
|
1261
|
+
}): void;
|
|
1262
|
+
/** 平滑聚焦到指定节点 */
|
|
1263
|
+
focusOnNode(nodeId: string): void;
|
|
1264
|
+
/** 设置路径编辑的活动效果 */
|
|
1265
|
+
setPathEditingEffect(effectKey: string | null): void;
|
|
1266
|
+
/** 是否正在编辑路径 */
|
|
1267
|
+
isPathEditing(): boolean;
|
|
1268
|
+
/** 设置路径编辑控制点大小 */
|
|
1269
|
+
setPathHandleSize(size: number): void;
|
|
1270
|
+
/** 显示告警弹框 */
|
|
1271
|
+
showAlarm(nodeId: string, data: any): void;
|
|
1272
|
+
/** 隐藏告警弹框 */
|
|
1273
|
+
hideAlarm(nodeId: string): void;
|
|
1274
|
+
/** 拖拽悬停:允许放置 */
|
|
1275
|
+
onDragOver(e: DragEvent): void;
|
|
1276
|
+
/** 拖拽放置:解析资产信息并发射事件 */
|
|
1277
|
+
onDrop(e: DragEvent): void;
|
|
1278
|
+
/** 获取 viewer 实例(供外部调用高级功能如告警弹窗) */
|
|
1279
|
+
getViewer(): SceneViewer | null;
|
|
1280
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<SceneViewComponent, never>;
|
|
1281
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<SceneViewComponent, "twin-scene-view", never, { "store": { "alias": "store"; "required": false; }; "mode": { "alias": "mode"; "required": false; }; "isEditorMode": { "alias": "isEditorMode"; "required": false; }; }, { "select": "select"; "transformEnd": "transformEnd"; "dropAsset": "dropAsset"; "thumbnailGenerated": "thumbnailGenerated"; "pathPointMoved": "pathPointMoved"; "addPathPoint": "addPathPoint"; "deletePathPoint": "deletePathPoint"; "pathEditingChanged": "pathEditingChanged"; "pathPointRightClick": "pathPointRightClick"; "segmentRightClick": "segmentRightClick"; "modelSurfaceClick": "modelSurfaceClick"; "movePathPointToEnd": "movePathPointToEnd"; }, never, never, true, never>;
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
/**
|
|
1285
|
+
* 路径编辑器 — 可视化编辑路径粒子的路径点
|
|
1286
|
+
* 控制点球体 + 路径线,支持拖拽/添加/删除
|
|
1287
|
+
* 复用 viewer 的 TransformControls,不创建独立实例
|
|
1288
|
+
*/
|
|
1289
|
+
|
|
1290
|
+
interface PathEditorCallbacks {
|
|
1291
|
+
onPathPointMoved(nodeId: string, effectIndex: number, pointIndex: number, newPosition: Vec3): void;
|
|
1292
|
+
onPathPointDrag?(nodeId: string, effectIndex: number, newPoints: Vec3[]): void;
|
|
1293
|
+
onAddPathPoint(nodeId: string, effectIndex: number, insertAfter: number, position: Vec3): void;
|
|
1294
|
+
onDeletePathPoint(nodeId: string, effectIndex: number, pointIndex: number): void;
|
|
1295
|
+
onActiveEffectChanged?(effectKey: string | null): void;
|
|
1296
|
+
/** 控制点右键点击 */
|
|
1297
|
+
onPathPointRightClick?(nodeId: string, effectIndex: number, pointIndex: number, position: Vec3, event: MouseEvent): void;
|
|
1298
|
+
/** 线段右键点击 */
|
|
1299
|
+
onSegmentRightClick?(nodeId: string, effectIndex: number, segmentIndex: number, position: Vec3, event: MouseEvent): void;
|
|
1300
|
+
/** 模型表面点击(左键) */
|
|
1301
|
+
onModelSurfaceClick?(nodeId: string, effectIndex: number, position: Vec3, event: PointerEvent): void;
|
|
1302
|
+
/** 移动控制点到数组开头/结尾 */
|
|
1303
|
+
onMovePathPointToEnd?(nodeId: string, effectIndex: number, pointIndex: number, toStart: boolean): void;
|
|
1304
|
+
/** 获取模型节点对象(用于添加子级) */
|
|
1305
|
+
getNodeObject?(nodeId: string): THREE.Object3D | null;
|
|
1306
|
+
}
|
|
1307
|
+
interface PathVis {
|
|
1308
|
+
handles: THREE.Mesh[];
|
|
1309
|
+
curveLine: THREE.Line;
|
|
1310
|
+
segmentLines: THREE.Line[];
|
|
1311
|
+
effectKey: string;
|
|
1312
|
+
nodeId: string;
|
|
1313
|
+
effectIndex: number;
|
|
1314
|
+
pathPoints: Vec3[];
|
|
1315
|
+
segmentTypes: ('linear' | 'smooth')[];
|
|
1316
|
+
}
|
|
1317
|
+
declare class PathEditor {
|
|
1318
|
+
private scene;
|
|
1319
|
+
private camera;
|
|
1320
|
+
private renderer;
|
|
1321
|
+
private store;
|
|
1322
|
+
private orbit;
|
|
1323
|
+
private cb;
|
|
1324
|
+
private visuals;
|
|
1325
|
+
private activeEffectKey;
|
|
1326
|
+
private dragging;
|
|
1327
|
+
private handleSize;
|
|
1328
|
+
private pendingAttachIndex;
|
|
1329
|
+
/** 复用 viewer 的 gizmo,不创建独立实例 */
|
|
1330
|
+
private gizmo;
|
|
1331
|
+
private raycaster;
|
|
1332
|
+
private pointer;
|
|
1333
|
+
onActiveEffectChanged?: (effectKey: string | null) => void;
|
|
1334
|
+
/** 设置 viewer 的 gizmo(由 viewer 初始化时调用) */
|
|
1335
|
+
setGizmo(gizmo: TransformControls): void;
|
|
1336
|
+
constructor(scene: THREE.Scene, camera: THREE.Camera, renderer: THREE.WebGLRenderer, store: SceneStore, orbit: OrbitControls, cb: PathEditorCallbacks);
|
|
1337
|
+
/** 绑定 gizmo 事件(由 viewer 初始化时调用一次) */
|
|
1338
|
+
bindGizmoEvents(): void;
|
|
1339
|
+
/** 同步 store 数据到可视化 */
|
|
1340
|
+
sync(): void;
|
|
1341
|
+
/** 设置活动编辑效果 */
|
|
1342
|
+
setActiveEffect(effectKey: string | null): void;
|
|
1343
|
+
/** 是否正在编辑 */
|
|
1344
|
+
isActive(): boolean;
|
|
1345
|
+
/** 获取所有绿球 mesh(用于射线检测) */
|
|
1346
|
+
getHandles(): THREE.Mesh[];
|
|
1347
|
+
/** 获取活动效果 key */
|
|
1348
|
+
getActiveEffectKey(): string | null;
|
|
1349
|
+
/** 根据世界坐标位置找到插入位置 */
|
|
1350
|
+
getInsertPosition(worldPoint: THREE.Vector3): {
|
|
1351
|
+
insertAfter: number;
|
|
1352
|
+
position: Vec3;
|
|
1353
|
+
} | null;
|
|
1354
|
+
/** 设置控制点球体大小 */
|
|
1355
|
+
setHandleSize(size: number): void;
|
|
1356
|
+
/** 设置待绑定的控制点索引(sync 后绑定到该点) */
|
|
1357
|
+
setPendingAttachIndex(index: number): void;
|
|
1358
|
+
private recreateHandles;
|
|
1359
|
+
/** 尝试拾取控制点,返回是否拾取到 */
|
|
1360
|
+
pickHandle(event: PointerEvent): boolean;
|
|
1361
|
+
/** 尝试拾取路径线,返回插入位置 */
|
|
1362
|
+
pickCurve(event: PointerEvent, groundY?: number): {
|
|
1363
|
+
insertAfter: number;
|
|
1364
|
+
position: Vec3;
|
|
1365
|
+
} | null;
|
|
1366
|
+
/** 拾取线段,返回段索引 */
|
|
1367
|
+
pickSegment(event: PointerEvent): {
|
|
1368
|
+
segmentIndex: number;
|
|
1369
|
+
position: Vec3;
|
|
1370
|
+
} | null;
|
|
1371
|
+
/** 处理右键点击 */
|
|
1372
|
+
handleRightClick(event: MouseEvent): boolean;
|
|
1373
|
+
/** 根据点击位置找到插入位置(开头/结尾) */
|
|
1374
|
+
findInsertPosition(vis: PathVis, point: THREE.Vector3): {
|
|
1375
|
+
insertAfter: number;
|
|
1376
|
+
position: Vec3;
|
|
1377
|
+
};
|
|
1378
|
+
/** 释放资源 */
|
|
1379
|
+
dispose(): void;
|
|
1380
|
+
private updatePointer;
|
|
1381
|
+
private createVisual;
|
|
1382
|
+
private updateVisual;
|
|
1383
|
+
private removeVisual;
|
|
1384
|
+
private setVisVisible;
|
|
1385
|
+
private buildCurvePoints;
|
|
1386
|
+
/** 为单个段构建曲线点,包含上下文点以便 smooth 段能正确弯曲 */
|
|
1387
|
+
private buildSegmentCurvePoints;
|
|
1388
|
+
private attachGizmo;
|
|
1389
|
+
private startDrag;
|
|
1390
|
+
private updateDrag;
|
|
1391
|
+
private finishDrag;
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
/**
|
|
1395
|
+
* 粒子系统运行时
|
|
1396
|
+
* 使用 BufferGeometry + Points + ShaderMaterial 实现 GPU 加速的粒子效果
|
|
1397
|
+
*/
|
|
1398
|
+
|
|
1399
|
+
/** 粒子运行时状态 */
|
|
1400
|
+
declare class ParticleRuntime {
|
|
1401
|
+
geometry: THREE.BufferGeometry;
|
|
1402
|
+
material: THREE.ShaderMaterial;
|
|
1403
|
+
points: THREE.Points;
|
|
1404
|
+
private positions;
|
|
1405
|
+
private velocities;
|
|
1406
|
+
private ages;
|
|
1407
|
+
private lifetimes;
|
|
1408
|
+
private sizes;
|
|
1409
|
+
private alive;
|
|
1410
|
+
private emitAccumulator;
|
|
1411
|
+
private config;
|
|
1412
|
+
private origin;
|
|
1413
|
+
/** 水平漂移累积(每个粒子独立的随机相位) */
|
|
1414
|
+
private driftPhases;
|
|
1415
|
+
private posAttr;
|
|
1416
|
+
private ageAttr;
|
|
1417
|
+
private lifetimeAttr;
|
|
1418
|
+
private sizeAttr;
|
|
1419
|
+
constructor(config: ParticleEffect, origin: THREE.Vector3);
|
|
1420
|
+
/** 更新配置参数(不重建 geometry) */
|
|
1421
|
+
updateConfig(config: ParticleEffect): void;
|
|
1422
|
+
/** 发射粒子 */
|
|
1423
|
+
private emit;
|
|
1424
|
+
private findDeadSlot;
|
|
1425
|
+
/** 每帧更新 */
|
|
1426
|
+
update(dt: number): void;
|
|
1427
|
+
/** 更新粒子系统的原点位置(跟随父节点) */
|
|
1428
|
+
setOrigin(position: THREE.Vector3): void;
|
|
1429
|
+
dispose(): void;
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
/**
|
|
1433
|
+
* 路径粒子系统运行时
|
|
1434
|
+
* 粒子沿指定路径(曲线)运动,模拟管道水流/光纤效果
|
|
1435
|
+
*/
|
|
1436
|
+
|
|
1437
|
+
/** 路径粒子运行时状态 */
|
|
1438
|
+
declare class PathParticleRuntime {
|
|
1439
|
+
geometry: THREE.BufferGeometry;
|
|
1440
|
+
material: THREE.ShaderMaterial;
|
|
1441
|
+
points: THREE.Points;
|
|
1442
|
+
private positions;
|
|
1443
|
+
private progresses;
|
|
1444
|
+
private sizes;
|
|
1445
|
+
/** 每个粒子的固定 3D 偏移(管道截面内随机分布) */
|
|
1446
|
+
private offsets;
|
|
1447
|
+
private opacities;
|
|
1448
|
+
private trailProgresses;
|
|
1449
|
+
private config;
|
|
1450
|
+
private curve;
|
|
1451
|
+
private time;
|
|
1452
|
+
private posAttr;
|
|
1453
|
+
private progressAttr;
|
|
1454
|
+
private sizeAttr;
|
|
1455
|
+
private opacityAttr;
|
|
1456
|
+
private trailAttr;
|
|
1457
|
+
constructor(config: PathParticleEffect);
|
|
1458
|
+
/** 获取当前路径点(用于比较是否变化) */
|
|
1459
|
+
getPathPoints(): Vec3[];
|
|
1460
|
+
/** 更新配置参数 */
|
|
1461
|
+
updateConfig(config: PathParticleEffect): void;
|
|
1462
|
+
private segmentTypesChanged;
|
|
1463
|
+
private pathChanged;
|
|
1464
|
+
private rebuildBuffers;
|
|
1465
|
+
/** 每帧更新 */
|
|
1466
|
+
update(dt: number): void;
|
|
1467
|
+
/** 更新路径点(仅更新曲线和粒子位置,不更新 config.pathPoints) */
|
|
1468
|
+
setPathPoints(points: Vec3[], segmentTypes?: ('linear' | 'smooth')[]): void;
|
|
1469
|
+
/** 根据切线方向,在垂直平面内计算圆盘偏移的 3D 向量 */
|
|
1470
|
+
private static _tmpN;
|
|
1471
|
+
private static _tmpB;
|
|
1472
|
+
private static _tmpT;
|
|
1473
|
+
static offsetOnPlane(angle: number, radius: number, tangent: THREE.Vector3): THREE.Vector3;
|
|
1474
|
+
dispose(): void;
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
/**
|
|
1478
|
+
* 分段路径 — 根据 segmentTypes 逐段生成曲线
|
|
1479
|
+
* - 'linear': 直线段(线性插值)
|
|
1480
|
+
* - 'smooth': 平滑曲线段(CatmullRom,前后各借一个控制点)
|
|
1481
|
+
*
|
|
1482
|
+
* 注意:CatmullRomCurve3([p0,p1,p2,p3]) 的参数范围是 p0→p3 (t=0→1)
|
|
1483
|
+
* 其中 p1 在 t=1/3, p2 在 t=2/3
|
|
1484
|
+
* 所以段 p1→p2 对应曲线参数 t∈[1/3, 2/3]
|
|
1485
|
+
*/
|
|
1486
|
+
|
|
1487
|
+
/**
|
|
1488
|
+
* 分段路径类
|
|
1489
|
+
* 根据 segmentTypes 数组决定每段是直线还是曲线
|
|
1490
|
+
*/
|
|
1491
|
+
declare class SegmentedPath {
|
|
1492
|
+
private segments;
|
|
1493
|
+
private totalLength;
|
|
1494
|
+
/**
|
|
1495
|
+
* @param points 路径点数组
|
|
1496
|
+
* @param segmentTypes 段类型数组,长度 = points.length - 1
|
|
1497
|
+
*/
|
|
1498
|
+
constructor(points: THREE.Vector3[], segmentTypes: ('linear' | 'smooth')[]);
|
|
1499
|
+
private buildSegments;
|
|
1500
|
+
/** 数值积分计算曲线在参数范围 [t0, t1] 内的弧长 */
|
|
1501
|
+
private computeSegmentLength;
|
|
1502
|
+
/** 将段内局部参数 t∈[0,1] 映射到曲线参数 t∈[1/3, 2/3] */
|
|
1503
|
+
private mapToCurveT;
|
|
1504
|
+
/** 获取段上某局部参数对应的点 */
|
|
1505
|
+
private getSmoothPoint;
|
|
1506
|
+
/**
|
|
1507
|
+
* 根据进度 t (0-1) 获取路径上的点
|
|
1508
|
+
* 按弧长参数化,保证匀速运动
|
|
1509
|
+
*/
|
|
1510
|
+
getPointAt(t: number): THREE.Vector3;
|
|
1511
|
+
/**
|
|
1512
|
+
* 获取路径上的采样点(用于可视化)
|
|
1513
|
+
* @param totalSegments 总采样段数
|
|
1514
|
+
*/
|
|
1515
|
+
getPoints(totalSegments?: number): THREE.Vector3[];
|
|
1516
|
+
/**
|
|
1517
|
+
* 按弧长均匀采样路径点(用于粒子均匀分布)
|
|
1518
|
+
* @param count 采样点数量
|
|
1519
|
+
*/
|
|
1520
|
+
getSpacedPoints(count?: number): THREE.Vector3[];
|
|
1521
|
+
/** 获取总长度 */
|
|
1522
|
+
getLength(): number;
|
|
1523
|
+
/**
|
|
1524
|
+
* 根据进度 t (0-1) 获取路径在该点的切线方向(单位向量)
|
|
1525
|
+
*/
|
|
1526
|
+
getTangentAt(t: number): THREE.Vector3;
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
/** Draco 解码器路径(打包进 npm 包) */
|
|
1530
|
+
declare const DRACO_DECODER_PATH = "assets/draco/gltf/";
|
|
1531
|
+
/** Draco 编码器路径(打包进 npm 包) */
|
|
1532
|
+
declare const DRACO_ENCODER_PATH = "assets/draco/encoder/";
|
|
1533
|
+
|
|
1534
|
+
export { AddAnimationCommand, AddEffectCommand, AddNodeCommand, BatchRemoveCommand, DRACO_DECODER_PATH, DRACO_ENCODER_PATH, FlyPathCommand, GroupCommand, History, InsertPathPointCommand, KeyframeCommand, MaterialOverrideCommand, MovePathPointToEndCommand, ParticleRuntime, PathEditor, PathParticleRuntime, RemoveAnimationCommand, RemoveEffectCommand, RemoveNodeCommand, RemovePathPointCommand, ReparentCommand, SceneStore, SceneViewComponent, SceneViewer, SegmentedPath, SetAlarmCommand, SetDashboardCommand, SetEnvironmentCommand, SetLightPropertyCommand, SetNodeNameCommand, SetNodeTextCommand, SyncMaterialCommand, ToggleSegmentTypeCommand, TransformCommand, UngroupCommand, UpdateAnimationCommand, UpdateEffectCommand, UpdatePathPointsCommand, clone, createNode, createSceneDoc, nextId };
|
|
1535
|
+
export type { AlarmConfig, AlarmPopupData, AnimationRule, CameraDoc, CameraKeyframe, Command, DashboardConfig, DashboardDataPoint, DashboardWidget, EnvironmentConfig, FlyPath, MaterialOverride, NodeDoc, ParticleEffect, PathEditorCallbacks, PathParticleEffect, PickEvent, PrimitiveKind, SceneDoc, SceneMeta, ScreenContentEffect, TransformMode, TransformState, UvAnimationEffect, Vec3, ViewerCallbacks, VisualEffect, VisualEffectBase, VisualEffectKind };
|