===============
Exporting Files
===============

.. only:: not spatial 

	|HPSNOW| can export a few different types of :doc:`file formats </general/supported_file_formats>`. Regardless of the file type, a similar pattern is employed. For this section of the Programming Guide, we'll cover HSF file export (the native file format for |HPSNOW|) as well 
	as export to HTML. See the Programming Guide sections on :doc:`Exchange integration <0906_exchange_integration>` and :ref:`Parasolid integration <prog_guide/0907_parasolid:Exporting>` for information on exporting via HOOPS Exchange and Parasolid, respectively.

.. only:: spatial
  
	|HPSNOW| can export a few different types of :doc:`file formats </general/supported_file_formats>`. Regardless of the file type, a similar pattern is employed. For this section of the Programming Guide, we'll cover HSF file export (the native file format for |HPSNOW|) as well as export to HTML.


.. _hsf:

HSF Export
==========

When exporting data, HSFs can be written from any arbitrary segment. This means that the entire scene can be saved by writing from the window segment. Alternatively, a portion of the scene can be written if only a piece of a model needs to be saved. Note that when saving partial scenes, attributes inherited by the branch root are **not** saved with the HSF. If you wish to save attributes into the HSF, they must be explicitly set within the branch you are exporting.

The basic method for exporting a model to HSF is shown below:

.. tabs::

	.. group-tab:: C++
	
		.. literalinclude:: ../../../internals/tests/docs/source/cpp/00700_load_hsf.cpp
		   :start-after: //! [exporting]
		   :end-before: //! [exporting]
		   
	.. group-tab:: C#

		.. literalinclude:: ../../../internals/tests/docs/source/cs/00700_load_hsf.cs
		   :start-after: //! [exporting]
		   :end-before: //! [exporting]

The ``HPS::Stream::ExportOptionsKit`` has a variety of lossy compression options. Color compression is enabled in the snippet above as an example of how to specify compression.

**IMPORTANT:** All file exports are done synchronously, except for HSF stream files. Do not try to export on a separate thread while the scene graph can be modified, because the scene graph must be in a write-locked state during the export.

For HSF files, the ``HPS::Stream`` export operation will happen on a separate thread. Do not modify the subtree while the export is occurring. When exporting stream files, you should use a ``HPS::Stream::ExportNotifier`` to control the export operation. For example, you can call:

* ``Status()``, to receive the current export status and check the progress.
* ``Wait()``, to block the current thread until the export operation is finished.
* ``Cancel()``, to cancel the export operation.

If you cancel a file export, you are responsible for cleaning up the partially exported file on disk.

Several :ref:`export options <prog_guide/appendix_default_attrbute_values:stream_io>` apply. Our HSF writer offers compression options which can be enabled or disabled for certain situations. All options are disabled by default.

**Vertex compression**

This involves encoding the locations of shell vertices, providing reduction in file size in exchange for loss of coordinate precision and slightly lower visual quality. The degradation in visual quality is highly dependent on the topology of the shell, as well as how the normals information is being exported.

Vertex compression can be enabled by setting the ``HPS::Stream::ExportOptionsKit::SetVertexCompression`` parameter.

.. admonition:: Note

	Vertex compression can cause precision issues when writing HSF files containing textures. If you are experiencing this problem, or think you may experience it, consider turning off position and parameter compression any time you are writing an HSF while using a texture.

	A less draconian approach would depend on the type of texture(s) associated with the HSF. High-contrast, distinct edges (checkerboard, stripes) favor disabling compression, while general photographic textures (landscapes, materials like wood grain) can get away with minor compression-based distortion.

--------------------------------------

**Normals compression**

Normals compression involves encoding vertex normals, providing reduction in file size in exchange for lower visual quality. Again, the degradation in visual quality is highly dependent on the topology of the shell, as well as how the normals information is being exported. HOOPS/Stream transmits compressed normals for vertices that have been compressed, or if a normal has been explicitly set on a vertex. Surfaces that had gradual curvature over a highly tessellated region can look faceted due to the aliasing of the compressed normals. The function ``HStreamFileToolkit::SetNumNormalBits`` allows the developer to greatly reduce or effectively remove such aliasing at the cost of transmitting more data per normal. The default is 10.

