Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new components with importDocument #43

Closed
andreasplesch opened this issue Mar 12, 2019 · 10 comments
Closed

new components with importDocument #43

andreasplesch opened this issue Mar 12, 2019 · 10 comments

Comments

@andreasplesch
Copy link
Contributor

Now that many components are loaded on demand, is there a way to trigger loading of new components with importDocument followed by replaceWorkd ?
For example, going from a Scene with Immersive profile to a Scene with Full profile.

@andreasplesch
Copy link
Contributor Author

Here is an example:

https://andreasplesch.github.io/x_ite_dom/tests/interactiveTransformations.xhtml

This scene uses event utilities such as BooleanFilter and TimeTrigger. Now there are XMLParser error complaining about unknown node types because the EventUtilities component is not loaded.

I could prepare a minimal example only using SAI methods as well, if helpful.

@create3000
Copy link
Owner

To trigger load in importDocument is not possible because it is not asynchronously.

In your setup function you could determine profile/components and if not present use profile FULL, then with getProviderUrls get the urls.

There is a getProviderUrls in X3DParser which determines the required components from profile/component declarations, may be should I put this function to X3DScene?

		getProviderUrls: (function ()
		{
			var componentsUrl = /\.js$/;

			return function ()
			{
				var
					profileComponents = this .getScene () .getProfile () .components,
					components        = this .getScene () .getComponents (),
					providerUrls      = new Set ();

				for (var i = 0, length = profileComponents .length; i < length; ++ i)
				{
					var providerUrl = profileComponents [i] .providerUrl;
	
					if (providerUrl .match (componentsUrl))
						providerUrls .add (providerUrl);
				}

				for (var i = 0, length = components .length; i < length; ++ i)
				{
					var providerUrl = components [i] .providerUrl;
	
					if (providerUrl .match (componentsUrl))
						providerUrls .add (providerUrl);
				}

				return Array .from (providerUrls);
			};
		})(),

The result can be passed to require.


				require (scene .getProviderUrls (),
				function ()
				{
// success
				},
				function (error)
				{
// error
				});

on success you can safely use importDocument.

@create3000
Copy link
Owner

I could make a X3DScene.requestImmediateLoad(success, error) function. How about this? Maybe an other name?

@andreasplesch
Copy link
Contributor Author

Yes, I saw that in the parser. I will give it a try.
From a spec. perspective, the loading of new component libraries does not need to happen at importDocument(), I believe, since this just returns a X3DScene which may never be loaded. As long as importDocument correctly sets the profile and/or components for the new scene.
But it needs to happen at replaceWorld(newScene). Perhaps that could become asynchronous, with lazy loading of required component libraries ?

I also tried loading a dummy x3d as a plain data url with a profile='Full' as a src attribute to X3DCanvas.

The main problem then was to wait for the loading to finish before installing the integrations. I could not figure it out. The onload event listener apparently can only be given as an attribute ? It would be nice to have an .onload property on X3Canvas or actually be able to .addEventListener.

@andreasplesch
Copy link
Contributor Author

Cool, using require 'manually' to get the component providers works:

https://raw.githack.com/andreasplesch/x_ite_dom/8b816e780aa98e6e5c904d1236a4e2cef723e1d0/tests/interactiveTransformations.xhtml

There is just a bit of a chicken and egg problem since I have to importDocument first to get the scene with the profile which causes parsing errors. But then I can use the scene to make a parser for this scene which has then the correct provider urls, and then I can reimport with require.

Overall this works well but I think from a standard perspective importDocument() and then replaceWorld should also just work with any profile.

@create3000
Copy link
Owner

I moved getProviderUrls to X3DScene, it is not longer part of X3DParser. This means you don't have to instantiate a XMLParser for this operation. To get the full power of dynamic loaded components you must parse the head element and the component elements and add the components to the scene and set the profile from the X3D element before you call getProviderUrls. If you do not set the profile and components of the scene and call getProviderUrls the profile 'Full' is used, which is the worst case, but everything will work.

@create3000
Copy link
Owner

The 'alpha' version is already up to date.

@andreasplesch
Copy link
Contributor Author

This uses the master branch directly:
https://raw.githack.com/andreasplesch/x_ite_dom/f6353a95bc6c02dbdbbeb6b904cd4b507a4956c5/tests/test_alphax_ite.xhtml

The X3D has a profile field, and x_ite_dom now looks for the X3D dom node rather than Scene.

It uses the same approach as before. There are two import passes, the first one to set the profile for the new scene, and then the second one to use the correct scene profile to reimport with the required components.
The only problem with that approach that it generates errors in the console during the first import pass if some used components are missing (and that it requires the second pass).

I could manually parse the profile (and components) of the new scene dom, and set it on the current (empty) scene.

Could there be a browser.importDocumentAsync that requires the components, then imports and returns a promise ? The use pattern would be something like:

scenePromise = browser.importDocumentAsync(dom);
scenePromise.then((loadedScene)=>browser.replaceWorld(loadedScene));
//or
scene.then(browser.replaceWorld, rejectFunction);
//or
async replaceWorldAsync(scenePromise) {
  var scene = await scenePromise;
  return browser.replaceWorld(scene) };
scene = replaceWorldAsync(scenePromise);

@andreasplesch
Copy link
Contributor Author

I am testing to replace .importDocument with parser.parseIntoScene since it can require the components if there is a success callback:

https://raw.githack.com/andreasplesch/x_ite_dom/8a1ba2d29c8a02f628ac14c166f918c030d92372/tests/test_alphax_ite.xhtml

Here is the modified loading:

https://github.com/andreasplesch/x_ite_dom/blob/8a1ba2d29c8a02f628ac14c166f918c030d92372/src/x_ite_dom.js#L58-L71

It is nicer because it does not require the two passes and does not generate errors.
I am replacing the world with itself to trigger a full reset which seems a bit strange.

But is uses the internal parseIntoScene parser function whereas it would be better to rely on SAI only. But since otherwise getProviderUrls would have to be used anyways, this is actually probably ok.

Still, it would be great if .importDocument could be made to work for lazy loading without having to use workarounds.

@andreasplesch
Copy link
Contributor Author

75d720a

x_ite 4.4.4 allows browser.importDocument(dom,success,error) for async. component loading and subsequent invocation of provided success callback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants