Friday, October 17, 2008

WPF Cover Flow Tutorial : Part 4

We can improve the HCI from Part 3 with some mouse handling.

We add a MouseDown handler method to our Viewport3D :
private void OnViewportMouseDown(object sender, MouseButtonEventArgs e)
{
var rayMeshResult = (RayMeshGeometry3DHitTestResult)VisualTreeHelper.HitTest(viewPort, e.GetPosition(viewPort));
if (rayMeshResult != null)
{
for (int i = 0; i < coverList.Count; i++)
{
if (coverList[i].Matches(rayMeshResult.MeshHit))
{
UpdateIndex(i);
break;
}
}
}
}
This method uses the VisualTreeHelper to find which mesh was hitten by the mouse click. If there is a match, we update the index like we did before. Matches are found with this new Cover method :
public bool Matches(MeshGeometry3D mesh)
{
foreach (GeometryModel3D part in modelGroup.Children)
if (part.Geometry == mesh)
return true;
return false;
}
We also modify the TestWindow constructor to load all the pictures from a sample folder :
public TestWindow()
{
InitializeComponent();
var imageDir = new DirectoryInfo(@"c:\_covers");
int doneImages = 0;
foreach (FileInfo image in imageDir.GetFiles("*.jpg"))
{
var cover = new Cover(image.FullName, doneImages++);
coverList.Add(cover);
visualModel.Children.Add(cover);
}
}
Mouse click may increment or decrement the index by more than one cover. That's wy we need to fix the UpdateIndex method :
private void UpdateIndex(int newIndex)
{
if (index != newIndex)
{
int oldIndex = index;
index = newIndex;
if (index > oldIndex)
for (int i = oldIndex; i <= index; i++)
RotateCover(i);
else
for (int i = oldIndex; i >= index; i--)
RotateCover(i);
camera.Position = new Point3D(.2 * index, camera.Position.Y, camera.Position.Z);
}
}
It is now time to play with more covers. Let's try the current application with a sample folder containing 500+ covers. These covers are usually between 400x400 and 500x500 pixels. The next Process Explorer capture shows that the application needs a lot of cpu and memory resources :
  • Loading all covers brings the cpu to 100%.
  • Browing all covers with the Right key pressed also needs a lot of cpu. Moreover, this rises the cache size from 300 MB to more than 1 GB.
It is possible to improve performance if we use thumbnails for all covers. We will store thumbnails in a subfolder.
private ImageSource LoadImageSource(string imagePath)
{
var imageFile = new FileInfo(imagePath);
var thumbnailDir = new DirectoryInfo(Path.Combine(imageFile.Directory.FullName, "tn"));
if (!thumbnailDir.Exists)
thumbnailDir.Create();
var thumbnail = new FileInfo(Path.Combine(thumbnailDir.FullName, imageFile.Name));
if (!thumbnail.Exists)
{
Image source = Image.FromFile(imageFile.FullName);
int height = source.Height;
int width = source.Width;
int factor = (height - 1) / 250 + 1;
int smallHeight = height / factor;
int smallWidth = width / factor;
Image thumb = source.GetThumbnailImage(smallWidth, smallHeight, null, IntPtr.Zero);
thumb.Save(thumbnail.FullName);
}
return new BitmapImage(new Uri(thumbnail.FullName, UriKind.RelativeOrAbsolute));
}
Thumbnails improve performance while browsing, but it is still slow at startup. We will try to improve this later.

Continue with Part 5.
Edit 2014-02-23 : Code has moved to github.

No comments: