前回の続き。
WPFを使って実験用アプリを作ってみた。
ImageZooming.zip
試してみるとズームアウトがどうもじれったい。
実際現実世界もこんなかんじかもと思いつつも使いづらいのはよろしくない。
そこで、ズームアウト時の拡大率にはズームイン時の拡大率の逆数使うようにしてみた。
それが上のチェックボックス。
いいような気もするが、そんなことするならなんでモデリングしたんだっけか…。
以下ソースコード。
MainWindow.xaml
<Window xClass="ImageZooming.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlnsx="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
Loaded="window_Loaded">
<DockPanel Height="Auto" Width="Auto" LastChildFill="True">
<CheckBox DockPanelDock="Top" Name="checkBox" Content="縮小率に拡大率の逆数を使う" CanvasLeft="109" CanvasTop="59" Checked="checkBox_Checked" Unchecked="checkBox_Unchecked"/>
<ScrollViewer DockPanelDock="Bottom" xName="scrollViewer" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"
PreviewMouseWheel="scrollViewer_PreviewMouseWheel">
<Canvas xName="canvas">
<Image xName="image" Stretch="Fill" Width="{Binding Source.PixelWidth, RelativeSource={RelativeSource Self}}" Height="{Binding Source.PixelHeight, RelativeSource={RelativeSource Self}}"/>
</Canvas>
</ScrollViewer>
</DockPanel>
</Window>
MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace ImageZooming
{
<summary>
</summary>
public partial class MainWindow : Window
{
const double MAGNIFICATION_LEVEL_1 = 1.1;
const double VIEW_ANGLE = Math.PI / 4;
private TransformGroup _canvasTransformGroup = new TransformGroup();
private int _zoomLevel = 0;
private double _canvasOriginalWidth;
private double _canvasOriginalHeight;
public MainWindow()
{
InitializeComponent();
}
private void window_Loaded(object sender, RoutedEventArgs e)
{
string currentDirectory = System.IO.Directory.GetCurrentDirectory();
string filePath = System.IO.Path.Combine(currentDirectory, "image.jpg");
BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.UriSource = new Uri(filePath);
bi.EndInit();
image.Source = bi;
canvas.Width = image.Width;
canvas.Height = image.Height;
_canvasOriginalWidth = image.Width;
_canvasOriginalHeight = image.Height;
}
void scrollViewer_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
if ((Keyboard.Modifiers & ModifierKeys.Control) != ModifierKeys.None)
{
changeCanvasScale(e.Delta);
e.Handled = true;
}
}
private void changeCanvasScale(int delta)
{
int originalZoomLevel = _zoomLevel;
int zoomType = 0;
if (checkBox.IsChecked.Value)
{
zoomType = 1;
}
double magnification = GetMagnification(zoomType, _zoomLevel);
double centerX = (scrollViewer.HorizontalOffset + scrollViewer.ViewportWidth / 2) / magnification;
double centerY = (scrollViewer.VerticalOffset + scrollViewer.ViewportHeight / 2) / magnification;
if (delta > 0)
{
_zoomLevel += 1;
}
else
{
_zoomLevel -= 1;
}
magnification = GetMagnification(zoomType, _zoomLevel);
if (magnification <= 0 || double.IsInfinity(magnification) || double.IsNegativeInfinity(magnification))
{
_zoomLevel = originalZoomLevel;
return;
}
_canvasTransformGroup.Children.Clear();
_canvasTransformGroup.Children.Add(new ScaleTransform(magnification, magnification));
canvas.RenderTransform = _canvasTransformGroup;
canvas.Width = _canvasOriginalWidth * magnification;
canvas.Height = _canvasOriginalHeight * magnification;
scrollViewer.ScrollToHorizontalOffset(centerX * magnification - scrollViewer.ViewportWidth / 2);
scrollViewer.ScrollToVerticalOffset(centerY * magnification - scrollViewer.ViewportHeight / 2);
}
<summary>
</summary>
<param name="zoomType"></param>
<param name="zoomLevel"></param>
<returns></returns>
private double GetMagnification(int zoomType, int zoomLevel)
{
double x = 0.1 / (2.0 * MAGNIFICATION_LEVEL_1 * Math.Tan(VIEW_ANGLE / 2));
if (zoomType == 0)
{
return 1 / (1 - (x * zoomLevel) * Math.Tan(VIEW_ANGLE / 2) * 2);
}
else
{
double d = 1 / (1 - (x * Math.Abs(zoomLevel)) * Math.Tan(VIEW_ANGLE / 2) * 2);
if (zoomLevel >= 0)
{
return d;
}
else
{
return 1 / d;
}
}
}
private void checkBox_Checked(object sender, RoutedEventArgs e)
{
_zoomLevel = 0;
}
private void checkBox_Unchecked(object sender, RoutedEventArgs e)
{
_zoomLevel = 0;
}
}
}