I figured out one possible solution making use of the linearity of the perspective camera.
- Set camera to a known distance (i.e. 1.0)
- Compute 2D bounding box of the model using
Visual3D.TransformToAncestor
- Compute scale factor (bounding box size / viewport size)
- Multiply camera distance by the scale factor
In other words, if the camera is twice further, the image is twice smaller...
PerspectiveCamera camera = (PerspectiveCamera)this.viewport.Camera;
// set camera to a known distance
camera.Position = new Point3D(0.0, 0.0, 100.0);
Point3D[] points3D = new[]
{
new Point3D(-1.0, -1.0, 0.0),
new Point3D(1.0, -1.0, 0.0),
new Point3D(-1.0, 1.0, 0.0),
new Point3D(1.0, 1.0, 0.0)
};
double minX = Double.MaxValue;
double maxX = Double.MinValue;
double minY = Double.MaxValue;
double maxY = Double.MinValue;
GeneralTransform3DTo2D transform = this.viewport.Children[1].TransformToAncestor(this.viewport);
foreach (var point3D in points3D)
{
Point point2D = transform.Transform(point3D);
minX = Math.Min(minX, point2D.X);
maxX = Math.Max(maxX, point2D.X);
minY = Math.Min(minY, point2D.Y);
maxY = Math.Max(maxY, point2D.Y);
}
Size currentSize = new Size(maxX - minX, maxY - minY);
Size desiredSize = new Size(this.viewport.ActualWidth, this.viewport.ActualHeight);
double scaleFactor = Math.Max(
currentSize.Width / desiredSize.Width,
currentSize.Height / desiredSize.Height);
camera.Position = new Point3D(0.0, 0.0, 100.0 * scaleFactor); // the known distance of 100.0 is multiplied by scaleFactor