I’ve been using d3 for quite some time but just created my first couple of real Observable notebooks. In particular, this one calls mathjs and x3dom as well as d3. When I load the page, sometimes the generated image appears and sometimes it doesn’t. My guess is that this has something to do with the various libraries loading asynchronously at different rates and not necessarily returning correctly, but I’m not sure how to fix it. Any help would be much appreciated.
For the record, the page should generate and image like the one shown here. Here’s a static version:
I must admit that I probably know the least about any of this out of all the users on this forum, but since your question’s been active for a couple of hours without a reply, please allow me to point out a pattern I’ve seen several places when these sorts of issues arise… await :
To pull a pattern from another thread (recognizing the modules being required differ from yours):
My understanding is (going out on a limb here) that you can work around the asynchronous loading by specifying a particular order for modules.
Also, you might check out the section on ‘stubborn add-ons’ here:
… If I were more adept at this, I’d try to help tailor it to your purposes, but I have a feeling you’ll sort it out on your own
@aaronkyle Thanks for the reply. Unfortunately, it doesn’t quite seem to help. I can see where using await would help in the case that you reference, since you’re basically loading one module that modifies the object returned by another but that’s not the case here.
There is good news, though:
Loading x3dom via the following command seems to help: x3dom = require('x3dom').catch(() => window['x3dom'])
More importantly, a web page using the embed code works reliably.
The X3DOM API seems a little unfriendly to X3DOM elements that are created dynamically (in JavaScript), as opposed to elements that are declared statically in the HTML at page load. It appears that you can call x3dom.reload after adding an X3DOM element to the DOM and it will realize there’s an element to render. Perhaps there’s a more API for this, but I couldn’t find one perusing the API.
Not only does is this reliable on reload but it responds immediately when the code is edited. The x3dom.reload() makes perfect sense. I’ll have to think about the use of yield.
Nice example using x3dom with d3 which is a good combo.
I also typically use x3dom.reload() to make sure all changes to the DOM are picked up. The main API for x3dom is just DOM manipulation with any tool including plain DOM API or d3. x3dom then uses observers to react to mutations in the x3d element. However, x3dom follows the x3d specification which does not allow for dynamically changing all fields/attributes of all nodes, and so not all DOM mutations lead to perhaps expected changes in the scene. Therefore it is safest to just recreate the scene from scratch after a change by using x3dom.reload().
If there is interested, it might be useful to put together more reactive x3dom examples, for example one using variable evaluation within a html tagged template.
[ For the x3d content, you could use DEF/USE for the coordinate axes as in this fork: ]
Yes, that’s exactly why I favor x3dom for 3D graphics as compared to other libraries like three.js. Working with x3d feels a lot like working with SVG so the style of programming doesn’t change too much. There are some oddities like you mention.
I’m getting a “Sorry, we couldn’t find that page” error. Perhaps you tried to share a private file?
Sorry, I forked without enabling link sharing. It should work now.
SVG is often used as an example in the X3D space how real integration into the web could look like. As a mature standard X3D also has the benefit that it is stable and supported outside the web.
I agree that X3Dom doesn’t react as to changes as I’m accustomed to. Nonetheless, building from scratch can be very expensive so it’s worth avoiding it, if possible. I just published a new version that allows the user to select how many threads are displayed. I did try to create that by building from scratch but that was quite slow. Thus, instead, the published code creates all threads from the beginning and then sets the transparency of some of the threads as necessary.
There are also a few other enhancements, including your suggestion to use DEF/USE for the axes - thanks for that!
x3dom reacts to most changes in the x3d sub DOM, just not to all of them. I think that is a VRML legacy where 3d performance needed to be carefully managed.
It does make sense to keep DOM changes to a minimum, let alone recreate from scratch, when possible. This includes actually first building the x3d element detached from the page dom, and only appending it to the page document when it is fully constructed. That way there are no unnecessary updates or parsing.
Sofar you do not really use d3 data binding, so selectAll would not be that useful. Here is variant which uses standard DOM querySelectorAll to update the transparencies:
I am not sure if it is a worthwhile enhancement but perhaps educational. [Actually, the thread attribute in this fork should be named data-thread].
Since d3 data binding is not really used, here is another take which uses html tagged templates to construct the scene:
This makes the description more declarative and compact, and can avoid the dependence on d3 if that would be a consideration. d3.range is substituted by a one-line helper.
[Minor update: this uses the ‘render’ attribute of the shape instead of the transparency to control which spindles are displayed.]