Paul blog

Explaining thoughts and findings is a great way to learn

August 2009 - Posts

Perspective camera animation on a cube in WPF 3D

I have started WPF 3D at my work. Actually, i wanted  to make roll animation effect using WPF 3D. After some R&D, I came up with roll animation using 2D animation, which I described in my previous blog post. At first, I tried to make roll animation using perspective camera rotation.  For that, I have made a sample program, which I am discussing in this blog post. In this sample program, a 3D cube rotates along the y-axis. The animation looks like the following:

fun

To rotate a 3D figure, you have to change the projection point. As camera is used for projection in WPF 3D, here a camera is being moved around the 3D figure to achieve rotate animation in y-direction.

How are images painted on cube surface?

You can make an image brush for any image and a visual brush from any visual. We have no special brushes for 3D graphics and we have to use two-dimensional brushes to paint the surfaces of three-dimensional figures. Material is created from brush. Texture coordinates are used to map which part of the material shows up on which part of the surfaces of three-dimensional figures.

Each GeometryModel3D consists of one MeshGeometry3D and one object of type Material, so we can use only one brush for one MeshGeometry3D figure. To use different brushes for different faces of cube, i have splited the figure into multiple GeometryModel3D objects.

The used c# code for this animation is in the following

   1: public partial class Window1 : Window
   2:   {
   3:       DispatcherTimer _timer = null;
   4:       PerspectiveCamera _perspectiveCamera = null;
   5:       double _angle = 0;        
   6:  
   7:       public Window1()
   8:       {
   9:           this.InitializeComponent();
  10:           this.Loaded += new RoutedEventHandler(Window1_Loaded);
  11:       }
  12:  
  13:       void Window1_Loaded(object sender, RoutedEventArgs e)
  14:       {
  15:           GenerateViewPort();
  16:       }       
  17:  
  18:       private void GenerateViewPort()
  19:       {
  20:           MakeCamera();
  21:  
  22:           Viewport3D Viewport3D1 = new Viewport3D();
  23:           Viewport3D1.Camera = _perspectiveCamera;
  24:           grdAniContainer.Children.Add(Viewport3D1);
  25:           Viewport3D1.Loaded += new RoutedEventHandler(Viewport3D1_Loaded);
  26:  
  27:           ModelVisual3D ModelVisual3D1 = new ModelVisual3D();
  28:           Viewport3D1.Children.Add(ModelVisual3D1);
  29:  
  30:  
  31:           Model3DGroup Model3DGroup1 = new Model3DGroup();
  32:           ModelVisual3D1.Content = Model3DGroup1;
  33:  
  34:           AmbientLight AmbientLight1 = new AmbientLight();
  35:           AmbientLight1.Color = Colors.Gray;
  36:           Model3DGroup1.Children.Add(AmbientLight1);
  37:  
  38:           DirectionalLight DirectionalLight1 = new DirectionalLight();
  39:           DirectionalLight1.Color = Colors.Gray;
  40:           DirectionalLight1.Direction = ((Vector3D)new Vector3DConverter().ConvertFromString("-1,-3,-2"));
  41:           Model3DGroup1.Children.Add(DirectionalLight1);
  42:  
  43:           DirectionalLight1 = new DirectionalLight();
  44:           DirectionalLight1.Color = Colors.Gray;
  45:           DirectionalLight1.Direction = ((Vector3D)new Vector3DConverter().ConvertFromString("1,-2,3"));
  46:           Model3DGroup1.Children.Add(DirectionalLight1);
  47:  
  48:           Model3DGroup Model3DGroup2 = new Model3DGroup();
  49:           Model3DGroup1.Children.Add(Model3DGroup2);
  50:  
  51:           GeometryModel3D GeometryModel3D1 = new GeometryModel3D();
  52:           Model3DGroup2.Children.Add(GeometryModel3D1);
  53:  
  54:  
  55:           MeshGeometry3D MeshGeometry3D1 = new MeshGeometry3D();
  56:           MeshGeometry3D1.Positions = ((Point3DCollection)new Point3DCollectionConverter().ConvertFromString("-1,-1,-1 1,-1,-1 1,-1,1 -1,-1,1"));
  57:           MeshGeometry3D1.TriangleIndices = ((Int32Collection)new Int32CollectionConverter().ConvertFromString("0,1,2 0,2,3"));
  58:           MeshGeometry3D1.TextureCoordinates = ((PointCollection)new PointCollectionConverter().ConvertFromString("0,0 0,1 1,1 1,0"));
  59:           GeometryModel3D1.Geometry = MeshGeometry3D1;
  60:  
  61:          
  62:           DiffuseMaterial DiffuseMaterial1 = new DiffuseMaterial();
  63:           GeometryModel3D1.Material = DiffuseMaterial1;
  64:  
  65:  
  66:           ImageBrush ImageBrush1 = new ImageBrush();
  67:           ImageBrush1.ImageSource = loadBitmap(Fun.Resource1.DSC06460);
  68:           DiffuseMaterial1.Brush = ImageBrush1;
  69:  
  70:           GeometryModel3D GeometryModel3D2 = new GeometryModel3D();
  71:           Model3DGroup2.Children.Add(GeometryModel3D2);
  72:  
  73:  
  74:           MeshGeometry3D1 = new MeshGeometry3D();
  75:           MeshGeometry3D1.Positions = ((Point3DCollection)new Point3DCollectionConverter().ConvertFromString("1,1,1 1,1,-1 -1,1,-1 -1,1,1"));
  76:           MeshGeometry3D1.TriangleIndices = ((Int32Collection)new Int32CollectionConverter().ConvertFromString("0,1,2 0,2,3"));
  77:           MeshGeometry3D1.TextureCoordinates = ((PointCollection)new PointCollectionConverter().ConvertFromString("0,0 0,1 1,1 1,0"));
  78:           GeometryModel3D2.Geometry = MeshGeometry3D1;
  79:  
  80:  
  81:           DiffuseMaterial DiffuseMaterial2 = new DiffuseMaterial();
  82:           GeometryModel3D2.Material = DiffuseMaterial2;
  83:  
  84:  
  85:           ImageBrush1 = new ImageBrush();
  86:           ImageBrush1.ImageSource = loadBitmap(Fun.Resource1.DSC06461);
  87:           DiffuseMaterial2.Brush = ImageBrush1;
  88:  
  89:           GeometryModel3D GeometryModel3D3 = new GeometryModel3D();
  90:           Model3DGroup2.Children.Add(GeometryModel3D3);
  91:  
  92:  
  93:           MeshGeometry3D1 = new MeshGeometry3D();
  94:           MeshGeometry3D1.Positions = ((Point3DCollection)new Point3DCollectionConverter().ConvertFromString("-1,1,-1 -1,-1,-1 -1,-1,1 -1,1,1"));
  95:           MeshGeometry3D1.TriangleIndices = ((Int32Collection)new Int32CollectionConverter().ConvertFromString("0,1,2 0,2,3"));
  96:           MeshGeometry3D1.TextureCoordinates = ((PointCollection)new PointCollectionConverter().ConvertFromString("0,0 0,1 1,1 1,0"));
  97:           GeometryModel3D3.Geometry = MeshGeometry3D1;
  98:  
  99:  
 100:           DiffuseMaterial DiffuseMaterial3 = new DiffuseMaterial();
 101:           GeometryModel3D3.Material = DiffuseMaterial3;
 102:  
 103:  
 104:           ImageBrush1 = new ImageBrush();
 105:           ImageBrush1.ImageSource = loadBitmap(Fun.Resource1.DSC06462);
 106:           DiffuseMaterial3.Brush = ImageBrush1;
 107:  
 108:           GeometryModel3D GeometryModel3D4 = new GeometryModel3D();
 109:           Model3DGroup2.Children.Add(GeometryModel3D4);
 110:  
 111:  
 112:           MeshGeometry3D1 = new MeshGeometry3D();
 113:           MeshGeometry3D1.Positions = ((Point3DCollection)new Point3DCollectionConverter().ConvertFromString("1,1,1 1,-1,1 1,-1,-1 1,1,-1"));
 114:           MeshGeometry3D1.TriangleIndices = ((Int32Collection)new Int32CollectionConverter().ConvertFromString("0,1,2 0,2,3"));
 115:           MeshGeometry3D1.TextureCoordinates = ((PointCollection)new PointCollectionConverter().ConvertFromString("0,0 0,1 1,1 1,0"));
 116:           GeometryModel3D4.Geometry = MeshGeometry3D1;
 117:  
 118:  
 119:           DiffuseMaterial DiffuseMaterial4 = new DiffuseMaterial();
 120:           GeometryModel3D4.Material = DiffuseMaterial4;
 121:  
 122:  
 123:           ImageBrush1 = new ImageBrush();
 124:           ImageBrush1.ImageSource = loadBitmap(Fun.Resource1.DSC06463);
 125:           DiffuseMaterial4.Brush = ImageBrush1;
 126:  
 127:           GeometryModel3D GeometryModel3D5 = new GeometryModel3D();
 128:           Model3DGroup2.Children.Add(GeometryModel3D5);
 129:  
 130:  
 131:           MeshGeometry3D1 = new MeshGeometry3D();
 132:           MeshGeometry3D1.Positions = ((Point3DCollection)new Point3DCollectionConverter().ConvertFromString("1,1,-1 1,-1,-1 -1,-1,-1 -1,1,-1"));
 133:           MeshGeometry3D1.TriangleIndices = ((Int32Collection)new Int32CollectionConverter().ConvertFromString("0,1,2 0,2,3"));
 134:           MeshGeometry3D1.TextureCoordinates = ((PointCollection)new PointCollectionConverter().ConvertFromString("0,0 0,1 1,1 1,0"));
 135:           GeometryModel3D5.Geometry = MeshGeometry3D1;
 136:  
 137:  
 138:           DiffuseMaterial DiffuseMaterial5 = new DiffuseMaterial();
 139:           GeometryModel3D5.Material = DiffuseMaterial5;
 140:  
 141:  
 142:           ImageBrush1 = new ImageBrush();
 143:           ImageBrush1.ImageSource = loadBitmap(Fun.Resource1.DSC06460);
 144:           DiffuseMaterial5.Brush = ImageBrush1;
 145:  
 146:           GeometryModel3D GeometryModel3D6 = new GeometryModel3D();
 147:           Model3DGroup2.Children.Add(GeometryModel3D6);
 148:  
 149:  
 150:           MeshGeometry3D1 = new MeshGeometry3D();
 151:           MeshGeometry3D1.Positions = ((Point3DCollection)new Point3DCollectionConverter().ConvertFromString("-1,1,1 -1,-1,1 1,-1,1 1,1,1"));
 152:           MeshGeometry3D1.TriangleIndices = ((Int32Collection)new Int32CollectionConverter().ConvertFromString("0,1,2 0,2,3"));
 153:           MeshGeometry3D1.TextureCoordinates = ((PointCollection)new PointCollectionConverter().ConvertFromString("0,0 0,1 1,1 1,0"));
 154:           GeometryModel3D6.Geometry = MeshGeometry3D1;
 155:  
 156:           DiffuseMaterial DiffuseMaterial6 = new DiffuseMaterial();
 157:           GeometryModel3D6.Material = DiffuseMaterial6;           
 158:  
 159:           VisualBrush VisualBrush1 = new VisualBrush();
 160:           DiffuseMaterial6.Brush = VisualBrush1;
 161:           Label Label1 = new Label();
 162:           Label1.Content = "Razan";
 163:           Label1.Foreground = new SolidColorBrush(Colors.Orchid);
 164:           Label1.Background = GetRectangleDrawingBrush();
 165:           VisualBrush1.Visual = Label1;            
 166:       }
 167:  
 168:       private void MakeCamera()
 169:       {
 170:           Transform3DGroup transform3DGroup = new Transform3DGroup();
 171:           RotateTransform3D rotateTransform3D_1 = new RotateTransform3D();
 172:           AxisAngleRotation3D axisAngleRotation3D_1 = new AxisAngleRotation3D(new Vector3D(0, 1, 0), 0);
 173:  
 174:  
 175:           transform3DGroup.Children.Add(rotateTransform3D_1);
 176:  
 177:  
 178:           _perspectiveCamera = new PerspectiveCamera();
 179:           _perspectiveCamera.Position = new Point3D(0, 0, 5);
 180:           _perspectiveCamera.LookDirection = new Vector3D(0, 0, -5);
 181:           _perspectiveCamera.UpDirection = new Vector3D(0, 1, 0);
 182:           _perspectiveCamera.FieldOfView = 45;
 183:           _perspectiveCamera.Transform = transform3DGroup;
 184:       }
 185:       private DrawingBrush GetRectangleDrawingBrush()
 186:       {         
 187:  
 188:           DrawingBrush DrawingBrush1 = new DrawingBrush();
 189:           DrawingBrush1.Viewport = ((Rect)new RectConverter().ConvertFromString("0,0,0.25,0.25"));
 190:           DrawingBrush1.TileMode = TileMode.Tile;
 191:            
 192:  
 193:           DrawingGroup DrawingGroup1 = new DrawingGroup();
 194:           DrawingBrush1.Drawing = DrawingGroup1;
 195:  
 196:           GeometryDrawing GeometryDrawing1 = new GeometryDrawing();
 197:           GeometryDrawing1.Brush = ((Brush)new BrushConverter().ConvertFromString("White"));
 198:           DrawingGroup1.Children.Add(GeometryDrawing1);
 199:  
 200:  
 201:           RectangleGeometry RectangleGeometry1 = new RectangleGeometry();
 202:           RectangleGeometry1.Rect = ((Rect)new RectConverter().ConvertFromString("0,0,100,100"));
 203:           GeometryDrawing1.Geometry = RectangleGeometry1;
 204:  
 205:           GeometryDrawing GeometryDrawing2 = new GeometryDrawing();
 206:           DrawingGroup1.Children.Add(GeometryDrawing2);
 207:  
 208:  
 209:           GeometryGroup GeometryGroup1 = new GeometryGroup();
 210:           GeometryDrawing2.Geometry = GeometryGroup1;
 211:  
 212:           RectangleGeometry1 = new RectangleGeometry();
 213:           RectangleGeometry1.Rect = ((Rect)new RectConverter().ConvertFromString("0,0,50,50"));
 214:           GeometryGroup1.Children.Add(RectangleGeometry1);
 215:  
 216:           RectangleGeometry1 = new RectangleGeometry();
 217:           RectangleGeometry1.Rect = ((Rect)new RectConverter().ConvertFromString("50,50,50,50"));
 218:           GeometryGroup1.Children.Add(RectangleGeometry1);
 219:  
 220:  
 221:           LinearGradientBrush LinearGradientBrush1 = new LinearGradientBrush();
 222:           GeometryDrawing2.Brush = LinearGradientBrush1;
 223:  
 224:           GradientStop GradientStop1 = new GradientStop();
 225:           GradientStop1.Offset = 0.0;
 226:           GradientStop1.Color = Colors.Black; 
 227:           LinearGradientBrush1.GradientStops.Add(GradientStop1);
 228:  
 229:           GradientStop1 = new GradientStop();
 230:           GradientStop1.Offset = 1.0;
 231:           GradientStop1.Color = Colors.Gray; 
 232:           LinearGradientBrush1.GradientStops.Add(GradientStop1);
 233:  
 234:           return DrawingBrush1;
 235:  
 236:       }
 237:  
 238:  
 239:       void Viewport3D1_Loaded(object sender, RoutedEventArgs e)
 240:       {
 241:           _timer = new DispatcherTimer();
 242:           _timer.Interval = TimeSpan.FromSeconds(.25);
 243:           _timer.Tick += new EventHandler(_timer_Tick);
 244:           _timer.IsEnabled = true;
 245:       }
 246:       
 247:       void _timer_Tick(object sender, EventArgs e)
 248:       {
 249:           Transform3DGroup transform3DGroup = new Transform3DGroup();
 250:           RotateTransform3D rotateTransform3D_1 = new RotateTransform3D();
 251:           AxisAngleRotation3D axisAngleRotation3D_1 = new AxisAngleRotation3D(new Vector3D(0, 1, 0), _angle );
 252:           rotateTransform3D_1.Rotation = axisAngleRotation3D_1;          
 253:           transform3DGroup.Children.Add(rotateTransform3D_1);             
 254:           _perspectiveCamera.Transform = transform3DGroup;
 255:           _angle = _angle + 2;
 256:       }
 257:       public static BitmapSource loadBitmap(System.Drawing.Bitmap source)
 258:       {
 259:           return System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(source.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty,
 260:               System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
 261:       }
 262:   }

How is this animation achieved?

Here we have used perspective camera for projection. To make this animation, I have rotated perspective camera in y direction. As we know second point of vector 3D indicates y coordinate. So AxisAngleRotation3D(new Vector3D(0, 1, 0), _angle ) directs to rotate the 3D object(here cube) to rotate _angle degree in y-axis. Here at each tick event, we have incremented the value of _angle, so it rotates in y direction. If you would like to rotate in x direction you can use AxisAngleRotation3D(new Vector3D(1, 0, 0), _angle ), then it will rotate in x-direction. Here visual brush is used to paint label UIelement, which contains "Razan".

The used XAML for this is in the following:

   1: <Window
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     x:Class="Fun.Window1"
   5:     x:Name="Window"
   6:     Title="Fun"
   7:     Height="400" Width="400">
   8:     <Grid Name="grdAniContainer"></Grid>
   9: </Window>

You can download the sample code from here. Hope this will save some of your time.

Creating a Roll animation effect in WPF

I know one picture can tell what the Roll animation looks like instead of writing many sentences. The animation looks like the following:

roll

Opacity masks allow us to make portions of a UIelement or visual either transparent or partially transparent. To apply opacity mask, we can assign a brush to the Opacity Mask property of an element or Visual to provide a mask to the opacity. There is a variety of brushes in WPF: Solid Color Brush, Image Brush, Linear Gradient Brush, and Visual Brush. You can apply any of them you want. The regions, which are not covered by the mask, will remain transparent. The Alpha value of ARGB is used to specify the opacity of the color. An alpha value of 00 indicates completely transparent color and an alpha value of FF indicates fully opaque

How can we give an image 3D look in WPF?

We have used transparent Gradient Stop to give the image 3D look. We have made a Linear Gradient Brush with some gradient stops where centre, top, and bottom  are transparent. Then we set the LinearGradientBrush as the opacity mask of Image.

How can we  reverse the image?

To reverse the image here i have used scale transform. As we want to reverse the image in y Direction so the scale value for y-axis is -1 and scale value for x-axis is 1.

The used c# code for Roll animation is in the following

   1: public partial class Window1 : Window
   2:    {
   3:        DispatcherTimer _timer = null;
   4:        double _rollTopLeft = 0;
   5:        double _imageWidth = 400;
   6:        double _imageHeight = 300;
   7:        double _offset = 3;
   8:        double _3DSize = 50;
   9:        public Window1()
  10:        {
  11:            InitializeComponent();
  12:            this.Loaded += new RoutedEventHandler(Window1_Loaded);
  13:        }
  14:        void Window1_Loaded(object sender, RoutedEventArgs e)
  15:        {      
  16:            LinearGradientBrush maskBrush = new LinearGradientBrush();
  17:            maskBrush.StartPoint = new Point(0, 0);
  18:            maskBrush.EndPoint = new Point(0, 1);
  19:  
  20:            GradientStop BlackStop1 = new GradientStop(Color.FromArgb(30, 255, 255, 255), 0.0);
  21:            GradientStop BlackStop3 = new GradientStop(Colors.Black, 0.0);
  22:            GradientStop BlackStop4 = new GradientStop(Colors.Black, .9);
  23:            GradientStop transparentStop = new GradientStop(Color.FromArgb(175, 255, 255, 255), .5);
  24:            GradientStop BlackStop2 = new GradientStop(Color.FromArgb(100, 255, 255, 255), 1);
  25:  
  26:            maskBrush.GradientStops.Add(BlackStop1);
  27:            maskBrush.GradientStops.Add(transparentStop);
  28:            maskBrush.GradientStops.Add(BlackStop2);
  29:            maskBrush.GradientStops.Add(BlackStop3);
  30:            maskBrush.GradientStops.Add(BlackStop4);
  31:  
  32:            CanAni.OpacityMask = maskBrush;
  33:          
  34:            _timer = new DispatcherTimer();
  35:            _timer.Interval = TimeSpan.FromSeconds(.1);
  36:            _timer.Tick += new EventHandler(_timer_Tick);
  37:            _timer.IsEnabled = true;
  38:        }
  39:  
  40:        void _timer_Tick(object sender, EventArgs e)
  41:        {
  42:            Image im = new Image();
  43:            im.Width = _imageWidth;
  44:            im.Height = _imageHeight;
  45:            im.Source = loadBitmap(RollAnimation.Properties.Resources.bedrijven3);    
  46:            ScaleTransform scaleTransform = new ScaleTransform(1, -1, 0, 0);
  47:            im.LayoutTransform = scaleTransform;
  48:  
  49:            _rollTopLeft = _rollTopLeft + _offset;
  50:            image.Source = loadBitmap(RollAnimation.Properties.Resources.bedrijven3);
  51:            image.Clip = new RectangleGeometry(new Rect(0, 0, _imageWidth, _rollTopLeft));
  52:  
  53:            double yPosition = _rollTopLeft + _3DSize;
  54:            double height = _3DSize;
  55:            if (Math.Abs((_imageHeight - _rollTopLeft)) < _3DSize)
  56:            {
  57:                height = Math.Abs(image.Height - _rollTopLeft);
  58:                CanAni.Height = height;
  59:                CanWrap.Height = height;
  60:                yPosition = _rollTopLeft;
  61:                Linetwo.Y1 = height;
  62:                Linetwo.Y2 = height;
  63:            }
  64:            Lineone.Visibility = Visibility.Visible;
  65:            Linetwo.Visibility = Visibility.Visible;
  66:            if (_imageHeight == _rollTopLeft)
  67:            {
  68:                _timer.IsEnabled = false;
  69:                Lineone.Visibility = Visibility.Collapsed;
  70:                Linetwo.Visibility = Visibility.Collapsed;
  71:            }
  72:            im.Clip = new RectangleGeometry(new Rect(0, yPosition, _imageWidth, height));
  73:            VisualBrush vb = new VisualBrush(im as Visual);
  74:            CanAni.Background = vb;
  75:            Canvas.SetTop(CanWrap, _rollTopLeft);
  76:        }
  77:        public static BitmapSource loadBitmap(System.Drawing.Bitmap source)
  78:        {
  79:            return System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(source.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty,
  80:                System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
  81:        }
  82:    }

Here four canvases are used here. The outer canvas is the main container for this application. The second canvas is used to wrap necessary UIelements to achieve this animation. The third canvas is used to make transparent portions while the fourth canvas is used to achieve 3D look. I have set an image background to the fourth canvas clipping certain portion of the image. To make this canvas moving, I am changing the top position of the canvas continuously.

This trick is applicable to any UIelement type. Here I have taken image as the UIelement type to implement the Roll animation. I have calculated the height and width of the image and started animation from (0,0) point. Every WPF UIelement type has a clip property and you can set any geometry object to clip. However, for this animation, we have used Rectangle geometry as clip geometry.

As I am changing the brush of animated canvas at runtime, property based animation does not work here. For that reason, I have used dispatcher timer based animation. I am using here dispatcher timer instead of classic Timer as dispatcher timer runs in UI thread and I do not need to write thread switching code

The used XAML for this is in the following

   1: <Window x:Class="RollAnimation.Window1"
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     Title="Roll Animation" Height="600" Width="600">
   5:     <Canvas  x:Name="CanOuter" Height="300" Width="400">
   6:         <Image  Canvas.Left="0" Canvas.Top="0"  Height="300" Width="400" Stretch="Fill"  Name="image" />
   7:         <Canvas Canvas.Left="0" x:Name="CanWrap" Canvas.Top="0" Background="White" Margin="0,0" Height="50" Width="400"  >
   8:             <Canvas x:Name="CanAni"   Height="50" Width="400"  >
   9:                 <Line  Visibility="Collapsed"  X1="01" Y1="00" Name="Lineone" X2="400" Y2="0" Stroke="Black"    StrokeThickness="1">
  10:                     <Line.BitmapEffect>
  11:                         <BlurBitmapEffect Radius="1" KernelType="Box" />
  12:                     </Line.BitmapEffect>
  13:                 </Line>
  14:                 <Line Visibility="Collapsed" X1="00" Y1="50" X2="400" Y2="50" Name="Linetwo" Stroke="Black" StrokeThickness="1">
  15:                     <Line.BitmapEffect>
  16:                         <BlurBitmapEffect Radius="1" KernelType="Box" />
  17:                     </Line.BitmapEffect>
  18:                 </Line>
  19:             </Canvas>
  20:         </Canvas>
  21:     </Canvas>
  22: </Window>

You can download the sample code from here. Hope this will save some of your time.

Wheel Clockwise animation in WPF

This animation is like the Wheel Clockwise, 1 spoke, animation of MS power point. Some people also call this animation as Radial animation. This animation starts from a line, which points from the centre to top middle, and then it rotates 360 degrees in clockwise direction and makes the underlying object visible. I know one picture can tell what is the animation looks like instead of writing many sentences. The animation looks like the following:

Wheel

How this animation effect is achieved?

As we know, every WPF UIelement type has a clip property and you can set any geometry object to clip. I have used this clip property to make this animation. I have changed the geometry object with time so that it looks like a wheel animation. All the challenges to make this animation in this way is making the exact geometry object with time.

I am showing this animation for Image type, which is a Uielement type. However, this trick is applicable to any UIelement type. PathGeometry is used here as the geometry to clip the image. Line segments are added to the path geometry dynamically to make the visible region bigger with time. I have calculated the height and width of the image. Then set the first line segment from (width/2, height/2) to (Width/2, 0). Then added the other line segments when they are needed and also change their points. For this animation, i have used DispatcherTimer, which runs in UI thread instead of worker thread. The code for this is in the following:

   1: public partial class Window1 : Window
   2:     {
   3:         DispatcherTimer _timer = null;
   4:         Ellipse _grdMain = null;
   5:         ContentPresenter _circleContentPresenter = null;
   6:         LineSegment LineSegment2 = null;
   7:         PathFigure PathFigure1 = null;
   8:         bool ISIncrementdirectionX = true;
   9:         bool IsIncrementX = true;
  10:         bool IsIncrementY = true;
  11:         LineSegment LineSegmentFirstcorner = null;
  12:         LineSegment LineSegmentseconcorner = null;
  13:         LineSegment LineSegmentThirdcorner = null;
  14:         LineSegment LineSegmentFourthcorner = null;
  15:         double _ofsetOfAnimation = 2;
  16:  
  17:         double _height = 0;
  18:         double _width = 0;
  19:  
  20:         public Window1()
  21:         {
  22:             InitializeComponent();
  23:             this.Loaded += new RoutedEventHandler(Window1_Loaded);
  24:         }
  25:  
  26:         void Window1_Loaded(object sender, RoutedEventArgs e)
  27:         {
  28:  
  29:             image.Source = loadBitmap(WheelClockwise.Properties.Resources.images1);
  30:             _height = image.Height;
  31:             _width = image.Width;
  32:             string topCentre = Convert.ToString(_width / 2) + ",0";
  33:             string centre = Convert.ToString(_width / 2) + "," + Convert.ToString(_height / 2);        
  34:  
  35:             PathGeometry PathGeometry1 = new PathGeometry();      
  36:             PathFigure1 = new PathFigure();
  37:             PathFigure1.StartPoint = ((Point)new PointConverter().ConvertFromString(centre));
  38:             PathGeometry1.Figures.Add(PathFigure1);
  39:             LineSegment LineSegmentdummy = new LineSegment();
  40:             LineSegmentdummy.Point = ((Point)new PointConverter().ConvertFromString(topCentre));
  41:             PathFigure1.Segments.Add(LineSegmentdummy);
  42:             LineSegmentseconcorner = new LineSegment();
  43:             LineSegmentseconcorner.Point = ((Point)new PointConverter().ConvertFromString(topCentre));
  44:             PathFigure1.Segments.Add(LineSegmentseconcorner);
  45:             LineSegmentThirdcorner = new LineSegment();
  46:             LineSegmentThirdcorner.Point = ((Point)new PointConverter().ConvertFromString(topCentre));
  47:             PathFigure1.Segments.Add(LineSegmentThirdcorner);
  48:             LineSegmentFourthcorner = new LineSegment();
  49:             LineSegmentFourthcorner.Point = ((Point)new PointConverter().ConvertFromString(topCentre));
  50:             PathFigure1.Segments.Add(LineSegmentFourthcorner);
  51:             LineSegmentFirstcorner = new LineSegment();
  52:             LineSegmentFirstcorner.Point = ((Point)new PointConverter().ConvertFromString(topCentre));
  53:             PathFigure1.Segments.Add(LineSegmentFirstcorner);
  54:             LineSegment2 = new LineSegment();
  55:             LineSegment2.Point = ((Point)new PointConverter().ConvertFromString(topCentre));
  56:             PathFigure1.Segments.Add(LineSegment2);
  57:             image.Clip = PathGeometry1;
  58:             _timer = new DispatcherTimer();
  59:             _timer.Interval = TimeSpan.FromSeconds(0.01);
  60:             _timer.Tick += new EventHandler(_timer_Tick);
  61:             _timer.IsEnabled = true;
  62:  
  63:         }
  64:         public static BitmapSource loadBitmap(System.Drawing.Bitmap source)
  65:         {
  66:             return System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(source.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty,
  67:                 System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
  68:         }
  69:  
  70:         void _timer_Tick(object sender, EventArgs e)
  71:         {
  72:             if ((LineSegment2.Point.X <= 0) && (LineSegment2.Point.Y <= 0))
  73:             {
  74:  
  75:                 LineSegmentFirstcorner.Point = new Point(LineSegment2.Point.X, LineSegment2.Point.Y);
  76:                 ISIncrementdirectionX = true;
  77:                 IsIncrementX = true;
  78:             }
  79:             else if (((LineSegment2.Point.X >= _width) && (LineSegment2.Point.Y <= 0)))
  80:             {
  81:                 LineSegmentseconcorner.Point = new Point(LineSegment2.Point.X, LineSegment2.Point.Y);
  82:                 LineSegmentThirdcorner.Point = new Point(LineSegment2.Point.X, LineSegment2.Point.Y);
  83:                 LineSegmentFourthcorner.Point = new Point(LineSegment2.Point.X, LineSegment2.Point.Y);
  84:                 LineSegmentFirstcorner.Point = new Point(LineSegment2.Point.X, LineSegment2.Point.Y);
  85:                 ISIncrementdirectionX = false;
  86:                 IsIncrementY = true;
  87:             }
  88:  
  89:             else if ((LineSegment2.Point.X >= _width) && (LineSegment2.Point.Y >= _height))
  90:             {
  91:                 LineSegmentThirdcorner.Point = new Point(LineSegment2.Point.X, LineSegment2.Point.Y);
  92:                 LineSegmentFourthcorner.Point = new Point(LineSegment2.Point.X, LineSegment2.Point.Y);
  93:                 LineSegmentFirstcorner.Point = new Point(LineSegment2.Point.X, LineSegment2.Point.Y);
  94:                 ISIncrementdirectionX = true;
  95:                 IsIncrementX = false;
  96:             }
  97:  
  98:             else if ((LineSegment2.Point.X <= 0) && (LineSegment2.Point.Y >= _height))
  99:             {
 100:                 LineSegmentFourthcorner.Point = new Point(LineSegment2.Point.X, LineSegment2.Point.Y);
 101:                 LineSegmentFirstcorner.Point = new Point(LineSegment2.Point.X, LineSegment2.Point.Y);
 102:                 ISIncrementdirectionX = false;
 103:                 IsIncrementY = false;
 104:             }
 105:  
 106:             double x = 0, y = 0;
 107:  
 108:             if (ISIncrementdirectionX == true)
 109:             {
 110:                 if (IsIncrementX)
 111:                 {
 112:                     x = LineSegment2.Point.X + _ofsetOfAnimation;
 113:                     y = LineSegment2.Point.Y;
 114:                 }
 115:                 else
 116:                 {
 117:                     x = LineSegment2.Point.X - _ofsetOfAnimation;
 118:                     y = LineSegment2.Point.Y;
 119:                 }
 120:             }
 121:             else
 122:             {
 123:                 if (IsIncrementY)
 124:                 {
 125:                     x = LineSegment2.Point.X;
 126:                     y = LineSegment2.Point.Y + _ofsetOfAnimation;
 127:                 }
 128:                 else
 129:                 {
 130:                     x = LineSegment2.Point.X;
 131:                     y = LineSegment2.Point.Y - _ofsetOfAnimation;
 132:                 }
 133:  
 134:             }
 135:  
 136:             LineSegment2.Point = new Point(x, y);
 137:             if ((LineSegment2.Point.X == _width / 2) && (LineSegment2.Point.Y == 0)) _timer.IsEnabled = false;
 138:  
 139:         }
 140:     }

The used XAML for this is in the following

   1: <Window x:Class="WheelClockwise.Window1"
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     Title="Window1" Height="400" Width="400">
   5:     <Canvas Name="canMain"  Background="White"    Height="300" Width="300"  >  
   6:         <Image Height="300" Width="300" Stretch="Fill"    Name="image" />
   7:     </Canvas>
   8: </Window>

You can download the sample code from here. Hope this will save some of your time.

How to pick numbers at random from a given set of numbers in c#

The Random class defined in the .NET Framework provides functionality to generate pseudo-random number. According to MSDN, the current implementation of the Random class is based on Donald E. Knuth's subtractive random number generator algorithm. The random number generation starts from a seed value. If the same seed is used repeatedly, the same series of numbers is generated. One way to produce different sequences is to make the seed value time-dependent, thereby producing a different series with each new instance of Random.

However, current implementation does not provide the following two functionalities.

  1. Picking random numbers from a given set of numbers: For example, you want random number from the given set of numbers. You have a set of numbers like (2, 4, 13, and 21).You would like to pick one number at a time at random from the list.
  2. Picking random numbers from a range of numbers with no repetition: Current implementation return random numbers within a range with some duplicate number. For example, you want random number from a range of numbers like (4 to 21). You would like to pick one number at a time at random from that range without any repetition or duplicate

However, these two requirements are easy to implement by using the Random class of the .NET Framework. I needed these two functionalities for one of my project, so I wrote a class to implement these two features. I think that this is a trivial work but I am posting it because it can save some of your time. To implement these two functionalities following class named RandomNumberFromAGivenSetOfNumbers is written.

   1: class RandomNumberFromAGivenSetOfNumbers
   2:    {
   3:        List<int> _setOfNumbers = new List<int>();
   4:  
   5:        public List<int> SetOfNumbers
   6:        {
   7:            get { return _setOfNumbers; }
   8:            set { _setOfNumbers = value; }
   9:        }
  10:        Random _random = new Random();
  11:  
  12:        public RandomNumberFromAGivenSetOfNumbers()
  13:        {
  14:  
  15:        }
  16:        public RandomNumberFromAGivenSetOfNumbers(int min, int max)
  17:        {
  18:            for (int i = min; i <= max; i++)
  19:            {
  20:                _setOfNumbers.Add(i);
  21:            }
  22:        }
  23:  
  24:        public int Next()
  25:        {
  26:            if (_setOfNumbers.Count > 0)
  27:            {
  28:                int nextNumberIndex = _random.Next(_setOfNumbers.Count);
  29:                int val = _setOfNumbers[nextNumberIndex];
  30:                _setOfNumbers.RemoveAt(nextNumberIndex);
  31:                return val;
  32:            }
  33:            return -1;
  34:        }
  35:    }

The used code for testing these two features is in the following

   1: static void Main(string[] args)
   2:        {
   3:            Console.WriteLine("Picking random numbers from a range of numbers with no repetition(Range is 0-24)");
   4:            Console.WriteLine();
   5:            RandomNumberFromAGivenSetOfNumbers rdm = new RandomNumberFromAGivenSetOfNumbers(0, 24);
   6:            string str = string.Empty;
   7:            for (int i = 0; i < 25; i++)
   8:            {
   9:                str += rdm.Next().ToString() + " ";
  10:            }
  11:            Console.WriteLine(str);
  12:            Console.WriteLine();
  13:            Console.WriteLine();
  14:  
  15:  
  16:            Console.WriteLine("Picking   random numbers from a given set of numbers( 2, 4, 13, and 21)");
  17:            Console.WriteLine();
  18:            RandomNumberFromAGivenSetOfNumbers rdm1 = new RandomNumberFromAGivenSetOfNumbers();
  19:            rdm1.SetOfNumbers.Add(2);
  20:            rdm1.SetOfNumbers.Add(4);
  21:            rdm1.SetOfNumbers.Add(13);
  22:            rdm1.SetOfNumbers.Add(21);
  23:            string str1 = string.Empty;
  24:            for (int i = 0; i < 4; i++)
  25:            {
  26:                str1 += rdm1.Next().ToString() + " ";
  27:            }
  28:            Console.WriteLine(str1);
  29:  
  30:            Console.ReadLine();
  31:        }

The output of the program is in the following:

random

You can download the sample code from here. This is a very trivial work, but I hope this will save some of your time.

Posted: 08-04-2009 8:40 PM by Razan | with no comments
Filed under: ,
Creating marquee/scrolling text in WPF

mar1

A WPF marquee is a scrolling piece of text displayed either horizontally across or vertically down your container. Here i show you how to make a scrolling marquee using Canvas and TextBlock. I will show only left to right in code. However, you can achieve right to left or vertical marquee text just by changing some portion of code using the same technique.

The following three techniques are used to achieve marquee animation:

  • Negative Positioning in canvas: If we set negative value to any of the positioning property of Canvas: Left, Right, Top and Bottom for an UIelement , Canvas draws that Uielement outside of Canvas area.
  • ClipToBounds property of canvas: if you set the value of the ClipToBounds property to true, Canvas will clip its children when they go outside of Canvas.
  • Changing Position of Uielement continuously: By changing continuously any of the canvas positioning property Left, right, Top, Bottom we can get moving text.

How is Left to right marquee text implemented in this sample?

Here a canvas is used as the container of marquee text. To move the text outside of canvas negative Left property of canvas is used. However, if someone uses negative left property of canvas, it will go outside of its parent container but will still remain visible outside of the parent container. To make marquee text invisible outside of canvas CliptoBound property is used. Then using double animation left property of canvas for that element is being changed continuously to get moving text. The whole thing is done using the following code.

   1: void Window1_Loaded(object sender, RoutedEventArgs e)
   2: {
   3:     DoubleAnimation doubleAnimation = new DoubleAnimation();
   4:     doubleAnimation.From = -tbmarquee.ActualWidth;
   5:     doubleAnimation.To = canMain.ActualWidth;
   6:     doubleAnimation.RepeatBehavior = RepeatBehavior.Forever;
   7:     doubleAnimation.Duration = new Duration(TimeSpan.Parse("0:0:10"));
   8:     tbmarquee.BeginAnimation(Canvas.LeftProperty, doubleAnimation);
   9: }

For Left to right marquee: use Left Property of Canvas

For right to left marquee: use Right Property of Canvas

For upward marquee: use Bottom Property of Canvas

For Downward marquee: use Top Property of Canvas

The used XAML for this is in the following

   1: <Window x:Class="MarqueText.Window1"
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     Title="Window1" Height="500" Width="700" >
   5:     <Canvas Background="White" >
   6:         <Canvas Margin="50" Canvas.Left="150" Canvas.Top="100" ClipToBounds="True" Name="canMain" Background="Red"   Height="100" Width="300"  >
   7:             <TextBlock Margin="0,40" FontSize="25" Name="tbmarquee">Bangladesh</TextBlock>
   8:         </Canvas>
   9:     </Canvas>
  10: </Window>

You can download the sample code from here. Hope this will save some of your time.

Making circle animation in WPF

circle2

This animation is like the circle animation of MS power point. You can set any geometry like ellipse geometry to define the visible region of a WPF UIelement type. To make it possible every WPF UIelement type has a clip property and you can set any geometry object to clip. However, for circle animation, we need circle geometry but WPF does not have built-in circle geometry.

How we can make circle geometry in WPF from ellipse Geometry?

An Ellipse Geometry is defined by a center point, an x-radius and a y-radius. As we know an ellipse geometry has two radius: radiousx and radiousy. If we keep the value of these two radiuses same, it will behave as circle. Just we will keep Radiousx = Radiousy all the time.

How this animation effect is achieved?

This trick is applicable to any UIelement type. Here I have taken image as the Uielement type to implement the circle animation. I have calculated the height and width of the image. Then set the centre of circle as (width/2, height/2) to start the animation from centre point. At first we keep the radius of the circle is 0. Then increment the radius proportion to time. For animation, we use double animation. Next thing is how much we will increase the radius. I have just compared the height and width of UIelement and set the radious equal to bigger one divided by two.

   1: EllipseGeometry ellipseGeometry = new EllipseGeometry();     
   2: ellipseGeometry.RadiusX = 0;
   3: ellipseGeometry.RadiusY = 0;
   4: double centrex = image.Source.Width / 2;
   5: double centrey = image.Source.Height / 2;
   6: ellipseGeometry.Center = new Point(centrex, centrey);
   7:  
   8: image.Clip = ellipseGeometry; //The most important line
   9:  
  10: if (image.Source.Width > image.Source.Height)
  11:    animationLength = image.Source.Width / 2;
  12: else
  13:    animationLength = image.Source.Height / 2;
  14: DoubleAnimation a = new DoubleAnimation();
  15: a.From = 0;
  16: a.To = animationLength+50;
  17: a.Duration = new Duration(TimeSpan.Parse("0:0:10"));
  18: ellipseGeometry.BeginAnimation(EllipseGeometry.RadiusXProperty, a);
  19: ellipseGeometry.BeginAnimation(EllipseGeometry.RadiusYProperty, a);

The used XAML for this is in the following

   1: <Window x:Class="circle.Window1"
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     Title="Window1" Height="300" Width="300">
   5:     <Canvas Name="canMain"    Height="300" Width="300"  >            
   6:         <Image   Canvas.Left="100" Canvas.Top="60"  Name="image"/>   
   7:     </Canvas>
   8: </Window>

You can download the sample code from here. Hope this will save some of your time.

Posted: 08-01-2009 5:29 PM by Razan | with no comments
Filed under: , ,