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');