Размещение собственных пометок (markups) по dbId в Forge Viewer
Размещение собственных пометок (markups) по dbId в Forge Viewer
Существуют разные подходы для реализации markups (пометок) в Forge Viewer. Например:
- 3D Markup с иконками и карточками с информацией, Michale Beale
- Pushpin Markup с использованием SVG, Xiaodong Liang
В обеих статьях используются сходные подходы: поверх модели размещается DIV, расположение которого подстраивается при изменении позиции камеры. Умно ведь? Этот подход вполне рабочий, воспользуемся им. Но для него требуется знать точное расположение (XYZ) markup-а, что которое иногда достаточно сложно определить. Petr Broz реализовал это с использованием id и id фрагмента. Сами markup-ы он показывает с помощью изображений:
Petr практически реализовал именно то, что я хотел, но мне бы также хотелось упростить его подход для моего собственного примера, чтобы просто передать dbId, текст и картинку, которую нужно показать в markup-е, т.е. что-то вроде:
- viewer.loadExtension('IconMarkupExtension', {
- icons: [
- { dbId: 987,label: '300C',css:'fas fa-thermometer' }
- ]
- });
Расширение использует dbId для того, чтобы найти центр 3D геометрии, т.е., например дверь из модели Revit может состоять из нескольких частей, поэтому мы используем метод THREE.Box3.union для объединения bounding box-ов и затем получаем центр. Свойство label - это просто текст. CSS содержит иконку markup-а и дополнительные стили, такие как фон и граница. Можно, например, использовать иконки из векторных шрифты, которые очень здорово смотрятся.
Ну и немножко ещё: нам нужна кнопка с иконкой на панели инструментов, возможность показать markup без текста, просто с иконкой.
Таким образом, чтобы получить что-то подобное GIF-ке в начале статьи нам нужно загрузить расширение для Forge Viewer примерно следующим образом:
- viewer.loadExtension('IconMarkupExtension', {
- button: {
- icon: 'fa-thermometer-half',
- tooltip: 'Show Temperature'
- },
- icons: [
- { dbId: 3944, label: '300°C', css: 'fas fa-thermometer-full' },
- { dbId: 721, label: '356°C', css: 'fas fa-thermometer-full' },
- { dbId: 10312, label: '450°C', css: 'fas fa-thermometer-empty' },
- { dbId: 563, css: 'fas fa-exclamation-triangle' },
- ],
- onClick: (id) => {
- viewers.select(id);
- viewers.utilities.fitToView();
- switch (id){
- case 563:
- alert('Sensor offline');
- }
- }
- })
Ниже приведен код самого расширения.
Посмотреть online: https://forgeplant.herokuapp.com/
- class IconMarkupExtension extends Autodesk.Viewing.Extension {
- constructor(viewer, options) {
- super(viewer, options);
- this._group = null;
- this._button = null;
- this._icons = options.icons || [];
- }
- load() {
- const updateIconsCallback = () => {
- if (this._enabled) {
- this.updateIcons();
- }
- };
- this.viewer.addEventListener(Autodesk.Viewing.CAMERA_CHANGE_EVENT, updateIconsCallback);
- this.viewer.addEventListener(Autodesk.Viewing.ISOLATE_EVENT, updateIconsCallback);
- this.viewer.addEventListener(Autodesk.Viewing.HIDE_EVENT, updateIconsCallback);
- this.viewer.addEventListener(Autodesk.Viewing.SHOW_EVENT, updateIconsCallback);
- return true;
- }
- unload() {
- // Clean our UI elements if we added any
- if (this._group) {
- this._group.removeControl(this._button);
- if (this._group.getNumberOfControls() === 0) {
- this.viewer.toolbar.removeControl(this._group);
- }
- }
- return true;
- }
- onToolbarCreated() {
- // Create a new toolbar group if it doesn't exist
- this._group = this.viewer.toolbar.getControl('customExtensions');
- if (!this._group) {
- this._group = new Autodesk.Viewing.UI.ControlGroup('customExtensions');
- this.viewer.toolbar.addControl(this._group);
- }
- // Add a new button to the toolbar group
- this._button = new Autodesk.Viewing.UI.Button('IconExtension');
- this._button.onClick = (ev) => {
- this._enabled = !this._enabled;
- this.showIcons(this._enabled);
- this._button.setState(this._enabled ? 0 : 1);
- };
- this._button.setToolTip(this.options.button.tooltip);
- this._button.container.children[0].classList.add('fas', this.options.button.icon);
- this._group.addControl(this._button);
- }
- showIcons(show) {
- const $viewer = $('#' + this.viewer.clientContainer.id + ' div.adsk-viewing-viewer');
- // remove previous...
- $('#' + this.viewer.clientContainer.id + ' div.adsk-viewing-viewer label.markup').remove();
- if (!show) return;
- // do we have anything to show?
- if (this._icons === undefined || this.icons === null) return;
- // do we have access to the instance tree?
- const tree = this.viewer.model.getInstanceTree();
- if (tree === undefined) { console.log('Loading tree...'); return; }
- const onClick = (e) => {
- if (this.options.onClick)
- this.options.onClick($(e.currentTarget).data('id'));
- };
- this._frags = {}
- for (var i = 0; i < this._icons.length; i++) {
- // we need to collect all the fragIds for a given dbId
- const icon = this._icons[i];
- this._frags['dbId' + icon.dbId] = []
- // create the label for the dbId
- const $label = $('
- <label class="markup update" data-id="${icon.dbId}">
- <span class="${icon.css}"> ${icon.label || ''}</span>
- </label>
- ');
- $label.css('display', this.viewer.isNodeVisible(icon.dbId) ? 'block' : 'none');
- $label.on('click', onClick);
- $viewer.append($label);
- // now collect the fragIds
- const _this = this;
- tree.enumNodeFragments(icon.dbId, function (fragId) {
- _this._frags['dbId' + icon.dbId].push(fragId);
- _this.updateIcons(); // re-position of each fragId found
- });
- }
- }
- getModifiedWorldBoundingBox(dbId) {
- var fragList = this.viewer.model.getFragmentList();
- const nodebBox = new THREE.Box3()
- // for each fragId on the list, get the bounding box
- for (const fragId of this._frags['dbId' + dbId]) {
- const fragbBox = new THREE.Box3();
- fragList.getWorldBounds(fragId, fragbBox);
- nodebBox.union(fragbBox); // create a unifed bounding box
- }
- return nodebBox
- }
- updateIcons() {
- for (const label of $('#' + this.viewer.clientContainer.id + ' div.adsk-viewing-viewer .update')) {
- const $label = $(label);
- const id = $label.data('id');
- // get the center of the dbId (based on its fragIds bounding boxes)
- const pos = this.viewer.worldToClient(this.getModifiedWorldBoundingBox(id).center());
- // position the label center to it
- $label.css('left', Math.floor(pos.x - $label[0].offsetWidth / 2) + 'px');
- $label.css('top', Math.floor(pos.y - $label[0].offsetHeight / 2) + 'px');
- $label.css('display', this.viewer.isNodeVisible(id) ? 'block' : 'none');
- }
- }
- }
- Autodesk.Viewing.theExtensionManager.registerExtension('IconMarkupExtension', IconMarkupExtension);
Источник: https://forge.autodesk.com/blog/placing-custom-markup-dbid
Опубликовано 28.02.2020