diff --git a/source/viewer/domutils.js b/source/viewer/domutils.js index f7a5522..6943c27 100644 --- a/source/viewer/domutils.js +++ b/source/viewer/domutils.js @@ -136,6 +136,21 @@ OV.AddRangeSlider = function (parentElement, min, max) return slider; }; +OV.AddSelect = function (parentElement, options, selectedIndex, onChange) +{ + let select = OV.AddDomElement (parentElement, 'select', 'ov_select'); + for (let option of options) { + OV.AddDomElement (select, 'option', null, option); + } + select.selectedIndex = selectedIndex; + if (onChange) { + select.addEventListener ('change', () => { + onChange (select.selectedIndex); + }); + } + return select; +}; + OV.AddToggle = function (parentElement, className) { function UpdateStatus (toggle, status) diff --git a/website/info/index.html b/website/info/index.html index aed3474..691b0ba 100644 --- a/website/info/index.html +++ b/website/info/index.html @@ -55,13 +55,16 @@
| Format | +Format | +Extension | +Type | Import | Export | Source | Comment | |
|---|---|---|---|---|---|---|---|---|
| Wavefront | obj | text | ✓ | @@ -70,6 +73,7 @@|||||
| 3D Studio | 3ds | binary | ✓ | @@ -78,6 +82,7 @@|||||
| Stereolithography | stl | text | ✓ | @@ -93,6 +98,7 @@|||||
| Polygon File Format | ply | text | ✓ | @@ -108,21 +114,24 @@|||||
| gltf | -text (.gltf) | +glTF | +gltf | +text | ✓ | ✓ | Native | |
| binary (.glb) | +glb | +binary | ✓ | ✓ | Native | |||
| Object File Format | off | text | ✓ | @@ -138,6 +147,7 @@|||||
| Rhinoceros 3D | 3dm | binary | ✓ | @@ -146,6 +156,7 @@experimental | ||||
| Filmbox | fbx | text | ✓ | @@ -161,6 +172,7 @@experimental | ||||
| Collada | dae | text | ✓ | @@ -169,6 +181,7 @@experimental | ||||
| Virtual Reality Modeling Language | wrl | text | ✓ | @@ -177,6 +190,7 @@experimental | ||||
| 3D Manufacturing Format | 3mf | text | ✓ | @@ -185,6 +199,7 @@experimental | ||||
| Industry Foundation Classes | ifc | text | ✓ | diff --git a/website/o3dv/css/controls.css b/website/o3dv/css/controls.css index 162dd01..bb1b93b 100644 --- a/website/o3dv/css/controls.css +++ b/website/o3dv/css/controls.css @@ -128,6 +128,14 @@ input.ov_checkbox:checked border: 0px; } +select.ov_select +{ + color: var(--ov_dialog_foreground_color); + background: var(--ov_dialog_background_color); + padding: 3px; + border: 1px solid var(--ov_border_color); +} + input.ov_slider { height: 1px; diff --git a/website/o3dv/css/dialogs.css b/website/o3dv/css/dialogs.css index c6f4344..e38996e 100644 --- a/website/o3dv/css/dialogs.css +++ b/website/o3dv/css/dialogs.css @@ -96,19 +96,6 @@ div.ov_dialog div.ov_dialog_options height: 50px; } -div.ov_dialog div.ov_dialog_select -{ - margin: 20px 0px; - overflow: auto; -} - -div.ov_dialog div.ov_dialog_select div.ov_dialog_select_option -{ - width: 40px; - margin-right: 5px; - float: left; -} - div.ov_dialog div.ov_dialog_file_list { max-height: 105px; @@ -177,6 +164,24 @@ div.ov_dialog div.ov_dialog_row overflow: auto; } +div.ov_dialog div.ov_dialog_row_name +{ + width: 30%; + float: left; +} + +div.ov_dialog div.ov_dialog_row_value +{ + width: 70%; + float: left; +} + +div.ov_dialog select.ov_select +{ + width: 100%; + box-sizing: border-box; +} + div.ov_popup { color: var(--ov_dialog_foreground_color); diff --git a/website/o3dv/js/exportdialog.js b/website/o3dv/js/exportdialog.js index 659ae4f..e2ee3d7 100644 --- a/website/o3dv/js/exportdialog.js +++ b/website/o3dv/js/exportdialog.js @@ -1,190 +1,73 @@ OV.ExportType = { - Model : 1, - Image : 2 + Model : 0, + Image : 1 }; -OV.ExportDialog = class +OV.ExporterUI = class { - constructor (callbacks) + constructor (name) { - this.callbacks = callbacks; - this.model = null; - this.exportFormats = [ - { - name : 'obj', - formats : [ - { name : 'text', type: OV.ExportType.Model, format : OV.FileFormat.Text, extension : 'obj' } - ] - }, - { - name : 'stl', - formats : [ - { name : 'text', type: OV.ExportType.Model, format : OV.FileFormat.Text, extension : 'stl' }, - { name : 'binary', type: OV.ExportType.Model, format : OV.FileFormat.Binary, extension : 'stl' } - ] - }, - { - name : 'ply', - formats : [ - { name : 'text', type: OV.ExportType.Model, format : OV.FileFormat.Text, extension : 'ply' }, - { name : 'binary', type: OV.ExportType.Model, format : OV.FileFormat.Binary, extension : 'ply' } - ] - }, - { - name : 'gltf', - formats : [ - { name : 'text', type: OV.ExportType.Model, format : OV.FileFormat.Text, extension : 'gltf' }, - { name : 'binary', type: OV.ExportType.Model, format : OV.FileFormat.Binary, extension : 'glb' } - ] - }, - { - name : 'off', - formats : [ - { name : 'text', type: OV.ExportType.Model, format : OV.FileFormat.Text, extension : 'off' } - ] - }, - { - name : '3dm', - formats : [ - { name : 'binary', type: OV.ExportType.Model, format : OV.FileFormat.Binary, extension : '3dm' } - ] - }, - { - name : 'png', - formats : [ - { name : 'current size', type: OV.ExportType.Image, width : null, height : null, extension : 'png' }, - { name : 'fixed size (1920x1080)', type: OV.ExportType.Image, width : 1920, height : 1080, extension : 'png' } - ] - } - ]; - this.formatParameters = { - exportFormatButtonDivs : [], - formatSettingsDiv : null, - selectedFormat : null - }; + this.name = name; } - Show (model, viewer) + GetType () { - if (model === null) { - let messageDialog = OV.ShowMessageDialog ( - 'Export Failed', - 'Please load a model before exporting.', - null - ); - this.callbacks.onDialog (messageDialog); - return; - } - - let mainDialog = new OV.ButtonDialog (); - let contentDiv = mainDialog.Init ('Export', [ - { - name : 'Close', - subClass : 'outline', - onClick () { - mainDialog.Hide (); - } - }, - { - name : 'Export', - onClick : () => { - let selectedFormat = this.formatParameters.selectedFormat; - if (selectedFormat === null) { - return; - } - mainDialog.Hide (); - this.ExportFormat (model, viewer); - } - } - ]); - - let text = 'Select a format from the below list to export your model. Please note that the export can take several second.'; - OV.AddDiv (contentDiv, 'ov_dialog_section', text); - - let exportFormatSelect = OV.AddDiv (contentDiv, 'ov_dialog_select'); - this.formatParameters.formatSettingsDiv = OV.AddDiv (contentDiv, 'ov_dialog_section ov_dialog_options'); - for (let i = 0; i < this.exportFormats.length; i++) { - let exportFormat = this.exportFormats[i]; - let exportFormatButton = OV.AddDiv (exportFormatSelect, 'ov_button outline ov_dialog_select_option', exportFormat.name); - this.formatParameters.exportFormatButtonDivs.push (exportFormatButton); - exportFormatButton.addEventListener ('click', () => { - this.OnExportFormatSelect (i); - }); - } - this.OnExportFormatSelect (0); - - mainDialog.Show (); - this.callbacks.onDialog (mainDialog); + return null; } - OnExportFormatSelect (exportFormatIndex) + GetName () { - OV.ClearDomElement (this.formatParameters.formatSettingsDiv); - for (let i = 0; i < this.formatParameters.exportFormatButtonDivs.length; i++) { - let exportFormatButtonDiv = this.formatParameters.exportFormatButtonDivs[i]; - if (i === exportFormatIndex) { - exportFormatButtonDiv.classList.remove ('outline'); - } else { - exportFormatButtonDiv.classList.add ('outline'); - } - } - - let exportFormat = this.exportFormats[exportFormatIndex]; - for (let i = 0; i < exportFormat.formats.length; i++) { - let format = exportFormat.formats[i]; - let formatDiv = OV.AddDiv (this.formatParameters.formatSettingsDiv, 'ov_dialog_row'); - let formatInput = OV.AddRadioButton (formatDiv, 'format', format.name, format.name, () => { - this.formatParameters.selectedFormat = format; - }); - if (i === 0) { - formatInput.checked = true; - this.formatParameters.selectedFormat = format; - } - } + return this.name; } - ExportFormat (model, viewer) + GenerateParametersUI (parametersDiv) { - let selectedFormat = this.formatParameters.selectedFormat; - if (selectedFormat === null) { - return; - } - if (selectedFormat.type === OV.ExportType.Model) { - let progressDialog = new OV.ProgressDialog (); - progressDialog.Init ('Exporting Model'); - progressDialog.Show (); - OV.RunTaskAsync (() => { - let exporter = new OV.Exporter (); - exporter.Export (model, selectedFormat.format, selectedFormat.extension, { - onError : () => { + } +}; + +OV.ModelExporterUI = class extends OV.ExporterUI +{ + constructor (name, format, extension) + { + super (name); + this.format = format; + this.extension = extension; + } + + GetType () + { + return OV.ExportType.Model; + } + + ExportModel (model, onDialog) + { + let progressDialog = new OV.ProgressDialog (); + progressDialog.Init ('Exporting Model'); + progressDialog.Show (); + + OV.RunTaskAsync (() => { + let exporter = new OV.Exporter (); + exporter.Export (model, this.format, this.extension, { + onError : () => { + progressDialog.Hide (); + }, + onSuccess : (files) => { + if (files.length === 0) { progressDialog.Hide (); - }, - onSuccess : (files) => { - if (files.length === 0) { - progressDialog.Hide (); - } else if (files.length === 1) { - progressDialog.Hide (); - let file = files[0]; - OV.DownloadArrayBufferAsFile (file.GetBufferContent (), file.GetName ()); - } else if (files.length > 1) { - progressDialog.Hide (); - this.ShowExportedFiles (files); - } + } else if (files.length === 1) { + progressDialog.Hide (); + let file = files[0]; + OV.DownloadArrayBufferAsFile (file.GetBufferContent (), file.GetName ()); + } else if (files.length > 1) { + progressDialog.Hide (); + let filesDialog = this.ShowExportedFiles (files); + onDialog (filesDialog); } - }); + } }); - } else if (selectedFormat.type === OV.ExportType.Image) { - let url = null; - if (selectedFormat.width === null || selectedFormat.height === null) { - let size = viewer.GetImageSize (); - url = viewer.GetImageAsDataUrl (size.width, size.height); - } else { - url = viewer.GetImageAsDataUrl (selectedFormat.width, selectedFormat.height); - } - OV.DownloadUrlAsFile (url, 'model.' + selectedFormat.extension); - } + }); } ShowExportedFiles (files) @@ -215,6 +98,130 @@ OV.ExportDialog = class } dialog.Show (); - this.callbacks.onDialog (dialog); + return dialog; + } +}; + +OV.ImageExporterUI = class extends OV.ExporterUI +{ + constructor (name, extension) + { + super (name); + this.extension = extension; + this.sizeSelect = null; + this.sizes = [ + { name : 'Current size', value : null }, + { name : '1280 x 720', value : [1280, 720] }, + { name : '1920 x 1080', value : [1920, 1080] } + ]; + } + + GetType () + { + return OV.ExportType.Image; + } + + GenerateParametersUI (parametersDiv) + { + function AddParameterSelect (parametersDiv, name, values, defaultIndex) + { + let parameterRow = OV.AddDiv (parametersDiv, 'ov_dialog_row'); + OV.AddDiv (parameterRow, 'ov_dialog_row_name', name); + let parameterValueDiv = OV.AddDiv (parameterRow, 'ov_dialog_row_value'); + return OV.AddSelect (parameterValueDiv, values, defaultIndex); + } + + let sizeNames = this.sizes.map (size => size.name); + this.sizeSelect = AddParameterSelect (parametersDiv, 'Image size', sizeNames, 1); + } + + ExportImage (viewer) + { + let selectedSize = this.sizes[this.sizeSelect.selectedIndex]; + let url = null; + if (selectedSize.value === null) { + let size = viewer.GetImageSize (); + url = viewer.GetImageAsDataUrl (size.width, size.height); + } else { + url = viewer.GetImageAsDataUrl (selectedSize.value[0], selectedSize.value[1]); + } + OV.DownloadUrlAsFile (url, 'model.' + this.extension); + } +}; + +OV.ExportDialog = class +{ + constructor (callbacks) + { + this.callbacks = callbacks; + this.selectedExporter = null; + this.parametersDiv = null; + + this.exporters = [ + new OV.ModelExporterUI ('Wavefront (.obj)', OV.FileFormat.Text, 'obj'), + new OV.ModelExporterUI ('Stereolithography Text (.stl)', OV.FileFormat.Text, 'stl'), + new OV.ModelExporterUI ('Stereolithography Binary (.stl)', OV.FileFormat.Binary, 'stl'), + new OV.ModelExporterUI ('Polygon File Format Text (.ply)', OV.FileFormat.Text, 'ply'), + new OV.ModelExporterUI ('Polygon File Format Binary (.ply)', OV.FileFormat.Binary, 'ply'), + new OV.ModelExporterUI ('glTF Text (.gltf)', OV.FileFormat.Text, 'gltf'), + new OV.ModelExporterUI ('glTF Binary (.glb)', OV.FileFormat.Binary, 'glb'), + new OV.ModelExporterUI ('Object File Format Text (.off)', OV.FileFormat.Text, 'off'), + new OV.ModelExporterUI ('Rhinoceros 3D (.3dm)', OV.FileFormat.Binary, '3dm'), + new OV.ImageExporterUI ('PNG Image (.png)', 'png') + ]; + } + + Show (model, viewer) + { + let mainDialog = new OV.ButtonDialog (); + let contentDiv = mainDialog.Init ('Export', [ + { + name : 'Close', + subClass : 'outline', + onClick () { + mainDialog.Hide (); + } + }, + { + name : 'Export', + onClick : () => { + mainDialog.Hide (); + this.ExportFormat (model, viewer); + } + } + ]); + + let text = 'Select the format from the list below, and adjust the settings of the selected format.'; + OV.AddDiv (contentDiv, 'ov_dialog_section', text); + + let formatRow = OV.AddDiv (contentDiv, 'ov_dialog_row'); + this.parametersDiv = OV.AddDiv (contentDiv); + let formatNames = this.exporters.map (exporter => exporter.GetName ()); + let defaultFormatIndex = 6; + OV.AddSelect (formatRow, formatNames, defaultFormatIndex, (selectedIndex) => { + this.OnFormatSelected (selectedIndex); + }); + this.OnFormatSelected (defaultFormatIndex); + + mainDialog.Show (); + this.callbacks.onDialog (mainDialog); + } + + OnFormatSelected (selectedIndex) + { + OV.ClearDomElement (this.parametersDiv); + this.selectedExporter = this.exporters[selectedIndex]; + this.selectedExporter.GenerateParametersUI (this.parametersDiv); + } + + ExportFormat (model, viewer) + { + if (this.selectedExporter.GetType () === OV.ExportType.Model) { + this.selectedExporter.ExportModel (model, (filesDialog) => { + this.callbacks.onDialog (filesDialog); + }); + } else if (this.selectedExporter.GetType () === OV.ExportType.Image) { + this.selectedExporter.ExportImage (viewer); + } } };|||||