Normals compression can be enabled by setting the ``HPS::Stream::ExportOptionsKit::SetNormalsCompression`` parameter. 

--------------------------------------

**Parameter compression**

This involves encoding the vertex parameters (texture coordinates). Compression will reduce the file size but this could also result in the loss of precision in textures mapping.
	  
Parameter compression can be disabled by setting the ``HPS::Stream::ExportOptionsKit::SetParameterCompression`` parameter.  

--------------------------------------

**Connectivity compression**

This compresses 'shell' connectivity information. This compression technique can provide compelling reductions in files sizes for datasets that contain many 'shell' primitives, but can also be a computationally intensive algorithm depending on the size of individual shells. Developers will need to decide for themselves whether the reduced file size is worth the extra computation time.

Additionally, depending on the topology of the shell, the algorithm may provide limited compression benefit or have to 'punt' after performing substantial work, thereby providing little to no additional file size reduction in exchange for extra computation time. Therefore, developers should do some experimentation with their specific class of datasets to see if the option is buying them any reduction in file size. If files sizes for typical files are the same both with and without the option set, then this compression option should be disabled when exporting an HSF file. Some specific examples of when the algorithm will punt or perform poorly are shells that contain many backwards faces (which also impact rendering performance and should generally be avoided anyway!), or contain certain complex combinations of 'handles' (a teapot or a torus each have one handle) and holes (for example, a flat plate that has a hole in the middle). In general, the connectivity compression algorithm will perform well with most of these cases, but developers should still take some time to investigate the [extra export time vs. file-size reduction] of their datasets with and without this option enabled.

Connectivity compression can be enabled by setting the ``HPS::Stream::ExportOptionsKit::SetConnectivityCompression`` parameter.  



Exporting to a Buffer
---------------------

In addition to saving to an HSF, the ``HPS::Stream`` class supports exporting HSF data to a buffer.  This is implemented as an overload to ``HPS::Stream::File::Export``.

.. tabs::

	.. group-tab:: C++
	
		.. literalinclude:: ../../../internals/tests/docs/source/cpp/00700_load_hsf.cpp
		   :start-after: //! [stream_buffer_export]
		   :end-before: //! [stream_buffer_export]
		   
	.. group-tab:: C#

		.. literalinclude:: ../../../internals/tests/docs/source/cs/00700_load_hsf.cs
		   :start-after: //! [stream_buffer_export]
		   :end-before: //! [stream_buffer_export]
		   

.. _export_events:

Export Events
=============

When writing an HSF file, |HPSNOW| can notify you of its progress through the use of ``HPS::Stream::SegmentExportEvent`` and ``HPS::Stream::GeometryExportEvent``. You receive these events by setting an ``HPS::Stream::ExportEventHandler`` for the ``HPS::Stream::ExportEvent`` type (from ``HPS::Object::ClassID``) on your ``HPS::Stream::ExportOptionsKit``. The ``HPS::Stream::ExportEventHandler`` class has a virtual function, Handle, that |HPSNOW| will call with the ``HPS::Stream::ExportEvent`` data when it is about to write the provided data. You should override the Handle function to inspect the data to suit your needs.

The following code sample defines a custom ``ExportEventHandler`` class, which will export some non-db user data if a specific segment-name is encountered in the HSF file:

.. tabs::

	.. group-tab:: C++
	
		.. literalinclude:: ../../../internals/tests/docs/source/cpp/00700_stream_events.cpp
		   :start-after: //! [define_stream_export_handler]
		   :end-before: //! [define_stream_export_handler]
		   
	.. group-tab:: C#

		.. literalinclude:: ../../../internals/tests/docs/source/cs/00700_stream_events.cs
		   :start-after: //! [define_stream_export_handler]
		   :end-before: //! [define_stream_export_handler]

