public void TestZoom1_XOnly_XDirection() { double aspectRatio = 1.0; double cameraDistanceOriginal = 1000; double zNear = 100; double zFar = 10000; double widthByZNear = 0.5; double distanceFactor = 0.75; var targetPosition = new PointD3D(0, 0, 0); var targetToEye = new VectorD3D(cameraDistanceOriginal, 0, 0); var cameraUpVector = new VectorD3D(0, 0, 1); var targetToWorldPoint = new VectorD3D(0, 200, 0); PointD3D cameraPosition = targetPosition + targetToEye; PointD3D worldPoint = targetPosition + targetToWorldPoint; Assert.AreEqual(0, VectorD3D.DotProduct(targetToEye, targetToWorldPoint), "The test must be set up in a way that targetToEye and targetToWorldPoint are perpendicular to each other"); var cameraO = new PerspectiveCamera(cameraUpVector, cameraPosition, targetPosition, zNear, zFar, widthByZNear * zNear); var screenO = cameraO.GetViewProjectionMatrix(aspectRatio).Transform(worldPoint); Assert.AreEqual(0.0, screenO.Y, "Test must be set up so that screen.Y is always zero"); var cameraN = cameraO.ZoomByGettingCloserToTarget(distanceFactor, screenO.X, screenO.Y, aspectRatio); var screenN = cameraN.GetViewProjectionMatrix(aspectRatio).Transform(worldPoint); Assert.AreEqual(screenO.X, screenN.X, 1E-3); Assert.AreEqual(screenO.Y, screenN.Y, 1E-3); }
/// <summary> /// Gets the hit point on that plane of the active layer rectangle, that is facing the camera. /// </summary> /// <param name="doc">The graph document containing the active layer.</param> /// <param name="activeLayer">The active layer of the graph document.</param> /// <param name="hitposition">Hit point in relative screen coordinates. The z-component is the aspect ratio of the screen (y/x).</param> /// <param name="hitPointOnPlaneInActiveLayerCoordinates">Output: The hit point on the plane of the active layer that faces the camera. The hit point is returned in active layer coordinates.</param> /// <param name="rotationsRadian">The rotation angles that can be used e.g. to orient text so that the text is most readable from the current camera setting. Rotation angle around x is the x-component of the returned vector, and so on.</param> /// <exception cref="InvalidProgramException">There should always be a plane of a rectangle that can be hit!</exception> public static void GetHitPointOnActiveLayerPlaneFacingTheCamera(GraphDocument doc, HostLayer activeLayer, PointD3D hitposition, out PointD3D hitPointOnPlaneInActiveLayerCoordinates, out VectorD3D rotationsRadian) { var activeLayerTransformation = activeLayer.TransformationFromRootToHere(); var camera = doc.Camera; var hitData = new HitTestPointData(camera.GetHitRayMatrix(hitposition)); hitData = hitData.NewFromAdditionalTransformation(activeLayerTransformation); // now hitdata are in layer cos var targetToEye = hitData.WorldTransformation.Transform(camera.TargetToEyeVectorNormalized); // targetToEye in layer coordinates var upEye = hitData.WorldTransformation.Transform(camera.UpVectorPerpendicularToEyeVectorNormalized); // camera up vector in layer coordinates // get the face which has the best dot product between the eye vector of the camera and the plane's normal var layerRect = new RectangleD3D(PointD3D.Empty, activeLayer.Size); double maxval = double.MinValue; PlaneD3D maxPlane = PlaneD3D.Empty; foreach (var plane in layerRect.Planes) { double val = VectorD3D.DotProduct(plane.Normal, targetToEye); if (val > maxval) { maxval = val; maxPlane = plane; } } bool isHit = hitData.IsPlaneHitByRay(maxPlane, out hitPointOnPlaneInActiveLayerCoordinates); // hitPointOnPlane is in layer coordinates too if (!isHit) { throw new InvalidProgramException("There should always be a plane of a rectangle that can be hit!"); } VectorD3D zaxis = maxPlane.Normal; VectorD3D yaxis = upEye; // Find y axis perpendicular to zaxis maxval = double.MinValue; foreach (var plane in layerRect.Planes) { double val = VectorD3D.DotProduct(plane.Normal, upEye); if (val > maxval && 0 == VectorD3D.DotProduct(plane.Normal, zaxis)) { maxval = val; yaxis = plane.Normal; } } var xaxis = VectorD3D.CrossProduct(yaxis, zaxis); // now we have all information about the spatial position and orientation of the text: // hitPointOnPlane is the position of the text // maxPlane.Normal is the face orientation of the text // maxUpVector is the up orientation of the text double cx, sx, cy, sy, cz, sz; sy = xaxis.Z; if (1 != Math.Abs(sy)) { cy = Math.Sqrt(1 - sy * sy); cz = xaxis.X / cy; sz = xaxis.Y / cy; sx = yaxis.Z / cy; cx = zaxis.Z / cy; } else // sy is +1, thus cy is zero { // we set x-rotation to zero, i.e. cx==1 and sx==0 cy = 0; cx = 1; sx = 0; cz = yaxis.Y; sz = -yaxis.X; } rotationsRadian = new VectorD3D(Math.Atan2(sx, cx), Math.Atan2(sy, cy), Math.Atan2(sz, cz)); }