364 lines
19 KiB
HTML
364 lines
19 KiB
HTML
<!doctype html>
|
|
<html lang="ru">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>BIM DC Viewer</title>
|
|
<link rel="icon" href="./assets/logo.svg">
|
|
<style id="preload-hidden">body{opacity:0;}</style>
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Manrope:wght@500;600;700&display=swap">
|
|
<script src="./theme.js"></script>
|
|
<script>
|
|
// Apply persisted theme ASAP to avoid initial flash of defaults
|
|
(function () {
|
|
const STORAGE_KEY = "bimdc-settings-v3";
|
|
const LEGACY_KEYS = ["bimdc-settings", "bimdc-settings-v2"];
|
|
try {
|
|
LEGACY_KEYS.forEach((k) => localStorage.removeItem(k));
|
|
|
|
const project = window.__BIMDC_THEME__ || {};
|
|
const raw = localStorage.getItem(STORAGE_KEY);
|
|
let stored = null;
|
|
try {
|
|
stored = raw ? JSON.parse(raw) : null;
|
|
} catch (e) {
|
|
stored = null;
|
|
}
|
|
const storedVersion = stored && stored.themeVersion;
|
|
const projectVersion = project && project.themeVersion;
|
|
if (!stored || storedVersion !== projectVersion) {
|
|
localStorage.setItem(STORAGE_KEY, JSON.stringify(project));
|
|
stored = { ...project };
|
|
}
|
|
const s = { ...(project || {}), ...(stored || {}) };
|
|
if (!Object.keys(s).length) return;
|
|
const root = document.documentElement;
|
|
const set = (k, v) => v !== undefined && root.style.setProperty(k, v);
|
|
set("--bg-outer", s.bgOuter);
|
|
set("--canvas-top", s.canvasTop);
|
|
set("--canvas-bottom", s.canvasBottom);
|
|
set("--loading-top", s.loadingTop);
|
|
set("--loading-bottom", s.loadingBottom);
|
|
set("--accent", s.accent);
|
|
set("--accent-2", s.accent2);
|
|
set("--template-fill", s.templateFill);
|
|
set("--template-outline", s.templateOutline);
|
|
set("--template-outline-hover", s.templateOutlineHover);
|
|
set("--template-glow-strength", `${s.templateGlow}%`);
|
|
set("--toolbar-bg", s.toolbarBg);
|
|
set("--toolbar-bg-active", s.toolbarBgActive);
|
|
set("--toolbar-border", s.toolbarBorder);
|
|
set("--toolbar-outline", s.toolbarOutline);
|
|
set("--modal-bg", s.modalBg);
|
|
set("--modal-bg-alpha", s.modalBgAlpha !== undefined ? `${s.modalBgAlpha * 100}%` : undefined);
|
|
set("--modal-focus", s.modalFocus);
|
|
set("--modal-focus-alpha", s.modalFocusAlpha !== undefined ? `${s.modalFocusAlpha * 100}%` : undefined);
|
|
set("--modal-border", s.modalBorder);
|
|
set("--modal-element", s.modalElement);
|
|
set("--modal-element-outline", s.modalElementOutline);
|
|
set("--modal-radius", s.modalRadius);
|
|
set("--project-btn-fill", s.projectBtnFill);
|
|
set("--project-btn-border", s.projectBtnBorder);
|
|
set("--project-btn-hover", s.projectBtnHover);
|
|
set("--modal-btn-primary", s.modalBtnPrimary);
|
|
set("--modal-btn-secondary", s.modalBtnSecondary);
|
|
set("--loader-color", s.loaderColor);
|
|
set("--border", s.border);
|
|
set("--text", s.text);
|
|
set("--panel", s.panel);
|
|
set("--panel-2", s.panel2);
|
|
set("--highlight-color", s.highlight);
|
|
set("--cube-text", s.navCubeText || s.cubeText);
|
|
set("--cube-edges", s.navCubeBase || s.cubeEdges);
|
|
set("--cube-base", s.navCubeBase || s.cubeEdges);
|
|
set("--cube-hover", s.navCubeHover);
|
|
set("--cube-edge-color", s.navCubeEdge);
|
|
set("--cube-edge-alpha", s.navCubeEdgeAlpha);
|
|
set("--plus-color", s.plus);
|
|
set("--measure-line", s.measureLine);
|
|
set("--measure-label", s.measureLabel);
|
|
set("--measure-label-text", s.measureLabelText);
|
|
set("--measure-dot", s.measureDot);
|
|
set("--section-axis-x", s.sectionAxisX);
|
|
set("--section-axis-y", s.sectionAxisY);
|
|
set("--section-axis-z", s.sectionAxisZ);
|
|
set("--section-gizmo-diameter", s.sectionGizmoDiameter);
|
|
set("--section-gizmo-thickness", s.sectionGizmoThickness);
|
|
set("--section-plane-fill", s.sectionPlaneFill);
|
|
set("--section-plane-alpha", s.sectionPlaneAlpha);
|
|
set("--section-plane-edge", s.sectionPlaneEdge);
|
|
set("--section-plane-edge-alpha", s.sectionPlaneEdgeAlpha);
|
|
} catch (e) {
|
|
// ignore corrupted settings
|
|
}
|
|
}());
|
|
</script>
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=Manrope:wght@500;600;700&display=swap" rel="stylesheet">
|
|
<link rel="stylesheet" href="./dcViewer.css?v=20">
|
|
</head>
|
|
<body>
|
|
<header>
|
|
<button class="logo-btn" id="homeButton" title="Вернуться к старту"></button>
|
|
<div class="toolbar"></div>
|
|
<div class="hidden-controls" style="display:none">
|
|
<input type="checkbox" id="toggleEdges" checked>
|
|
<input type="checkbox" id="toggleTransparent" checked>
|
|
<input type="checkbox" id="addMode" checked>
|
|
<button id="fitView"></button>
|
|
</div>
|
|
</header>
|
|
|
|
<main class="main">
|
|
<section class="viewer-wrapper">
|
|
<canvas id="viewerCanvas"></canvas>
|
|
<canvas id="navCube"></canvas>
|
|
<div class="drop-hint" id="dropZone">
|
|
<div class="drop-center">
|
|
<div class="drop-plus">+</div>
|
|
<div class="drop-formats">
|
|
<div class="muted">IFC · BIM · LAS · LAZ</div>
|
|
<div class="muted">GLB · GLTF · OBJ · STL</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="status-bar" id="status">
|
|
<span class="dot"></span>
|
|
<span id="statusText">Готов к загрузке</span>
|
|
</div>
|
|
<div class="loader hidden" id="loader">
|
|
<div class="loader__figure"></div>
|
|
<p class="loader__label">DC</p>
|
|
</div>
|
|
</section>
|
|
|
|
<aside class="side-panel" id="viewerInspector">
|
|
<div class="inspector-panel-header">
|
|
<h3>Инспектор</h3>
|
|
<button
|
|
class="inspector-collapse-btn"
|
|
id="inspectorCollapseButton"
|
|
type="button"
|
|
title="Свернуть инспектор"
|
|
aria-label="Свернуть инспектор"
|
|
>
|
|
<svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true">
|
|
<path d="M4.25 4.25 11.75 11.75M11.75 4.25 4.25 11.75" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"></path>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
<div class="panel-section inspector-main-section">
|
|
<div id="treeContainer" class="tree"></div>
|
|
<div class="inspector-controls">
|
|
<div class="inspector-control-row inspector-control-row--half" id="displayModeControl">
|
|
<div class="inspector-control-label">Режим отображения</div>
|
|
<div class="inspector-select-host" id="displayModeSelect"></div>
|
|
</div>
|
|
<div class="inspector-control-row inspector-control-row--half" id="lightingModeControl">
|
|
<div class="inspector-control-label">Свет</div>
|
|
<div class="inspector-select-host" id="lightingModeSelect"></div>
|
|
</div>
|
|
<div class="inspector-control-row inspector-control-row--full" id="navigationAxisControl">
|
|
<div class="inspector-control-label">Ось навигации</div>
|
|
<div class="inspector-select-host" id="navigationAxisSelect"></div>
|
|
</div>
|
|
<div class="inspector-control-row inspector-control-row--full inspector-control-row--slider" id="zoomSpeedControl"></div>
|
|
</div>
|
|
</div>
|
|
<div class="panel-section" id="projectNameSection">
|
|
<h3>Название проекта</h3>
|
|
<input type="text" id="projectNameInput" class="project-name-input" placeholder="Проект" autocomplete="off">
|
|
<div class="error-text inline-error" id="projectNameError"></div>
|
|
</div>
|
|
<div class="panel-section">
|
|
<h3>Глобальные координаты</h3>
|
|
<div class="muted" style="margin-bottom:6px;">Центр выделения (мировые)</div>
|
|
<div class="transform-grid">
|
|
<label>X <input type="text" id="globalX"></label>
|
|
<label>Y <input type="text" id="globalY"></label>
|
|
<label>Z <input type="text" id="globalZ"></label>
|
|
</div>
|
|
</div>
|
|
<div class="panel-section">
|
|
<h3>Трансформации</h3>
|
|
<div class="muted" style="margin-bottom:6px;">Перемещение</div>
|
|
<div class="transform-grid">
|
|
<label>X <input type="text" id="posX"></label>
|
|
<label>Y <input type="text" id="posY"></label>
|
|
<label>Z <input type="text" id="posZ"></label>
|
|
</div>
|
|
<div class="muted" style="margin:8px 0 6px;">Вращение (deg)</div>
|
|
<div class="transform-grid">
|
|
<label>X <input type="text" id="rotX"></label>
|
|
<label>Y <input type="text" id="rotY"></label>
|
|
<label>Z <input type="text" id="rotZ"></label>
|
|
</div>
|
|
<div class="muted" style="margin:8px 0 6px;">Масштаб</div>
|
|
<div class="transform-grid">
|
|
<label>X <input type="text" id="scaleX"></label>
|
|
<label>Y <input type="text" id="scaleY"></label>
|
|
<label>Z <input type="text" id="scaleZ"></label>
|
|
</div>
|
|
<small id="transformHint" class="muted">Выберите объект в инспекторе или сцене, чтобы редактировать.</small>
|
|
</div>
|
|
<div class="panel-section">
|
|
<h3>Журнал</h3>
|
|
<div id="logList" class="log"></div>
|
|
</div>
|
|
<div class="panel-section" id="saveProjectSection">
|
|
<button class="secondary-btn model-settings-btn" id="saveModelSettingsButton" type="button">Сохранить настройки</button>
|
|
<button class="primary-btn" id="saveProjectButton" type="button">Сохранить проект</button>
|
|
<button class="secondary-btn danger-btn" id="deleteProjectButton" type="button">Удалить проект</button>
|
|
</div>
|
|
</aside>
|
|
<div class="inspector-restore-tray" id="inspectorRestoreTray" hidden>
|
|
<button class="inspector-restore-button" id="inspectorRestoreButton" type="button">Инспектор</button>
|
|
</div>
|
|
</main>
|
|
|
|
<div id="templatesPanel" class="templates-panel hidden"></div>
|
|
<div id="settingsPanel" class="settings-panel hidden"></div>
|
|
<div id="measureModalBackdrop" class="modal-backdrop hidden"></div>
|
|
<div id="measureModal" class="templates-panel measurement-modal hidden"></div>
|
|
<div id="objectModalBackdrop" class="modal-backdrop hidden"></div>
|
|
<div id="objectModal" class="templates-panel measurement-modal hidden"></div>
|
|
<div id="saveProjectBackdrop" class="modal-backdrop hidden"></div>
|
|
<div id="saveProjectModal" class="save-project-modal hidden">
|
|
<div class="modal-header">
|
|
<div class="modal-title">Сохранить проект</div>
|
|
<button class="modal-close inspector-collapse-btn" id="saveProjectClose" aria-label="Закрыть">
|
|
<svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true">
|
|
<path d="M4.25 4.25 11.75 11.75M11.75 4.25 4.25 11.75" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"></path>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
<label for="saveProjectName" class="input-label">Имя проекта</label>
|
|
<input type="text" id="saveProjectName" placeholder="Новый проект" autocomplete="off">
|
|
<div class="input-hint" id="saveProjectHint">Введите имя, чтобы сохранить проект в хранилище</div>
|
|
<div class="modal-actions">
|
|
<button class="secondary-btn" id="saveProjectCancel" type="button">Отмена</button>
|
|
<button class="primary-btn" id="saveProjectConfirm" type="button" disabled>Сохранить</button>
|
|
<button class="secondary-btn" id="saveProjectDuplicate" type="button" disabled>Сохранить как новый</button>
|
|
</div>
|
|
<div class="error-text" id="saveProjectError"></div>
|
|
</div>
|
|
<div id="deleteProjectBackdrop" class="modal-backdrop hidden"></div>
|
|
<div id="deleteProjectModal" class="save-project-modal hidden">
|
|
<div class="modal-header">
|
|
<div class="modal-title">Удалить проект</div>
|
|
<button class="modal-close inspector-collapse-btn" id="deleteProjectClose" aria-label="Закрыть">
|
|
<svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true">
|
|
<path d="M4.25 4.25 11.75 11.75M11.75 4.25 4.25 11.75" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"></path>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
<div class="input-hint">Вы уверены, что хотите удалить проект?</div>
|
|
<div class="modal-actions">
|
|
<button class="secondary-btn" id="deleteProjectCancel" type="button">Отмена</button>
|
|
<button class="primary-btn danger-btn" id="deleteProjectConfirm" type="button">Удалить</button>
|
|
</div>
|
|
<div class="error-text" id="deleteProjectError"></div>
|
|
</div>
|
|
<div id="shareModalBackdrop" class="modal-backdrop hidden"></div>
|
|
<div id="shareModal" class="save-project-modal hidden">
|
|
<div class="modal-header">
|
|
<div class="modal-title">Ссылка на модель</div>
|
|
<button class="modal-close inspector-collapse-btn" id="shareModalClose" aria-label="Закрыть">
|
|
<svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true">
|
|
<path d="M4.25 4.25 11.75 11.75M11.75 4.25 4.25 11.75" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"></path>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
<input type="text" id="shareLinkInput" readonly autocomplete="off">
|
|
<div class="modal-actions">
|
|
<button class="secondary-btn" id="shareModalCancel" type="button">Закрыть</button>
|
|
<button class="primary-btn" id="shareCopyButton" type="button" disabled>Скопировать</button>
|
|
</div>
|
|
<div class="error-text" id="shareError"></div>
|
|
</div>
|
|
|
|
<div id="bottomToolbar" class="bottom-toolbar">
|
|
<button class="tb-btn" data-action="settings" title="Настройки" aria-label="Настройки">
|
|
<svg viewBox="0 0 24 24" aria-hidden="true">
|
|
<path d="M12 15.5A3.5 3.5 0 1 0 12 8a3.5 3.5 0 0 0 0 7.5Z"></path>
|
|
<path d="M19.4 15a1.7 1.7 0 0 0 .34 1.88l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06A1.7 1.7 0 0 0 15 19.37a1.7 1.7 0 0 0-1 .56V20a2 2 0 0 1-4 0v-.08a1.7 1.7 0 0 0-1-.56 1.7 1.7 0 0 0-1.88.34l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.7 1.7 0 0 0 4.63 15a1.7 1.7 0 0 0-.56-1H4a2 2 0 0 1 0-4h.08a1.7 1.7 0 0 0 .56-1 1.7 1.7 0 0 0-.34-1.88l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.7 1.7 0 0 0 9 4.63a1.7 1.7 0 0 0 1-.56V4a2 2 0 0 1 4 0v.08a1.7 1.7 0 0 0 1 .56 1.7 1.7 0 0 0 1.88-.34l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.7 1.7 0 0 0 19.37 9c.18.38.38.73.56 1H20a2 2 0 0 1 0 4h-.08c-.18.27-.37.62-.52 1Z"></path>
|
|
</svg>
|
|
</button>
|
|
<button class="tb-btn" data-action="templates" title="Примеры" aria-label="Примеры">
|
|
<svg viewBox="0 0 24 24" aria-hidden="true">
|
|
<path d="M3.5 6.5h6l2 2h9v8.75a2.25 2.25 0 0 1-2.25 2.25H5.75A2.25 2.25 0 0 1 3.5 17.25V6.5Z"></path>
|
|
<path d="M3.5 10h17"></path>
|
|
</svg>
|
|
</button>
|
|
<button class="tb-btn" data-action="measure" title="Линейка" aria-label="Линейка">
|
|
<svg viewBox="0 0 24 24" aria-hidden="true">
|
|
<path d="M4.5 15.5 15.5 4.5l4 4-11 11-4-4Z"></path>
|
|
<path d="m8 12 2 2"></path>
|
|
<path d="m10.5 9.5 1.5 1.5"></path>
|
|
<path d="m13 7 2 2"></path>
|
|
</svg>
|
|
</button>
|
|
<button class="tb-btn" data-action="section" title="Разрез" aria-label="Разрез">
|
|
<svg viewBox="0 0 24 24" aria-hidden="true">
|
|
<path d="M4.5 7.25 12 3.5l7.5 3.75L12 11 4.5 7.25Z"></path>
|
|
<path d="M4.5 16.75 12 20.5l7.5-3.75"></path>
|
|
<path d="M12 11v9.5"></path>
|
|
<path d="M6.25 4.5 17.75 19.5"></path>
|
|
</svg>
|
|
</button>
|
|
<button class="tb-btn" data-action="projection" title="Перспектива / аксонометрия">A</button>
|
|
<button class="tb-btn" data-action="share" title="Поделиться моделью" aria-label="Поделиться моделью">
|
|
<svg viewBox="0 0 24 24" aria-hidden="true">
|
|
<path d="M12 14.5V4.75"></path>
|
|
<path d="m8.25 8.5 3.75-3.75 3.75 3.75"></path>
|
|
<path d="M7.25 11.25H6.5a2 2 0 0 0-2 2v4.25a2 2 0 0 0 2 2h11a2 2 0 0 0 2-2v-4.25a2 2 0 0 0-2-2h-.75"></path>
|
|
</svg>
|
|
</button>
|
|
<button class="tb-btn" data-action="add" title="Добавить файл" aria-label="Добавить файл">
|
|
<svg viewBox="0 0 24 24" aria-hidden="true">
|
|
<path d="M12 5v14"></path>
|
|
<path d="M5 12h14"></path>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
|
|
<div id="sectionToolbar" class="section-toolbar hidden" aria-label="Управление разрезом">
|
|
<canvas id="sectionPlanesOverview" class="section-overview" width="80" height="80"></canvas>
|
|
<button class="section-tool-btn" data-section-action="camera" type="button" title="По камере" aria-label="По камере">
|
|
<svg viewBox="0 0 24 24" aria-hidden="true">
|
|
<path d="M4.5 12s2.75-5 7.5-5 7.5 5 7.5 5-2.75 5-7.5 5-7.5-5-7.5-5Z"></path>
|
|
<path d="M12 14.75A2.75 2.75 0 1 0 12 9.25a2.75 2.75 0 0 0 0 5.5Z"></path>
|
|
</svg>
|
|
</button>
|
|
<button class="section-tool-btn section-axis-btn" data-section-action="x" type="button" title="Ось X" aria-label="Ось X">X</button>
|
|
<button class="section-tool-btn section-axis-btn" data-section-action="y" type="button" title="Ось Y" aria-label="Ось Y">Y</button>
|
|
<button class="section-tool-btn section-axis-btn" data-section-action="z" type="button" title="Ось Z" aria-label="Ось Z">Z</button>
|
|
<button class="section-tool-btn" data-section-action="flip" type="button" title="Инвертировать" aria-label="Инвертировать">
|
|
<svg viewBox="0 0 24 24" aria-hidden="true">
|
|
<path d="M7.5 7.5h8a3.5 3.5 0 0 1 0 7h-7"></path>
|
|
<path d="m10.5 4.5-3 3 3 3"></path>
|
|
<path d="m13.5 19.5 3-3-3-3"></path>
|
|
</svg>
|
|
</button>
|
|
<button class="section-tool-btn" data-section-action="clear" type="button" title="Удалить разрез" aria-label="Удалить разрез">
|
|
<svg viewBox="0 0 24 24" aria-hidden="true">
|
|
<path d="M5 7h14"></path>
|
|
<path d="M9 7V5h6v2"></path>
|
|
<path d="M8 10v8"></path>
|
|
<path d="M12 10v8"></path>
|
|
<path d="M16 10v8"></path>
|
|
<path d="M7 7l1 13h8l1-13"></path>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
|
|
<input id="fileInput" class="hidden" type="file"
|
|
accept=".xkt,.glb,.gltf,.bim,.las,.laz,.obj,.stl">
|
|
|
|
<script type="module" src="./dcViewer.js?v=25"></script>
|
|
</body>
|
|
</html>
|