We then set the custom handler on the ``HPS::Stream::ExportOptionsKit`` for the segment-export event, prior to exporting the HSF File:

.. tabs::

	.. group-tab:: C++
	
		.. literalinclude:: ../../../internals/tests/docs/source/cpp/00700_stream_events.cpp
		   :start-after: //! [set_stream_export_handler]
		   :end-before: //! [set_stream_export_handler]
		   
	.. group-tab:: C#

		.. literalinclude:: ../../../internals/tests/docs/source/cs/00700_stream_events.cs
		   :start-after: //! [set_stream_export_handler]
		   :end-before: //! [set_stream_export_handler]
		   
		   
User Data
---------

User data can be exported to an HSF file in two different ways. One form is user data that is specified within the database and is associated with a segment or geometry (see :ref:`User data <prog_guide/0101_database:User data>`), and will be automatically exported along with the database. The second form is user-data that is not associated with the database and can be exported by utilizing the ``HPS::Stream::ExportEvent.non_db_user_data``, where you may copy arbitrary data. 


.. _html:

HTML Export
===========

Please note, HTML Export is only available for 64-bit Windows applications.

To export your model to HTML, first import it into |HPSNOW| and construct a SprocketPath object using your ``Canvas``, ``Layout``, ``Model``, and 
``View`` objects:

.. tabs::

	.. group-tab:: C++
	
		.. literalinclude:: ../../../internals/tests/docs/source/cpp/00700_sprocket_html_export.cpp
		   :start-after: //! [sprocket_html_export]
		   :end-before: //! [sprocket_html_export]
		   
	.. group-tab:: C#

		.. literalinclude:: ../../../internals/tests/docs/source/cs/00700_sprocket_html_export.cs
		   :start-after: //! [sprocket_html_export]
		   :end-before: //! [sprocket_html_export]
		   
Call ``HPS::HTML::File::Export()`` with the following parameters: the keypath of your ``SprocketPath``, the output file path for your HTML file, and the HTML template file into which the exported model will be embedded.

.. only:: not spatial

	For your convenience, there are two template files in the |HPSNOW| package, both of which are based on HOOPS Communicator's StreamCache technology: *HOOPSCommunicatorTemplate.html* and *HOOPSCommunicatorMinimalTemplate.html*. The standard template includes advanced UI features, whereas the minimal template only includes the basic functionality for viewing a model.

For simplicity, we'll discuss how to modify the minimal HTML template below.


.. _template:

Modifying the HTML Template
---------------------------

The HTML export creates a totally self-contained HTML file for viewing a model. This means that all the markup, styling, javascript, images, and model data are contained in a single file which works out of the box in any modern browser with no internet connection required.


About the HOOPS Communicator Template File
------------------------------------------

The template file is a pre-processed HTML file with all dependencies inlined. Binary dependencies such as images and model data are base64 encoded into the file as well. All this preprocessing is done when the HOOPS Communicator package is built. Browsers can automatically base64 decode binary files such as images. Additional javascript code must be used to base64 decode the model data. This code is included in the standard template and can be leveraged by customers if needed.


Creating a Custom Template File
-------------------------------

Aside from ensuring that the contents of *hoops_web_viewer.js* are included in the page, there are four required steps to create a custom template file. (To see a sample template, please see the :ref:`sample HTML template <prog_guide/0902_exporting_files:Sample HTML template>` below.)

**Step 1: Insert the Data Replacement String**

Include the replacement string in the file::

	<!-- SC_MODEL_DATA -->

The system searches the template file for this string and replaces it with base64 binary encoded data. Ideally this string should be saved in a variable:: 

	var SC_MODEL_DATA = "<!-- SC_MODEL_DATA -->";


**Step 2: Add a Container div**

There must be a container div in the template in which the viewer will be created::

	<div id="viewerContainer" style="width:100%; height:100%;"></div>

