block by hlvoorhees 5376764

d3 x3dom event test

Full Screen

Testing D3 mouse events with X3dom elements.

The x3dom canvas captures onclick events, so just defining a 3d event handler on an x3dom element does not work. Hence, clicking the red cube does nothing.

A workaround is to define an onclick handler which calls the 3d ‘click’ event handler with the event, as demonstrated by clicking on the blue sphere. Note that x3dom event members differ from d3’s, so d3.mouse() function does not work.

Another solution, applied to the green cone, is to call addEventListener(“click”,…).

index.html

<!DOCTYPE html>
<html>
<head>
<meta HTTP-EQUIV="REFRESH" content="0; url=./d3_x3dom_event_test.xhtml">
</head>
</html>

d3_x3dom_event_test.xhtml

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <meta http-equiv="X-UA-Compatible" content="chrome=1" />
  <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
  <title>d3 x3dom event test</title>
  <script type="text/javascript" src="http://x3dom.org/x3dom/dist/x3dom.js"></script>
  <script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script>
  <link rel="stylesheet" type="text/css" href="http://www.x3dom.org/download/dev/x3dom.css"></link>
</head>
       
<body>
<div id="divX3d">
  <X3D xmlns="http://www.web3d.org/specifications/x3d-namespace" id="x3d" x="0px" y="0px" width="500px" height="500px">
    <Scene>
      <Viewpoint centerOfRotation="0 0 0" position="0 0 15" orientation="0 1 0 0" />
      <Background skyColor='.95 .95 .95' />
      <Group>
        <Transform translation='-3 0 0'>
          <Shape>
            <Appearance> 
              <Material diffuseColor="1 0 0" specularColor=".5 .5 .5" />
	    </Appearance>
	    <Box id="Red cube"/>
	  </Shape>
        </Transform>
        <Transform translation="0 0 0">
          <Shape>
            <Appearance>
               <Material diffuseColor="0 0 1" specularColor=".5 .5 .5" />
            </Appearance>
    	    <Sphere id="Blue sphere"/>
          </Shape>
       </Transform>
       <Transform translation="3 0 0">
          <Shape>
            <Appearance>
               <Material diffuseColor="0 1 0" specularColor=".5 .5 .5" />
            </Appearance>
    	    <Cone id="Green cone"/>
          </Shape>
       </Transform>
       <Transform translation="5 0 0">
          <Billboard axisOfRotation="0 0 0">
       	     <Shape>
       	         <Text solid="true" string="Click Me">
       	         </Text>
       	     </Shape>	
       	  </Billboard>
       </Transform>
    </Group>
  </Scene>
</X3D>
</div>

<script type="text/javascript">

document.onload = function()
{
  // This will not work by itself because the x3d canvas captures onclick methods
  d3.select('Box').on('click', showAlertWithEventCoordinate)

  // This works. Note however that in the javascript string assigned to 'onclick', the function
  // forwardClick must be globally, not locally scoped.
  d3.select('Sphere').on('click', showAlertWithEventCoordinate)
  d3.select('Sphere').attr('onclick', "forwardClick(event)")

  // This works too.
  d3.select('Cone').node().addEventListener('click', showAlertWithEventCoordinate)
  d3.select('Text').node().addEventListener('click', showAlertWithEventCoordinate)

  function showAlertWithEventCoordinate(event) {
   var pagePt = invertMousePosition(event);
   alert(d3.select(event.target).attr('id') + ' picked at:\n'
     + 'world coordinate (' + event.hitPnt + '),\n'
     + 'canvas coordinate (' + event.layerX + ', ' + event.layerY + '),\n'
     + 'page coordinate (' + pagePt.x + ', ' + pagePt.y + ')' )
  }
}

// Call the 'click' handler with event. Note that event member variables differ from
// what d3 expects. In particular d3.mouse() will not work.
//
function forwardClick(event) {
  d3.select(event.target).on('click')(event);

  // Is possible to instead construct a new event with members that d3 requires? e.g.,
  //var e=document.createEvent('mouseEvent');
  //var screenX = 0, screenY = 0; // TODO: compute
  //e.initMouseEvent('click', event.bubbles, event.cancelable, window, event.detail,
  //  screenX, screenY, event.layerX, event.layerY,
  //  event.ctrlKey, event.altKey, event.shiftKey, event.metaKey, event.button, null);
  //d3.select(event.target).node().dispatchEvent(e);
}

// inverse of coordinate transform defined by function mousePosition(evt) in x3dom.js
//
function invertMousePosition(evt) 
{
  var convertPoint = window.webkitConvertPointFromPageToNode;
  var pageX = -1, pageY = -1;
  if ( "getBoundingClientRect" in document.documentElement ) {
    var elem = d3.select('#divX3d').node();
    console.log('elem:', elem)
    var box = elem.getBoundingClientRect();
    var scrolleft =  window.pageXOffset || document.body.scrollLeft;
    var scrolltop =  window.pageYOffset || document.body.scrollTop;
    var paddingLeft = parseFloat(document.defaultView.getComputedStyle(elem, null).getPropertyValue('padding-left'));
    var borderLeftWidth = parseFloat(document.defaultView.getComputedStyle(elem, null).getPropertyValue('border-left-width'));
    var paddingTop = parseFloat(document.defaultView.getComputedStyle(elem, null).getPropertyValue('padding-top'));
    var borderTopWidth = parseFloat(document.defaultView.getComputedStyle(elem, null).getPropertyValue('border-top-width'));
    pageX = Math.round(evt.layerX + (box.left + paddingLeft + borderLeftWidth + scrolleft));
    pageY = Math.round(evt.layerY + (box.top + paddingTop + borderTopWidth + scrolltop));
  } else if (convertPoint) {
    var pagePoint = convertPoint(evt.target, new WebKitPoint(0, 0));
    x = Math.round(point.x);
    y = Math.round(point.y);
  } else {
    x3dom.debug.logError('NO getBoundingClientRect, NO webkitConvertPointFromPageToNode');
  }
  return { x: pageX, y: pageY };
}

</script>
</body>
</html>