From 9405aff3dd7889287a7d52801bd6ae8994cdd61d Mon Sep 17 00:00:00 2001 From: kovacsv Date: Sat, 24 Jul 2021 13:49:22 +0200 Subject: [PATCH] Make mesh visibility options available from context menu #102 --- source/viewer/navigation.js | 36 ++++++++- source/viewer/viewer.js | 5 ++ website/o3dv/dialogs.js | 10 ++- website/o3dv/exportdialog.js | 3 +- website/o3dv/featureset.js | 3 +- website/o3dv/loader.js | 3 +- website/o3dv/modal.js | 139 +++++++++++++++++------------------ website/o3dv/navigator.js | 40 +++++++--- website/o3dv/website.js | 63 ++++++++++++++++ 9 files changed, 214 insertions(+), 88 deletions(-) diff --git a/source/viewer/navigation.js b/source/viewer/navigation.js index 2707f37..0c476f7 100644 --- a/source/viewer/navigation.js +++ b/source/viewer/navigation.js @@ -245,6 +245,7 @@ OV.Navigation = class this.onUpdate = null; this.onClick = null; + this.onContext = null; if (this.canvas.addEventListener) { this.canvas.addEventListener ('mousedown', this.OnMouseDown.bind (this)); @@ -271,6 +272,11 @@ OV.Navigation = class this.onClick = onClick; } + SetContextMenuHandler (onContext) + { + this.onContext = onContext; + } + IsFixUpVector () { return this.fixUpVector; @@ -375,12 +381,15 @@ OV.Navigation = class OnMouseDown (ev) { ev.preventDefault (); + this.mouse.Down (this.canvas, ev); this.clickDetector.Down (ev); } OnMouseMove (ev) { + ev.preventDefault (); + this.mouse.Move (this.canvas, ev); this.clickDetector.Move (); if (!this.mouse.IsButtonDown ()) { @@ -403,6 +412,8 @@ OV.Navigation = class OnMouseUp (ev) { + ev.preventDefault (); + this.mouse.Up (this.canvas, ev); this.clickDetector.Up (ev); if (this.clickDetector.IsClick ()) { @@ -413,6 +424,8 @@ OV.Navigation = class OnMouseLeave (ev) { + ev.preventDefault (); + this.mouse.Leave (this.canvas, ev); this.clickDetector.Leave (ev); } @@ -420,12 +433,14 @@ OV.Navigation = class OnTouchStart (ev) { ev.preventDefault (); + this.touch.Start (this.canvas, ev); } OnTouchMove (ev) { ev.preventDefault (); + this.touch.Move (this.canvas, ev); if (!this.touch.IsFingerDown ()) { return; @@ -450,12 +465,14 @@ OV.Navigation = class OnTouchEnd (ev) { ev.preventDefault (); + this.touch.End (this.canvas, ev); } OnMouseWheel (ev) { ev.preventDefault (); + let params = ev || window.event; let delta = -params.deltaY / 40; @@ -471,6 +488,11 @@ OV.Navigation = class OnContextMenu (ev) { ev.preventDefault (); + + this.clickDetector.Up (ev); + if (this.clickDetector.IsClick ()) { + this.Context (ev.clientX, ev.clientY); + } } Orbit (angleX, angleY) @@ -541,5 +563,17 @@ OV.Navigation = class let mouseCoords = OV.GetClientCoordinates (this.canvas, clientX, clientY); this.onClick (button, isCtrlPressed, mouseCoords); } - } + } + + Context (clientX, clientY) + { + if (this.onContext) { + let globalCoords = { + x : clientX, + y : clientY + }; + let localCoords = OV.GetClientCoordinates (this.canvas, clientX, clientY); + this.onContext (globalCoords, localCoords); + } + } }; diff --git a/source/viewer/viewer.js b/source/viewer/viewer.js index 60550eb..d3cbb3d 100644 --- a/source/viewer/viewer.js +++ b/source/viewer/viewer.js @@ -180,6 +180,11 @@ OV.Viewer = class this.navigation.SetClickHandler (onClick); } + SetContextMenuHandler (onContext) + { + this.navigation.SetContextMenuHandler (onContext); + } + SetBackgroundColor (color) { let hexColor = '#' + OV.ColorToHexString (color); diff --git a/website/o3dv/dialogs.js b/website/o3dv/dialogs.js index bbca050..eecffbe 100644 --- a/website/o3dv/dialogs.js +++ b/website/o3dv/dialogs.js @@ -17,10 +17,16 @@ OV.ShowMessageDialog = function (title, message, subMessage) return dialog; }; -OV.ShowListPopup = function (button, items, callbacks) +OV.ShowListPopup = function (items, callbacks) { + if (items.length === 0) { + return null; + } + let popup = new OV.ListPopup (); - popup.Init (button); + popup.Init (() => { + return callbacks.calculatePosition (popup.GetContentDiv ()); + }); for (let i = 0; i < items.length; i++) { let item = items[i]; popup.AddListItem (item, { diff --git a/website/o3dv/exportdialog.js b/website/o3dv/exportdialog.js index 60f4d72..6ad391b 100644 --- a/website/o3dv/exportdialog.js +++ b/website/o3dv/exportdialog.js @@ -157,7 +157,8 @@ OV.ExportDialog = class if (selectedFormat.type === OV.ExportType.Model) { let progressDialog = new OV.ProgressDialog (); - progressDialog.Show ('Exporting Model'); + progressDialog.Init ('Exporting Model'); + progressDialog.Show (); OV.RunTaskAsync (() => { let exporter = new OV.Exporter (); exporter.AddExporter (new OV.Exporter3dm ()); diff --git a/website/o3dv/featureset.js b/website/o3dv/featureset.js index 01b03a5..c0cb65c 100644 --- a/website/o3dv/featureset.js +++ b/website/o3dv/featureset.js @@ -1,4 +1,5 @@ OV.FeatureSet = { - SettingsPanel : false + SettingsPanel : false, + ContextMenu : false }; diff --git a/website/o3dv/loader.js b/website/o3dv/loader.js index 79d5ec9..830b040 100644 --- a/website/o3dv/loader.js +++ b/website/o3dv/loader.js @@ -38,7 +38,8 @@ OV.InitModelLoader = function (modelLoader, callbacks) CloseDialogIfOpen (errorDialog); callbacks.onStart (); progressDialog = new OV.ProgressDialog (); - progressDialog.Show ('Loading Model'); + progressDialog.Init ('Loading Model'); + progressDialog.Show (); }, onImportStart : () => { progressDialog.SetText ('Importing Model'); diff --git a/website/o3dv/modal.js b/website/o3dv/modal.js index ad50cd2..5469b56 100644 --- a/website/o3dv/modal.js +++ b/website/o3dv/modal.js @@ -5,7 +5,7 @@ OV.Modal = class this.modalDiv = $('
').css ('position', 'absolute'); this.overlayDiv = null; this.resizeHandler = null; - this.customResizeHandler = null; + this.positionCalculator = null; this.closeHandler = null; this.isOpen = false; this.closeable = true; @@ -21,9 +21,9 @@ OV.Modal = class this.closeable = closeable; } - SetCustomResizeHandler (customResizeHandler) + SetPositionCalculator (positionCalculator) { - this.customResizeHandler = customResizeHandler; + this.positionCalculator = positionCalculator; } SetCloseHandler (closeHandler) @@ -43,6 +43,11 @@ OV.Modal = class windowObj.bind ('resize', this.resizeHandler); if (this.closeable) { this.overlayDiv.click ((ev) => { + ev.preventDefault (); + this.Close (); + }); + this.overlayDiv.contextmenu ((ev) => { + ev.preventDefault (); this.Close (); }); } @@ -86,33 +91,69 @@ OV.Modal = class left : 0, top : 0 }); - if (this.customResizeHandler) { - this.customResizeHandler (this.modalDiv); - } else { - this.modalDiv.offset ({ - left : (windowWidth - this.modalDiv.outerWidth ()) / 2, - top : (windowHeight - this.modalDiv.outerHeight ()) / 3 - }); + let positionX = (windowWidth - this.modalDiv.outerWidth ()) / 2; + let positionY = (windowHeight - this.modalDiv.outerHeight ()) / 3; + if (this.positionCalculator !== null) { + let calculatedPosition = this.positionCalculator (); + positionX = calculatedPosition.x; + positionY = calculatedPosition.y; } + this.modalDiv.offset ({ + left : positionX, + top : positionY + }); } }; -OV.ProgressDialog = class +OV.Dialog = class { constructor () { this.modal = new OV.Modal (); + } + + GetContentDiv () + { + return this.modal.GetContentDiv (); + } + + SetCloseable (closeable) + { + this.modal.SetCloseable (closeable); + } + + SetCloseHandler (closeHandler) + { + this.modal.SetCloseHandler (closeHandler); + } + + SetPositionCalculator (positionCalculator) + { + this.modal.SetPositionCalculator (positionCalculator); + } + + Show () + { + this.modal.Open (); + } + + Hide () + { + this.modal.Close (); + } +}; + +OV.ProgressDialog = class extends OV.Dialog +{ + constructor () + { + super (); this.modal.SetCloseable (false); this.imageDiv = null; this.textDiv = null; } - SetText (text) - { - this.textDiv.html (text); - } - - Show (text) + Init (text) { let contentDiv = this.modal.GetContentDiv (); contentDiv.addClass ('ov_progress'); @@ -121,20 +162,19 @@ OV.ProgressDialog = class this.textDiv = $('
').addClass ('ov_progress_text').appendTo (contentDiv); this.SetText (text); - this.modal.Open (); } - Hide () + SetText (text) { - this.modal.Close (); + this.textDiv.html (text); } }; -OV.ButtonDialog = class +OV.ButtonDialog = class extends OV.Dialog { constructor () { - this.modal = new OV.Modal (); + super (); } Init (title, buttons) @@ -163,65 +203,22 @@ OV.ButtonDialog = class return dialogContentDiv; } - - SetCloseable (closeable) - { - this.modal.SetCloseable (closeable); - } - - SetCloseHandler (closeHandler) - { - this.modal.SetCloseHandler (closeHandler); - } - - Show () - { - this.modal.Open (); - } - - Hide () - { - this.modal.Close (); - } }; -OV.PopupDialog = class +OV.PopupDialog = class extends OV.Dialog { constructor () { - this.modal = new OV.Modal (); + super (); } - Init (parentItem) + Init (positionCalculator) { let contentDiv = this.modal.GetContentDiv (); contentDiv.addClass ('ov_popup'); - this.modal.SetCustomResizeHandler ((modalDiv) => { - let offset = parentItem.offset (); - let left = offset.left + parentItem.outerWidth (false); - let bottom = offset.top + parentItem.outerHeight (false); - modalDiv.offset ({ - left : left, - top : bottom - modalDiv.outerHeight (true) - }); - }); + this.modal.SetPositionCalculator (positionCalculator); return contentDiv; } - - SetCustomResizeHandler (customResizeHandler) - { - this.modal.SetCustomResizeHandler (customResizeHandler); - } - - Show () - { - this.modal.Open (); - } - - Hide () - { - this.modal.Close (); - } }; OV.ListPopup = class extends OV.PopupDialog @@ -232,9 +229,9 @@ OV.ListPopup = class extends OV.PopupDialog this.listDiv = null; } - Init (parentItem) + Init (positionCalculator) { - let contentDiv = super.Init (parentItem); + let contentDiv = super.Init (positionCalculator); this.listDiv = $('
').addClass ('ov_popup_list').addClass ('ov_thin_scrollbar').appendTo (contentDiv); return contentDiv; } diff --git a/website/o3dv/navigator.js b/website/o3dv/navigator.js index ebd5c27..b8c3f80 100644 --- a/website/o3dv/navigator.js +++ b/website/o3dv/navigator.js @@ -41,15 +41,22 @@ OV.NavigatorInfoPanel = class if (meshItems.length === 0) { return; } - this.popup = OV.ShowListPopup (button, meshItems, { - onHoverStart : function (index) { + this.popup = OV.ShowListPopup (meshItems, { + calculatePosition : (contentDiv) => { + let offset = button.offset (); + return { + x : offset.left + button.outerWidth (false), + y : offset.top + button.outerHeight (false) - contentDiv.outerHeight (true) + }; + }, + onHoverStart : (index) => { const meshItem = usedByMeshes[index]; callbacks.onMeshHover (meshItem.index); }, - onHoverStop : function (index) { + onHoverStop : (index) => { callbacks.onMeshHover (null); }, - onClick : function (index) { + onClick : (index) => { const meshItem = usedByMeshes[index]; callbacks.onMeshSelect (meshItem.index); } @@ -75,13 +82,20 @@ OV.NavigatorInfoPanel = class let materialsText = 'Materials (' + materialItems.length + ')'; this.CreateButton (this.parentDiv, materialsText, (button) => { - this.popup = OV.ShowListPopup (button, materialItems, { + this.popup = OV.ShowListPopup (materialItems, { + calculatePosition : (contentDiv) => { + let offset = button.offset (); + return { + x : offset.left + button.outerWidth (false), + y : offset.top + button.outerHeight (false) - contentDiv.outerHeight (true) + }; + }, onClick : (index) => { let usedMaterial = usedMaterials[index]; callbacks.onMaterialSelect (usedMaterial.index); } }); - }); + }); } CreateButton (parentDiv, buttonText, onClick) @@ -214,16 +228,20 @@ OV.Navigator = class return meshData.IsVisible (); } - IsolateMesh (meshIndex) + IsMeshIsolated (meshIndex) { - let isIsolated = true; for (let i = 0; i < this.modelData.MeshCount (); i++) { let meshData = this.modelData.GetMeshData (i); if (i !== meshIndex && meshData.IsVisible ()) { - isIsolated = false; - break; + return false; } } + return true; + } + + IsolateMesh (meshIndex) + { + let isIsolated = this.IsMeshIsolated (meshIndex); for (let i = 0; i < this.modelData.MeshCount (); i++) { let meshData = this.modelData.GetMeshData (i); if (i === meshIndex || isIsolated) { @@ -248,7 +266,7 @@ OV.Navigator = class return this.tempSelectedMeshIndex; } if (this.selection === null || this.selection.type !== OV.SelectionType.Mesh) { - return -1; + return null; } return this.selection.index; } diff --git a/website/o3dv/website.js b/website/o3dv/website.js index 487aad5..1a305fd 100644 --- a/website/o3dv/website.js +++ b/website/o3dv/website.js @@ -54,6 +54,7 @@ OV.Website = class this.InitCookieConsent (); this.viewer.SetClickHandler (this.OnModelClicked.bind (this)); + this.viewer.SetContextMenuHandler (this.OnModelContextMenu.bind (this)); this.Resize (); this.hashHandler.SetEventListener (this.OnHashChange.bind (this)); @@ -141,6 +142,68 @@ OV.Website = class } } + OnModelContextMenu (globalMouseCoordinates, mouseCoordinates) + { + if (!OV.FeatureSet.ContextMenu) { + return; + } + + let meshUserData = this.viewer.GetMeshUserDataUnderMouse (mouseCoordinates); + let items = []; + if (meshUserData === null) { + items = [ + { + name : 'Fit model to window', + onClick : () => { + this.FitModelToWindow (false); + } + } + ]; + } else { + let meshIndex = meshUserData.originalMeshIndex; + let isSelectedMesh = (meshIndex === this.navigator.GetSelectedMeshIndex ()); + let isMeshIsolated = this.navigator.IsMeshIsolated (meshIndex); + items = [ + { + name : isSelectedMesh ? 'Deselect mesh' : 'Select mesh', + onClick : () => { + this.navigator.SetSelection (new OV.Selection (OV.SelectionType.Mesh, meshIndex)); + } + }, + { + name : 'Hide mesh', + onClick : () => { + this.navigator.ToggleMeshVisibility (meshIndex); + } + }, + { + name : 'Fit to window', + onClick : () => { + this.navigator.FitMeshToWindow (meshIndex); + } + }, + { + name : isMeshIsolated ? 'Remove mesh isolation' : 'Isolate mesh', + onClick : () => { + this.navigator.IsolateMesh (meshIndex); + } + } + ]; + } + this.dialog = OV.ShowListPopup (items, { + calculatePosition : (contentDiv) => { + return { + x : globalMouseCoordinates.x, + y : globalMouseCoordinates.y + }; + }, + onClick : (index) => { + let clickedItem = items[index]; + clickedItem.onClick (); + } + }); + } + OpenFileBrowserDialog () { this.parameters.fileInput.trigger ('click');