void
JX3DWidget::ContinueDrag
	(
	const JPoint& pt
	)
{
	if (itsDragType == kRotateDrag)
		{
		const JVector delta(3, - pt.x + itsStartPt.x, pt.y - itsStartPt.y, 0.0);

		const JVector& o = itsCamera->GetAttentionPt();
		JVector v        = itsStartPos - o;
		const JFloat r   = v.GetLength();
		const JFloat lat = asin(v.GetElement(3) / r);
		const JFloat lon = atan2(v.GetElement(2), v.GetElement(1));

		// direction of drag relative to screen x-axis

		const JFloat alpha = atan2(delta.GetElement(2), delta.GetElement(1));

		// delta along the sphere in drag direction

		const JFloat phi = delta.GetLength() / (r * 50);
		JVector p(3, r*cos(phi), r*sin(phi), 0.0);

		// inverse: rotate around z-axis for partial alignment of x-axis

		JMatrix r1(3, 3);
		r1.SetElements(
			cos(lon), -sin(lon), 0.0,
			sin(lon),  cos(lon), 0.0,
				 0.0,       0.0, 1.0);

		// inverse: rotate around y-axis for complete alignment of x-axis towards viewer

		JMatrix r2(3, 3);
		r2.SetElements(
			cos(lat), 0.0, -sin(lat),
				 0.0, 1.0,       0.0,
			sin(lat), 0.0,  cos(lat));

		// inverse: rotate around x-axis to align y-axis with drag direction

		JMatrix r3(3, 3);
		r3.SetElements(
			1.0,        0.0,         0.0,
			0.0, cos(alpha), -sin(alpha),
			0.0, sin(alpha),  cos(alpha));

		// transform delta long the sphere to our coordinate system

		p = (r1 * r2 * r3 * p).GetColVector(1);

		itsCamera->SetPosition(p + o);
		}
}
JBoolean
JX3DWidget::HandleRotationKeyPress
	(
	const int				key,
	const JXKeyModifiers&	modifiers
	)
{
	if (modifiers.meta() && key == kJUpArrow)
		{
		return ZoomForWheel(kJXButton4, modifiers);
		}
	else if (modifiers.meta() && key == kJDownArrow)
		{
		return ZoomForWheel(kJXButton5, modifiers);
		}

	const JVector& o = itsCamera->GetAttentionPt();
	JVector v        = itsCamera->GetPosition() - o;
	const JFloat r   = v.GetLength();
	JFloat lat       = asin(v.GetElement(3) / r);
	JFloat lon       = atan2(v.GetElement(2), v.GetElement(1));

	const JFloat delta =
		(modifiers.shift()   ? kSmallDeltaAngle :
		(modifiers.control() ? kBigDeltaAngle   :
		kDeltaAngle));

	JBoolean moved = kJFalse;

	if (key == kJUpArrow)
		{
		lat  += delta;
		moved = kJTrue;
		}
	else if (key == kJDownArrow)
		{
		lat  -= delta;
		moved = kJTrue;
		}

	else if (key == kJLeftArrow)
		{
		lon  += delta;
		moved = kJTrue;
		}
	else if (key == kJRightArrow)
		{
		lon  -= delta;
		moved = kJTrue;
		}

	if (moved)
		{
		itsCamera->SetPosition(JVector(3,
			r * cos(lat) * cos(lon),
			r * cos(lat) * sin(lon),
			r * sin(lat)));

		Redraw();	// key repeats can delay Refresh()
		return kJTrue;
		}
	else
		{
		return kJFalse;
		}
}