void FakeInstanceNode::doDisplay(FrameContext & frameContext, const RenderParam & rp) {
	if(fakePrototype.isNull()) {
		Node::doDisplay(frameContext, rp);
		return;
	}

	auto camera = static_cast<AbstractCameraNode*>(frameContext.getCamera()->clone());
	const Geometry::Matrix4x4f & cameraMatrix = frameContext.getCamera()->getWorldTransformationMatrix();
	camera->setRelTransformation(	fakePrototype->getWorldTransformationMatrix() 
						* getWorldToLocalMatrix() 
						* cameraMatrix);

	frameContext.pushAndSetCamera(camera);
	frameContext.displayNode(fakePrototype.get(), rp);
	frameContext.popCamera();
}
void
SoDragPointDragger::checkBoxLimits()
//
////////////////////////////////////////////////////////////////////////
{
    // The limit box is defined in a space aligned and scaled as LOCAL 
    // space, but with it's center remaining fixed in WORLD space.

    // We need to do this work in LOCAL space, so to begin with,
    // get the location of the center of this box in LOCAL space.
    SbMatrix worldToLocal = getWorldToLocalMatrix();
    SbVec3f limitBoxCenterInLocal = limitBox.getCenter();
    worldToLocal.multVecMatrix( limitBoxCenterInLocal, 
				     limitBoxCenterInLocal );

    // Now, if our current position is out of range, we need to move
    // the limit box center accordingly. We continue to do our work
    // in LOCAL space.

    SbBool changed = FALSE;
    SbVec3f boxSize = limitBox.getMax() - limitBox.getMin();

    for (int i = 0; i < 3; i++) {

	float length = boxSize[i];
	float halfLength = length * 0.5;

	// Check the location of startLocalHitPt against boundaries of the limit
	// box (keeping in mind the jump limit as a % of the total length).

	// Have we gone too far in the positive direction?
	float high = limitBoxCenterInLocal[i] + halfLength;
	while ( (high - startLocalHitPt[i]) / length < jumpLimit ) {
	    limitBoxCenterInLocal[i] += halfLength;
	    high += halfLength;
	    changed = TRUE;

	}

	// Have we gone too far in the negative direction?
	float low  = limitBoxCenterInLocal[i] - halfLength;
	while (( startLocalHitPt[i] - low ) / length < jumpLimit ) {
	    limitBoxCenterInLocal[i] -= halfLength;
	    low  -= halfLength;
	    changed = TRUE;
	}
    }

    if (changed == TRUE ) {
	// First, convert the changed limitBoxCenterInLocal
	// into WORLD space...
	SbMatrix localToWorld = getLocalToWorldMatrix();
	SbVec3f newCenter;
	localToWorld.multVecMatrix(limitBoxCenterInLocal,newCenter);

	// Next, set the bounds of the limit box to have the same size
	// as before, but centered about this new point.
	SbVec3f diag = limitBox.getMax() - 
		       limitBox.getCenter();

	limitBox.setBounds( newCenter - diag, newCenter + diag );
    }
}
////////////////////////////////////////////////////////////////////////
//
// Description:
//    Sets the feedback geometry depending on which dragger is active.
//    A 1-D translator results in that line's feedback turning on.
//    A 2-D translator results in that plane's feedback turning on.
//
// Use: private
//
void
SoDragPointDragger::setFeedbackGeometry()
//
////////////////////////////////////////////////////////////////////////
{
    // First, figure out if we need to know a constrained translation
    // direction. This happens if a plane dragger is in use, but is
    // being constrained.
    int translateDir = -1;
#define TINY .0001
    if ( shftDown ) {
	SbVec3f current = getMotionMatrix()[3];
	SbVec3f start   = getStartMotionMatrix()[3];
	SbVec3f motion  = current - start;
	if (    fabs( motion[0]) > fabs( motion[1])
	     && fabs( motion[0]) > fabs( motion[2]))
	     translateDir = 0;
	else if ( fabs( motion[1]) > fabs( motion[2]))
	    translateDir = 1;
	else if ( fabs( motion[2] ) > TINY )
	    translateDir = 2;
    }
#undef TINY

    // Turn on appropriate parts, based on state.
    if (currentDragger == NULL) {
	setSwitchValue(xFeedbackSwitch.getValue(),      SO_SWITCH_NONE );
	setSwitchValue(yFeedbackSwitch.getValue(),      SO_SWITCH_NONE );
	setSwitchValue(zFeedbackSwitch.getValue(),      SO_SWITCH_NONE );
	setSwitchValue(planeFeedbackSwitch.getValue(),  SO_SWITCH_NONE );
    }
    else if (currentDragger == xTranslator.getValue() ) {
	setSwitchValue(xFeedbackSwitch.getValue(),      0 );
	setSwitchValue(yFeedbackSwitch.getValue(),      SO_SWITCH_NONE );
	setSwitchValue(zFeedbackSwitch.getValue(),      SO_SWITCH_NONE );
	setSwitchValue(planeFeedbackSwitch.getValue(),  SO_SWITCH_NONE );
    }
    else if (currentDragger == yTranslator.getValue() ) {
	setSwitchValue(xFeedbackSwitch.getValue(),      SO_SWITCH_NONE );
	setSwitchValue(yFeedbackSwitch.getValue(),      0 );
	setSwitchValue(zFeedbackSwitch.getValue(),      SO_SWITCH_NONE );
	setSwitchValue(planeFeedbackSwitch.getValue(),  SO_SWITCH_NONE );
    }
    else if (currentDragger == zTranslator.getValue() ) {
	setSwitchValue(xFeedbackSwitch.getValue(),      SO_SWITCH_NONE );
	setSwitchValue(yFeedbackSwitch.getValue(),      SO_SWITCH_NONE );
	setSwitchValue(zFeedbackSwitch.getValue(),      0 );
	setSwitchValue(planeFeedbackSwitch.getValue(),  SO_SWITCH_NONE );
    }
    else if (currentDragger == yzTranslator.getValue() ) {
	setSwitchValue(xFeedbackSwitch.getValue(),      SO_SWITCH_NONE );
	if ( translateDir == -1 || translateDir == 1 )
	    setSwitchValue(yFeedbackSwitch.getValue(),  0 );
	else
	    setSwitchValue(yFeedbackSwitch.getValue(),  SO_SWITCH_NONE );
	if ( translateDir == -1 || translateDir == 2 )
	    setSwitchValue(zFeedbackSwitch.getValue(),  0 );
	else
	    setSwitchValue(zFeedbackSwitch.getValue(),  SO_SWITCH_NONE );
	setSwitchValue(planeFeedbackSwitch.getValue(),  0 );
    }
    else if (currentDragger == xzTranslator.getValue() ) {
	if ( translateDir == -1 || translateDir == 0 )
	    setSwitchValue(xFeedbackSwitch.getValue(),  0 );
	else
	    setSwitchValue(xFeedbackSwitch.getValue(),  SO_SWITCH_NONE );
	setSwitchValue(yFeedbackSwitch.getValue(),      SO_SWITCH_NONE );
	if ( translateDir == -1 || translateDir == 2 )
	    setSwitchValue(zFeedbackSwitch.getValue(),  0 );
	else
	    setSwitchValue(zFeedbackSwitch.getValue(),  SO_SWITCH_NONE );
	setSwitchValue(planeFeedbackSwitch.getValue(),  1 );
    }
    else if (currentDragger == xyTranslator.getValue() ) {
	if ( translateDir == -1 || translateDir == 0 )
	    setSwitchValue(xFeedbackSwitch.getValue(),  0 );
	else
	    setSwitchValue(xFeedbackSwitch.getValue(),  SO_SWITCH_NONE );
	if ( translateDir == -1 || translateDir == 1 )
	    setSwitchValue(yFeedbackSwitch.getValue(),  0 );
	else
	    setSwitchValue(yFeedbackSwitch.getValue(),  SO_SWITCH_NONE );
	setSwitchValue(zFeedbackSwitch.getValue(),      SO_SWITCH_NONE );
	setSwitchValue(planeFeedbackSwitch.getValue(),  2 );
    }

    checkBoxLimits();

    //**************************************
    // Set the transforms for the feedback axes and/or planes.
    //**************************************

    // First, find the center and scale for each part, in LOCAL space.

	// The limit box is defined in a space aligned and scaled as 
	// LOCAL space, but with it's center remaining fixed in WORLD 
	// space.

	// We need to transform the center of the limitBox
	// from WORLD space to LOCAL space to 
	// find the location of the center of the limit box feedback.
	SbMatrix worldToLocal = getWorldToLocalMatrix();

	SbVec3f limitBoxCenterInLocal = limitBox.getCenter();
	worldToLocal.multVecMatrix( limitBoxCenterInLocal, 
				     limitBoxCenterInLocal );

	SbVec3f xAxisSpot = limitBoxCenterInLocal;
	SbVec3f yAxisSpot = limitBoxCenterInLocal;
	SbVec3f zAxisSpot = limitBoxCenterInLocal;
	SbVec3f planeSpot = limitBoxCenterInLocal;

        //With center matching mouse location...
	// The point under the mouse is stored as 'startLocalHitPt', which is
	// expressed in LOCAL space. 

	xAxisSpot[1] = startLocalHitPt[1];
	xAxisSpot[2] = startLocalHitPt[2];

	yAxisSpot[0] = startLocalHitPt[0];
	yAxisSpot[2] = startLocalHitPt[2];

	zAxisSpot[0] = startLocalHitPt[0];
	zAxisSpot[1] = startLocalHitPt[1];

	if (   currentDragger == yzTranslator.getValue()) {
	    planeSpot[0] = startLocalHitPt[0];
	}
	else if (   currentDragger == xzTranslator.getValue()) {
	    planeSpot[1] = startLocalHitPt[1];
	}
	else if (   currentDragger == xyTranslator.getValue()) {
	    planeSpot[2] = startLocalHitPt[2];
	}

    SoTranslation *transNode;
    // Set position of each axis feedback
	if ( xFeedbackTranslation.getValue() == NULL )
	    setAnyPart( "xFeedbackTranslation", new SoTranslation );
	transNode = (SoTranslation *) xFeedbackTranslation.getValue();
	transNode->translation = xAxisSpot;

	if ( yFeedbackTranslation.getValue() == NULL )
	    setAnyPart( "yFeedbackTranslation", new SoTranslation );
	transNode = (SoTranslation *) yFeedbackTranslation.getValue();
	transNode->translation = yAxisSpot;

	if ( zFeedbackTranslation.getValue() == NULL )
	    setAnyPart( "zFeedbackTranslation", new SoTranslation );
	transNode = (SoTranslation *) zFeedbackTranslation.getValue();
	transNode->translation = zAxisSpot;

    // Set position of plane feedback
	if ( planeFeedbackTranslation.getValue() == NULL )
	    setAnyPart( "planeFeedbackTranslation", new SoTranslation );
	transNode = (SoTranslation *) planeFeedbackTranslation.getValue();
	transNode->translation = planeSpot;
}