| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210 | 
							- import {
 
-   init,
 
-   classModule,
 
-   propsModule,
 
-   styleModule,
 
-   eventListenersModule,
 
-   h,
 
-   attributesModule,
 
- } from 'snabbdom';
 
- const patch = init([
 
-   attributesModule,
 
-   classModule,
 
-   propsModule,
 
-   styleModule,
 
-   eventListenersModule,
 
- ]);
 
- function mountDummySVGContainer(canvas) {
 
-   const container = canvas.parentElement;
 
-   const dummy = document.createElement('div');
 
-   container.insertBefore(dummy, canvas.nextSibling);
 
-   const containerStyles = window.getComputedStyle(container);
 
-   if (containerStyles.position === 'static') {
 
-     container.style.position = 'relative';
 
-   }
 
-   return dummy;
 
- }
 
- export const VerticalTextAlignment = {
 
-   TOP: 1,
 
-   MIDDLE: 2,
 
-   BOTTOM: 3,
 
- };
 
- /**
 
-  * Computes the relative dy values around 0 for multiline text.
 
-  *
 
-  * @param nLines
 
-  * @param fontSize
 
-  * @returns a list of vertical offsets (from a zero origin) for placing multiline text.
 
-  */
 
- export function multiLineTextCalculator(
 
-   nLines,
 
-   fontSize,
 
-   alignment = VerticalTextAlignment.BOTTOM
 
- ) {
 
-   const dys = [];
 
-   for (let i = 0; i < nLines; i++) {
 
-     switch (alignment) {
 
-       case VerticalTextAlignment.TOP:
 
-         dys.push(fontSize * (i + 1));
 
-         break;
 
-       case VerticalTextAlignment.MIDDLE:
 
-         dys.push(-fontSize * (0.5 * nLines - i - 1));
 
-         break;
 
-       case VerticalTextAlignment.BOTTOM:
 
-       default:
 
-         dys.push(-fontSize * (nLines - i - 1));
 
-     }
 
-   }
 
-   return dys;
 
- }
 
- /**
 
-  * Automatically updates an SVG rendering whenever a widget's state is updated.
 
-  *
 
-  * This update is done in two phases:
 
-  * 1. mapState(widgetState) takes the widget state and transforms it into an intermediate data representation.
 
-  * 2. render(data, h) takes the intermediate data representation and a createElement `h` function, and returns
 
-  *    an SVG rendering of the state encoded in `data`.
 
-  *
 
-  * See snabbdom's documentation for how to use the `h` function passed to `render()`.
 
-  *
 
-  * @param renderer the widget manager's renderer
 
-  * @param widgetState the widget state
 
-  * @param mapState (object parameter) transforms the given widget's state into an intermediate data representation to be passed to render().
 
-  * @param render (object parameter) returns the SVG representation given the data from mapState() and snabbdom's h render function.
 
-  */
 
- export function bindSVGRepresentation(
 
-   renderer,
 
-   widgetState,
 
-   { mapState, render }
 
- ) {
 
-   const view = renderer.getRenderWindow().getViews()[0];
 
-   const canvas = view.getCanvas();
 
-   const getSize = () => {
 
-     const [width, height] = view.getSize();
 
-     const ratio = window.devicePixelRatio || 1;
 
-     return {
 
-       width: width / ratio,
 
-       height: height / ratio,
 
-       viewBox: `0 0 ${width} ${height}`,
 
-     };
 
-   };
 
-   const renderState = (state) => {
 
-     const repData = mapState(state, {
 
-       size: view.getSize(),
 
-     });
 
-     const rendered = render(repData, h);
 
-     return h(
 
-       'svg',
 
-       {
 
-         attrs: getSize(),
 
-         style: {
 
-           position: 'absolute',
 
-           top: '0',
 
-           left: '0',
 
-           width: '100%',
 
-           height: '100%',
 
-           // deny pointer events by default
 
-           'pointer-events': 'none',
 
-         },
 
-       },
 
-       Array.isArray(rendered) ? rendered : [rendered]
 
-     );
 
-   };
 
-   const dummy = mountDummySVGContainer(canvas);
 
-   let vnode = patch(dummy, renderState(widgetState));
 
-   const updateVNode = () => {
 
-     vnode = patch(vnode, renderState(widgetState));
 
-   };
 
-   const stateSub = widgetState.onModified(() => updateVNode());
 
-   const cameraSub = renderer.getActiveCamera().onModified(() => updateVNode());
 
-   const observer = new ResizeObserver(() => updateVNode());
 
-   observer.observe(canvas);
 
-   return () => {
 
-     stateSub.unsubscribe();
 
-     cameraSub.unsubscribe();
 
-     observer.disconnect();
 
-     patch(vnode, h('!')); // unmount hack
 
-     vnode = null;
 
-   };
 
- }
 
- /**
 
-  * Applies a set of default interaction handling behavior.
 
-  *
 
-  * Typically, firing pointerenter means that the pointer is
 
-  * "hovering", meaning the associated widget state should
 
-  * be selected. (This is on the user to do this step.)
 
-  * Accordingly, pointerleave means no more hovering.
 
-  *
 
-  * However, vtk.js captures the pointer on pointerdown,
 
-  * which means that clicking on an SVG element will result
 
-  * in a pointerleave being triggered, deselecting the
 
-  * widget state.
 
-  *
 
-  * The abridged sequence of events is as follows:
 
-  * 1. pointerenter on the SVG element (mouse moves over SVG handle)
 
-  * 2. pointerdown on the SVG element (left button press)
 
-  * 2. pointerdown on the vtk.js canvas (left button press)
 
-  * 3. pointer captured on the vtk.js canvas (left button press)
 
-  * 4. pointerleave on the SVG element as soon as the mouse is moved,
 
-  *    since the capture target is now the canvas.
 
-  * 5. pointerenter on the SVG element when the mouse/pointer is released.
 
-  *
 
-  * To workaround this issue, we conditionally fire the user's
 
-  * pointerleave listener only when we are "locked", which means
 
-  * we saw a pointerdown and so the vtk.js canvas is capturing
 
-  * the current pointer.
 
-  */
 
- function applyDefaultInteractions(userListeners) {
 
-   let locked = false;
 
-   return {
 
-     ...userListeners,
 
-     pointerdown(ev) {
 
-       locked = true;
 
-       return userListeners?.pointerdown?.(ev);
 
-     },
 
-     pointerenter(ev) {
 
-       if (locked) {
 
-         locked = false;
 
-       }
 
-       return userListeners?.pointerenter?.(ev);
 
-     },
 
-     pointerleave(ev) {
 
-       if (!locked) {
 
-         return userListeners?.pointerleave?.(ev);
 
-       }
 
-       return undefined;
 
-     },
 
-   };
 
- }
 
- /**
 
-  * Requires the snabbdom eventlisteners and style modules.
 
-  * @param vnode
 
-  * @returns
 
-  */
 
- export function makeListenableSVGNode(vnode) {
 
-   // allow pointer events on this vnode
 
-   vnode.data.style = {
 
-     ...vnode.data.style,
 
-     'pointer-events': 'all',
 
-   };
 
-   vnode.data.on = applyDefaultInteractions(vnode.data.on);
 
-   return vnode;
 
- }
 
- export default { bindSVGRepresentation, multiLineTextCalculator };
 
 
  |