@sequent-org/ifc-viewer 1.0.3-ci.6.0 → 1.0.4-ci.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +122 -2
- package/package.json +1 -1
- package/src/IfcViewer.js +4 -2
- package/src/ifc/IfcService.js +238 -43
package/README.md
CHANGED
|
@@ -43,7 +43,8 @@ function showIfcModal(ifcUrl) {
|
|
|
43
43
|
|
|
44
44
|
const viewer = new IfcViewer({
|
|
45
45
|
container: container,
|
|
46
|
-
ifcUrl: ifcUrl
|
|
46
|
+
ifcUrl: ifcUrl,
|
|
47
|
+
wasmUrl: '/storage/web-ifc.wasm' // Путь к WASM в Laravel storage
|
|
47
48
|
// Минимальный режим по умолчанию - только просмотрщик с верхней панелью
|
|
48
49
|
})
|
|
49
50
|
|
|
@@ -53,6 +54,28 @@ function showIfcModal(ifcUrl) {
|
|
|
53
54
|
}
|
|
54
55
|
```
|
|
55
56
|
|
|
57
|
+
**Настройка WASM файла в Laravel:**
|
|
58
|
+
|
|
59
|
+
1. Скопируйте `web-ifc.wasm` в папку `public/storage/`:
|
|
60
|
+
```bash
|
|
61
|
+
cp node_modules/web-ifc/web-ifc.wasm public/storage/
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
2. Или используйте символическую ссылку:
|
|
65
|
+
```bash
|
|
66
|
+
php artisan storage:link
|
|
67
|
+
# Затем скопируйте файл в storage/app/public/
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
3. Альтернативно, укажите путь к файлу в `node_modules`:
|
|
71
|
+
```javascript
|
|
72
|
+
const viewer = new IfcViewer({
|
|
73
|
+
container: container,
|
|
74
|
+
ifcUrl: ifcUrl,
|
|
75
|
+
wasmUrl: '/node_modules/web-ifc/web-ifc.wasm'
|
|
76
|
+
})
|
|
77
|
+
```
|
|
78
|
+
|
|
56
79
|
### Загрузка пользовательского файла
|
|
57
80
|
|
|
58
81
|
```javascript
|
|
@@ -74,6 +97,18 @@ fileInput.addEventListener('change', async (e) => {
|
|
|
74
97
|
})
|
|
75
98
|
```
|
|
76
99
|
|
|
100
|
+
### Кастомный путь к WASM файлу
|
|
101
|
+
|
|
102
|
+
```javascript
|
|
103
|
+
const viewer = new IfcViewer({
|
|
104
|
+
container: '#viewer-container',
|
|
105
|
+
ifcUrl: '/models/building.ifc',
|
|
106
|
+
wasmUrl: '/custom-path/web-ifc.wasm' // Указываем свой путь к WASM
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
await viewer.init()
|
|
110
|
+
```
|
|
111
|
+
|
|
77
112
|
## ⚙️ Опции конфигурации
|
|
78
113
|
|
|
79
114
|
| Опция | Тип | По умолчанию | Описание |
|
|
@@ -81,12 +116,52 @@ fileInput.addEventListener('change', async (e) => {
|
|
|
81
116
|
| `container` | `HTMLElement \| string` | **обязательно** | DOM элемент или селектор для контейнера |
|
|
82
117
|
| `ifcUrl` | `string` | `null` | URL для загрузки IFC файла |
|
|
83
118
|
| `ifcFile` | `File` | `null` | File объект для загрузки |
|
|
119
|
+
| `wasmUrl` | `string` | `null` | URL для загрузки WASM файла web-ifc |
|
|
84
120
|
| `showSidebar` | `boolean` | `false` | Показывать боковую панель с деревом |
|
|
85
121
|
| `showControls` | `boolean` | `false` | Показывать нижние кнопки управления |
|
|
86
122
|
| `showToolbar` | `boolean` | `true` | Показывать верхнюю панель инструментов |
|
|
87
123
|
| `autoLoad` | `boolean` | `true` | Автоматически загружать при инициализации |
|
|
88
124
|
| `theme` | `string` | `'light'` | Тема интерфейса (`'light'` \| `'dark'`) |
|
|
89
125
|
|
|
126
|
+
### 🔧 Подробное описание параметров
|
|
127
|
+
|
|
128
|
+
#### `wasmUrl` - Настройка пути к WASM файлу
|
|
129
|
+
|
|
130
|
+
Параметр `wasmUrl` позволяет указать кастомный путь к WASM файлу библиотеки `web-ifc`. Это особенно полезно при интеграции пакета в проекты с нестандартной структурой ресурсов.
|
|
131
|
+
|
|
132
|
+
**Особенности:**
|
|
133
|
+
- **По умолчанию**: Если не указан, используется автоматически определяемый путь из папки `public/wasm/`
|
|
134
|
+
- **Поддержка форматов**: Полные URL (`https://example.com/web-ifc.wasm`) и относительные пути (`/assets/web-ifc.wasm`)
|
|
135
|
+
- **Обратная совместимость**: При ошибке загрузки кастомного пути автоматически переключается на дефолтный
|
|
136
|
+
- **Обработка ошибок**: В консоль выводится предупреждение при неудачной настройке кастомного пути
|
|
137
|
+
|
|
138
|
+
**Примеры использования:**
|
|
139
|
+
|
|
140
|
+
```javascript
|
|
141
|
+
// Относительный путь
|
|
142
|
+
const viewer1 = new IfcViewer({
|
|
143
|
+
container: '#viewer',
|
|
144
|
+
wasmUrl: '/assets/web-ifc.wasm'
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
// Полный URL
|
|
148
|
+
const viewer2 = new IfcViewer({
|
|
149
|
+
container: '#viewer',
|
|
150
|
+
wasmUrl: 'https://cdn.example.com/web-ifc.wasm'
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
// Путь с подпапкой
|
|
154
|
+
const viewer3 = new IfcViewer({
|
|
155
|
+
container: '#viewer',
|
|
156
|
+
wasmUrl: '/static/libs/web-ifc.wasm'
|
|
157
|
+
})
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**Когда использовать:**
|
|
161
|
+
- При размещении WASM файла в нестандартной папке
|
|
162
|
+
- При использовании CDN для статических ресурсов
|
|
163
|
+
- При интеграции в фреймворки с особой структурой ресурсов (Laravel, Next.js и т.д.)
|
|
164
|
+
|
|
90
165
|
## 🎯 API методы
|
|
91
166
|
|
|
92
167
|
### Основные методы
|
|
@@ -196,11 +271,56 @@ npm run test:manual
|
|
|
196
271
|
- `.ifczip` - архивы IFC
|
|
197
272
|
- `.zip` - ZIP архивы с IFC файлами
|
|
198
273
|
|
|
274
|
+
## 🔧 Troubleshooting
|
|
275
|
+
|
|
276
|
+
### Проблемы с WASM файлом
|
|
277
|
+
|
|
278
|
+
**Ошибка загрузки WASM:**
|
|
279
|
+
```
|
|
280
|
+
Failed to load web-ifc.wasm
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
**Решения:**
|
|
284
|
+
1. **Проверьте путь к файлу:**
|
|
285
|
+
```javascript
|
|
286
|
+
// Убедитесь, что файл доступен по указанному пути
|
|
287
|
+
const viewer = new IfcViewer({
|
|
288
|
+
container: '#viewer',
|
|
289
|
+
wasmUrl: '/correct-path/web-ifc.wasm'
|
|
290
|
+
})
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
2. **Проверьте CORS настройки:**
|
|
294
|
+
- Для локальной разработки используйте относительные пути
|
|
295
|
+
- Для продакшена настройте CORS для WASM файлов
|
|
296
|
+
|
|
297
|
+
3. **Проверьте MIME-тип:**
|
|
298
|
+
```nginx
|
|
299
|
+
# В nginx.conf
|
|
300
|
+
location ~* \.wasm$ {
|
|
301
|
+
add_header Content-Type application/wasm;
|
|
302
|
+
}
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
4. **Альтернативные пути:**
|
|
306
|
+
```javascript
|
|
307
|
+
// Попробуйте разные варианты
|
|
308
|
+
wasmUrl: '/web-ifc.wasm' // корень
|
|
309
|
+
wasmUrl: '/assets/web-ifc.wasm' // папка assets
|
|
310
|
+
wasmUrl: '/static/web-ifc.wasm' // папка static
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
**Отладка:**
|
|
314
|
+
- Откройте DevTools → Network и проверьте загрузку WASM файла
|
|
315
|
+
- Проверьте консоль на предупреждения о `wasmUrl`
|
|
316
|
+
- Убедитесь, что файл `web-ifc.wasm` существует и доступен
|
|
317
|
+
|
|
199
318
|
## 🔧 Системные требования
|
|
200
319
|
|
|
201
320
|
- Node.js >= 16
|
|
202
|
-
- Современный браузер с поддержкой WebGL
|
|
321
|
+
- Современный браузер с поддержкой WebGL и WebAssembly
|
|
203
322
|
- Для работы требуются файлы `web-ifc.wasm` в публичной папке проекта
|
|
323
|
+
- Поддержка ES6 модулей в браузере
|
|
204
324
|
|
|
205
325
|
## 📄 Лицензия
|
|
206
326
|
|
package/package.json
CHANGED
package/src/IfcViewer.js
CHANGED
|
@@ -21,6 +21,7 @@ export class IfcViewer {
|
|
|
21
21
|
* @param {HTMLElement|string} options.container - Контейнер для рендера (элемент или селектор)
|
|
22
22
|
* @param {string} [options.ifcUrl] - URL для загрузки IFC файла
|
|
23
23
|
* @param {File} [options.ifcFile] - File объект для загрузки IFC файла
|
|
24
|
+
* @param {string} [options.wasmUrl] - URL для загрузки WASM файла web-ifc
|
|
24
25
|
* @param {boolean} [options.showSidebar=false] - Показывать ли боковую панель с деревом
|
|
25
26
|
* @param {boolean} [options.showControls=false] - Показывать ли панель управления (нижние кнопки)
|
|
26
27
|
* @param {boolean} [options.showToolbar=true] - Показывать ли верхнюю панель инструментов
|
|
@@ -47,8 +48,9 @@ export class IfcViewer {
|
|
|
47
48
|
this.options = {
|
|
48
49
|
ifcUrl: options.ifcUrl || null,
|
|
49
50
|
ifcFile: options.ifcFile || null,
|
|
51
|
+
wasmUrl: options.wasmUrl || null,
|
|
50
52
|
showSidebar: options.showSidebar === true, // по умолчанию false
|
|
51
|
-
showControls: options.showControls === true, // по умолчанию false
|
|
53
|
+
showControls: options.showControls === true, // по умолчанию false
|
|
52
54
|
showToolbar: options.showToolbar !== false, // по умолчанию true
|
|
53
55
|
autoLoad: options.autoLoad !== false,
|
|
54
56
|
theme: options.theme || 'light',
|
|
@@ -430,7 +432,7 @@ export class IfcViewer {
|
|
|
430
432
|
throw new Error('Viewer должен быть инициализирован перед IfcService');
|
|
431
433
|
}
|
|
432
434
|
|
|
433
|
-
this.ifcService = new IfcService(this.viewer);
|
|
435
|
+
this.ifcService = new IfcService(this.viewer, this.options.wasmUrl);
|
|
434
436
|
this.ifcService.init();
|
|
435
437
|
}
|
|
436
438
|
|
package/src/ifc/IfcService.js
CHANGED
|
@@ -12,9 +12,11 @@ import IFCWorkerUrl from 'web-ifc-three/IFCWorker.js?url';
|
|
|
12
12
|
export class IfcService {
|
|
13
13
|
/**
|
|
14
14
|
* @param {import('../viewer/Viewer').Viewer} viewer
|
|
15
|
+
* @param {string} [wasmUrl] - URL для загрузки WASM файла web-ifc
|
|
15
16
|
*/
|
|
16
|
-
constructor(viewer) {
|
|
17
|
+
constructor(viewer, wasmUrl = null) {
|
|
17
18
|
this.viewer = viewer;
|
|
19
|
+
this.wasmUrl = wasmUrl;
|
|
18
20
|
this.loader = null;
|
|
19
21
|
this.lastModel = null; // THREE.Object3D модели IFC
|
|
20
22
|
this.lastFileName = null;
|
|
@@ -24,30 +26,115 @@ export class IfcService {
|
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
init() {
|
|
27
|
-
this.loader = new IFCLoader();
|
|
28
|
-
// Отключаем Web Worker: временно парсим в главном потоке для стабильности
|
|
29
|
-
try { this.loader.ifcManager.useWebWorkers?.(false); } catch(_) {}
|
|
30
|
-
// Путь к wasm файлу (скопируйте web-ifc.wasm в public/wasm)
|
|
31
29
|
try {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
this.loader.ifcManager.
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
30
|
+
this.loader = new IFCLoader();
|
|
31
|
+
// Отключаем Web Worker: временно парсим в главном потоке для стабильности
|
|
32
|
+
try { this.loader.ifcManager.useWebWorkers?.(false); } catch(_) {}
|
|
33
|
+
|
|
34
|
+
// Настройка пути к WASM файлу с улучшенной обработкой ошибок
|
|
35
|
+
this._setupWasmPath();
|
|
36
|
+
|
|
37
|
+
// Настройка конфигурации web-ifc
|
|
38
|
+
this._setupWebIfcConfig();
|
|
39
|
+
|
|
40
|
+
} catch (error) {
|
|
41
|
+
console.error('IfcService: критическая ошибка инициализации:', error);
|
|
42
|
+
this._handleCriticalError(error);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Настройка пути к WASM файлу с fallback
|
|
48
|
+
* @private
|
|
49
|
+
*/
|
|
50
|
+
_setupWasmPath() {
|
|
51
|
+
const wasmPaths = this._getWasmPaths();
|
|
52
|
+
|
|
53
|
+
for (let i = 0; i < wasmPaths.length; i++) {
|
|
54
|
+
try {
|
|
55
|
+
this.loader.ifcManager.setWasmPath(wasmPaths[i]);
|
|
56
|
+
console.log(`IfcService: WASM путь установлен: ${wasmPaths[i]}`);
|
|
57
|
+
return; // Успешно установлен
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.warn(`IfcService: не удалось установить WASM путь ${wasmPaths[i]}:`, error.message);
|
|
60
|
+
if (i === wasmPaths.length - 1) {
|
|
61
|
+
// Последний путь тоже не сработал
|
|
62
|
+
throw new Error('Все пути к WASM файлу недоступны');
|
|
63
|
+
}
|
|
64
|
+
}
|
|
39
65
|
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Получает список путей к WASM файлу в порядке приоритета
|
|
70
|
+
* @private
|
|
71
|
+
*/
|
|
72
|
+
_getWasmPaths() {
|
|
73
|
+
const paths = [];
|
|
74
|
+
|
|
75
|
+
// 1. Пользовательский путь (если указан)
|
|
76
|
+
if (this.wasmUrl) {
|
|
77
|
+
paths.push(this.wasmUrl);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// 2. Пути по умолчанию
|
|
81
|
+
try {
|
|
82
|
+
const wasmDir = new URL('.', WEBIFC_WASM_URL).href;
|
|
83
|
+
paths.push(wasmDir);
|
|
84
|
+
} catch (_) {}
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
paths.push(WEBIFC_WASM_URL);
|
|
88
|
+
} catch (_) {}
|
|
89
|
+
|
|
90
|
+
// 3. Резервные пути
|
|
91
|
+
paths.push('/wasm/', '/wasm/web-ifc.wasm', '/web-ifc.wasm');
|
|
92
|
+
|
|
93
|
+
return paths;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Настройка конфигурации web-ifc
|
|
98
|
+
* @private
|
|
99
|
+
*/
|
|
100
|
+
_setupWebIfcConfig() {
|
|
40
101
|
try {
|
|
41
102
|
this.loader.ifcManager.applyWebIfcConfig?.({
|
|
42
103
|
COORDINATE_TO_ORIGIN: true,
|
|
43
104
|
USE_FAST_BOOLS: true,
|
|
44
105
|
// Порог игнорирования очень мелких полигонов (уменьшаем шум)
|
|
45
|
-
// Некоторые сборки поддерживают SMALL_TRIANGLE_THRESHOLD
|
|
46
106
|
SMALL_TRIANGLE_THRESHOLD: 1e-9,
|
|
47
107
|
});
|
|
48
|
-
} catch(
|
|
108
|
+
} catch (error) {
|
|
109
|
+
console.warn('IfcService: не удалось применить конфигурацию web-ifc:', error.message);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Обработка критических ошибок инициализации
|
|
115
|
+
* @private
|
|
116
|
+
*/
|
|
117
|
+
_handleCriticalError(error) {
|
|
118
|
+
// Создаем заглушку для loader, чтобы избежать падения
|
|
119
|
+
this.loader = {
|
|
120
|
+
ifcManager: {
|
|
121
|
+
setWasmPath: () => {},
|
|
122
|
+
applyWebIfcConfig: () => {},
|
|
123
|
+
useWebWorkers: () => {},
|
|
124
|
+
load: () => Promise.reject(new Error('WASM не инициализирован'))
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// Уведомляем о критической ошибке
|
|
129
|
+
this.viewer?.container?.dispatchEvent(new CustomEvent('ifcviewer:error', {
|
|
130
|
+
detail: {
|
|
131
|
+
error: new Error('Критическая ошибка инициализации WASM: ' + error.message),
|
|
132
|
+
type: 'wasm_init_error'
|
|
133
|
+
}
|
|
134
|
+
}));
|
|
49
135
|
}
|
|
50
136
|
|
|
137
|
+
|
|
51
138
|
/**
|
|
52
139
|
* Возвращает пространственную структуру IFC (иерархия) для активной модели
|
|
53
140
|
* Структура: { expressID, type, children: [...] }
|
|
@@ -119,33 +206,36 @@ export class IfcService {
|
|
|
119
206
|
* @param {File} file
|
|
120
207
|
*/
|
|
121
208
|
async loadFile(file) {
|
|
122
|
-
if (!this.loader) this.init();
|
|
123
|
-
// Проверка расширения: поддерживаются .ifc и .ifczip
|
|
124
|
-
const name = (file?.name || "").toLowerCase();
|
|
125
|
-
const isIFC = name.endsWith(".ifc");
|
|
126
|
-
const isIFS = name.endsWith(".ifs");
|
|
127
|
-
const isZIP = name.endsWith(".ifczip") || name.endsWith(".zip");
|
|
128
|
-
if (!isIFC && !isIFS && !isZIP) {
|
|
129
|
-
alert("Формат не поддерживается. Используйте .ifc, .ifs или .ifczip");
|
|
130
|
-
return null;
|
|
131
|
-
}
|
|
132
|
-
const url = URL.createObjectURL(file);
|
|
133
209
|
try {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
210
|
+
if (!this.loader) this.init();
|
|
211
|
+
|
|
212
|
+
// Проверка расширения: поддерживаются .ifc и .ifczip
|
|
213
|
+
const name = (file?.name || "").toLowerCase();
|
|
214
|
+
const isIFC = name.endsWith(".ifc");
|
|
215
|
+
const isIFS = name.endsWith(".ifs");
|
|
216
|
+
const isZIP = name.endsWith(".ifczip") || name.endsWith(".zip");
|
|
217
|
+
if (!isIFC && !isIFS && !isZIP) {
|
|
218
|
+
throw new Error("Формат не поддерживается. Используйте .ifc, .ifs или .ifczip");
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const url = URL.createObjectURL(file);
|
|
222
|
+
try {
|
|
223
|
+
const model = await this._loadModelWithFallback(url);
|
|
224
|
+
// Показать модель вместо демо-куба
|
|
225
|
+
if (this.viewer.replaceWithModel) this.viewer.replaceWithModel(model);
|
|
226
|
+
if (this.viewer.focusObject) this.viewer.focusObject(model);
|
|
227
|
+
this.lastModel = model;
|
|
228
|
+
this.lastFileName = file?.name || null;
|
|
229
|
+
// Сообщим, что модель загружена
|
|
230
|
+
this._dispatchModelLoaded(model);
|
|
231
|
+
return model;
|
|
232
|
+
} finally {
|
|
233
|
+
URL.revokeObjectURL(url);
|
|
234
|
+
}
|
|
143
235
|
} catch (err) {
|
|
144
236
|
console.error("IFC load error:", err);
|
|
145
|
-
|
|
237
|
+
this._handleLoadError(err, 'loadFile');
|
|
146
238
|
return null;
|
|
147
|
-
} finally {
|
|
148
|
-
URL.revokeObjectURL(url);
|
|
149
239
|
}
|
|
150
240
|
}
|
|
151
241
|
|
|
@@ -154,15 +244,17 @@ export class IfcService {
|
|
|
154
244
|
* @param {string} url
|
|
155
245
|
*/
|
|
156
246
|
async loadUrl(url) {
|
|
157
|
-
if (!this.loader) this.init();
|
|
158
|
-
if (!url) return null;
|
|
159
247
|
try {
|
|
160
|
-
|
|
161
|
-
|
|
248
|
+
if (!this.loader) this.init();
|
|
249
|
+
if (!url) return null;
|
|
250
|
+
|
|
251
|
+
const model = await this._loadModelWithFallback(url);
|
|
162
252
|
if (!model || !model.geometry) throw new Error('IFC model returned without geometry');
|
|
253
|
+
|
|
163
254
|
if (this.viewer.replaceWithModel) this.viewer.replaceWithModel(model);
|
|
164
255
|
if (this.viewer.focusObject) this.viewer.focusObject(model);
|
|
165
256
|
this.lastModel = model;
|
|
257
|
+
|
|
166
258
|
try {
|
|
167
259
|
// Показать имя файла из URL
|
|
168
260
|
const u = new URL(url, window.location.origin);
|
|
@@ -170,12 +262,13 @@ export class IfcService {
|
|
|
170
262
|
} catch (_) {
|
|
171
263
|
this.lastFileName = url;
|
|
172
264
|
}
|
|
265
|
+
|
|
173
266
|
// Сообщим, что модель загружена
|
|
174
|
-
|
|
267
|
+
this._dispatchModelLoaded(model);
|
|
175
268
|
return model;
|
|
176
269
|
} catch (err) {
|
|
177
270
|
console.error("IFC loadUrl error:", err);
|
|
178
|
-
|
|
271
|
+
this._handleLoadError(err, 'loadUrl');
|
|
179
272
|
return null;
|
|
180
273
|
}
|
|
181
274
|
}
|
|
@@ -263,6 +356,108 @@ export class IfcService {
|
|
|
263
356
|
this.lastModel.visible = true;
|
|
264
357
|
}
|
|
265
358
|
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Загружает модель с fallback обработкой ошибок WASM
|
|
362
|
+
* @private
|
|
363
|
+
*/
|
|
364
|
+
async _loadModelWithFallback(url) {
|
|
365
|
+
try {
|
|
366
|
+
return await this.loader.loadAsync(url);
|
|
367
|
+
} catch (error) {
|
|
368
|
+
// Проверяем, связана ли ошибка с WASM
|
|
369
|
+
if (this._isWasmError(error)) {
|
|
370
|
+
console.warn('IfcService: обнаружена ошибка WASM, пытаемся переинициализировать...');
|
|
371
|
+
await this._reinitializeWithFallback();
|
|
372
|
+
// Повторная попытка загрузки
|
|
373
|
+
return await this.loader.loadAsync(url);
|
|
374
|
+
}
|
|
375
|
+
throw error;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Проверяет, связана ли ошибка с WASM
|
|
381
|
+
* @private
|
|
382
|
+
*/
|
|
383
|
+
_isWasmError(error) {
|
|
384
|
+
const message = error?.message?.toLowerCase() || '';
|
|
385
|
+
return message.includes('wasm') ||
|
|
386
|
+
message.includes('webassembly') ||
|
|
387
|
+
message.includes('module') ||
|
|
388
|
+
message.includes('instantiate');
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Переинициализирует loader с fallback путями
|
|
393
|
+
* @private
|
|
394
|
+
*/
|
|
395
|
+
async _reinitializeWithFallback() {
|
|
396
|
+
try {
|
|
397
|
+
// Попробуем переинициализировать с другими путями
|
|
398
|
+
this.loader = new IFCLoader();
|
|
399
|
+
this._setupWasmPath();
|
|
400
|
+
this._setupWebIfcConfig();
|
|
401
|
+
} catch (error) {
|
|
402
|
+
console.error('IfcService: не удалось переинициализировать:', error);
|
|
403
|
+
throw new Error('Критическая ошибка WASM: невозможно загрузить модель');
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Отправляет событие о загрузке модели
|
|
409
|
+
* @private
|
|
410
|
+
*/
|
|
411
|
+
_dispatchModelLoaded(model) {
|
|
412
|
+
try {
|
|
413
|
+
document.dispatchEvent(new CustomEvent('ifc:model-loaded', {
|
|
414
|
+
detail: { modelID: model.modelID }
|
|
415
|
+
}));
|
|
416
|
+
} catch (_) {}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Обрабатывает ошибки загрузки
|
|
421
|
+
* @private
|
|
422
|
+
*/
|
|
423
|
+
_handleLoadError(error, method) {
|
|
424
|
+
const errorMessage = `Ошибка загрузки IFC (${method}): ${error?.message || error}`;
|
|
425
|
+
|
|
426
|
+
// Отправляем событие об ошибке
|
|
427
|
+
this.viewer?.container?.dispatchEvent(new CustomEvent('ifcviewer:error', {
|
|
428
|
+
detail: {
|
|
429
|
+
error: new Error(errorMessage),
|
|
430
|
+
type: 'load_error',
|
|
431
|
+
method: method
|
|
432
|
+
}
|
|
433
|
+
}));
|
|
434
|
+
|
|
435
|
+
// Показываем пользователю понятное сообщение
|
|
436
|
+
const userMessage = this._getUserFriendlyMessage(error);
|
|
437
|
+
alert(userMessage);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Возвращает понятное пользователю сообщение об ошибке
|
|
442
|
+
* @private
|
|
443
|
+
*/
|
|
444
|
+
_getUserFriendlyMessage(error) {
|
|
445
|
+
const message = error?.message?.toLowerCase() || '';
|
|
446
|
+
|
|
447
|
+
if (message.includes('wasm') || message.includes('webassembly')) {
|
|
448
|
+
return 'Ошибка загрузки WASM модуля. Проверьте доступность файла web-ifc.wasm';
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
if (message.includes('network') || message.includes('fetch')) {
|
|
452
|
+
return 'Ошибка сети при загрузке файла. Проверьте подключение к интернету';
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
if (message.includes('format') || message.includes('parse')) {
|
|
456
|
+
return 'Ошибка формата файла. Убедитесь, что файл является корректным IFC файлом';
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
return `Ошибка загрузки модели: ${error?.message || 'Неизвестная ошибка'}`;
|
|
460
|
+
}
|
|
266
461
|
}
|
|
267
462
|
|
|
268
463
|
|