Feel free to add your own styling.


**Step 3: Decode the Base64-Encoded Data to a Uint8 Buffer**

Sample code to do this is provided in the minimal template. The ``_base64ToUint8Array`` method is a small helper function provided by the HOOPS Communicator team::

	var binaryModelData = _base64ToUint8Array(SC_MODEL_DATA)


**Step 4: Create and Start a WebViewer Object**

Specify your binary decoded data to the web viewer::

	var viewer= new Communicator.WebViewer({
		containerId: "viewerContainer",
		buffer: binaryModelData,
	});


Look, Feel, and Functionality
-----------------------------

Aside from the required elements enumerated above, users are free to add in their own HTML / JS / CSS code to the template. HOOPS Communicator customers may use the entirety of the Communicator API to create their own customized pages.

There are some restrictions for non-Communicator customers. The following classes aren't available to non-Communicator customers:

* ``Communicator.MeshData``
* ``Communicator.MeshInstanceData``

The following functions aren't available to non-Communicator customers:

* ``Communicator.Model.createMesh``
* ``Communicator.Model.createMeshInstance``


Helpful Hints
-------------

It can be challenging to work only in a single template file, especially given the size of the required javascript for the HOOPS WebViewer. There are a number of tools which can take a website and convert it to a self contained file. It would be helpful to design your template using normal separation of HTML, JS, CSS and employ a tool to combine all of it into your final template. We have had good luck using the *webpage2html* python module.


.. _html_template_sample:

Sample HTML Template
====================

The sample below shows the minimal HTML Export template; this file is available in the package in the *samples/data* directory. It will decode the plaintext data and start the viewer:

.. admonition:: HTML sample file
	:class: dropdown
	
	.. code-block:: none

		<pre>
		<html>
		<head>
		<title>Minimal HTML Export Template</title>

		<script>
			//The contents of hoops_web_viewer.js will appear here.
		</script>

		<script>
			function _base64ToUint8Array(base64) {
				var binary_string =  window.atob(base64);
				var len = binary_string.length;
				var bytes = new Uint8Array( len );
				for (var i = 0; i < len; i++)        {
					bytes[i] = binary_string.charCodeAt(i);
				}
				return bytes;
			}

			var SC_MODEL_DATA = "<!-- SC_MODEL_DATA -->";

			var hwv = null;
			var ui = null;

			window.onload = function () {
				hwv = new Communicator.WebViewer({
						containerId: "viewerContainer",
						buffer: _base64ToUint8Array(SC_MODEL_DATA),
					});
					
				window.onresize = function (event) {
					hwv.resizeCanvas();
				};

				hwv.start();
			}
		  </script>
		  
		  <style>
			  body{
				  margin: 0;
				  padding 0;
			  }
			#viewerContainer{
				position:relative;
				width:100%;
				height:100%;
			}
		  </style>
		</head>
		<body>
		<div id="viewerContainer"></div>
		</body>
		</html>


.. _obj:

OBJ Export
==========

To export your scene to an OBJ file, create a ``HPS::SprocketPath`` using your canvas, model, layout, and view. Then simply pass the ``SprocketPath`` as a keypath to the ``HPS::OBJ::File::Export`` function along with your file path:

.. tabs::

	.. group-tab:: C++
	
		.. literalinclude:: ../../../internals/tests/docs/source/cpp/00700_obj_export.cpp
		   :start-after: //! [obj_export]
		   :end-before: //! [obj_export]
		   
	.. group-tab:: C#

		.. literalinclude:: ../../../internals/tests/docs/source/cs/00700_obj_export.cs
		   :start-after: //! [obj_export]
		   :end-before: //! [obj_export]

In addition to an OBJ file, a file with an MTL extension will also be created in the output directory for most OBJ exports. 
This MTL file contains color and texture information. An image file will also be created for each texture used in the exported model.

For limitations of OBJ export, please see :doc:`Supported File Formats </general/supported_file_formats>`.


