Wednesday, May 20, 2009

3D projections in Silverlight 3 Beta

I had a quick play with the new Plane Projection feature in the Silverlight 3 beta. Like many others, my first experiments were with taking a rectangular image and flipping it around the X, Y & Z axes (these suffixes will be denoted by * for mentions of properties from now on to save typing the variants). It is useful to think of these as long poles running in the respective directions with objects being transformed around these ‘spindles’ using the Rotation* properties. The CenterOfRotation* properties allow a displacement from the centre of the axis so that an element traces a circle around an axis when rotated rather than flips on that axis, so:

        <Image Stretch="Uniform" Source="images/Tulip.jpg" Opacity="1" Height="154" Width="154">
<
Image.Projection>
<
PlaneProjection RotationY="120" />
</
Image.Projection>
</
Image>
<
Image Stretch="Uniform" Source="images/Leaves.jpg" Opacity="0.5" Height="154" Width="154">
<
Image.Projection>
<
PlaneProjection RotationY="240" />
</
Image.Projection>
</
Image>
<
Image Stretch="Uniform" Source="images/Leaves.jpg" Height="154" Width="154">
<
Image.Projection>
<
PlaneProjection RotationY="120" CenterOfRotationZ="100"/>
</
Image.Projection>
</
Image>
<
Image Stretch="Uniform" Source="images/Tulip.jpg" Height="154" Width="154">
<
Image.Projection>
<
PlaneProjection RotationY="240" CenterOfRotationZ="100" />
</
Image.Projection>
</
Image>
<
Path Data="M77,0 L77,154" Fill="#FF000000" StrokeThickness="1" Stroke="#FF000000" />


 



Produces the following:



PlaneProjectionSample1



 



The first 2 images rotate around the Y axis (I have added a Path line to mark its position and altered their opacity slightly to make things clearer), but the second 2 have had their Z positions (in and out of the screen, as it were) brought forward by 100 before being rotated around the Y axis. The grey background is the Grid that holds the images and you can see that the changes to the images’ projections can take them beyond their containing element.



Armed with this small amount of knowledge, I wanted to see if I could make a photocube and rotate it with animations or sliders so that the photos on all sides of the cube could be seen. Here’s the code:



<Grid.RowDefinitions>
<
RowDefinition Height="30" />
<
RowDefinition Height="*" />
</
Grid.RowDefinitions>
<
StackPanel x:Name="ControlsPanel" Grid.Row="0" Orientation="Horizontal">
<
TextBlock Text="Y Rotation : " Margin="2" VerticalAlignment="Center" />
<
Slider x:Name="YSlider" Margin="2" VerticalAlignment="Center" Minimum="0" Maximum="360" Width="150" Value="0" ValueChanged="YSlider_ValueChanged"/>
</
StackPanel>
<
Grid x:Name="ImageGrid" Grid.Row="1">
<
Image x:Name="FrontImage" Visibility="Visible" Width="154" Height="154" Stretch="Fill" Source="Images/Front.png">
<
Image.Projection>
<
PlaneProjection CenterOfRotationZ="-77" RotationY="0"/>
</
Image.Projection>
</
Image>
<
Image x:Name="LeftImage" Visibility="Visible" Width="154" Height="154" Stretch="Fill" Source="Images/Left.png">
<
Image.Projection>
<
PlaneProjection CenterOfRotationZ="-77" RotationY="90"/>
</
Image.Projection>
</
Image>
<
Image x:Name="BackImage" Visibility="Visible" Width="154" Height="154" Stretch="Fill" Source="Images/Back.png">
<
Image.Projection>
<
PlaneProjection CenterOfRotationZ="-77" RotationY="180"/>
</
Image.Projection>
</
Image>
<
Image x:Name="RightImage" Visibility="Visible" Width="154" Height="154" Stretch="Fill" Source="Images/Right.png">
<
Image.Projection>
<
PlaneProjection CenterOfRotationZ="-77" RotationY="270"/>
</
Image.Projection>
</
Image>
<
Image x:Name="TopImage" Visibility="Visible" Width="154" Height="154" Stretch="Fill" Source="Images/Top.png">
<
Image.Projection>
<
PlaneProjection CenterOfRotationZ="-77" RotationX="90" />
</
Image.Projection>
</
Image>
<
Image x:Name="BottomImage" Visibility="Visible" Width="154" Height="154" Stretch="Fill" Source="Images/Bottom.png">
<
Image.Projection>
<
PlaneProjection CenterOfRotationZ="-77" RotationX="270" />
</
Image.Projection>
</
Image>
</
Grid>




public partial class PhotoCubePage : Page
{
readonly Dictionary<Image, double> yRotations = new Dictionary<Image, double>(6);

public PhotoCubePage()
{
InitializeComponent();

foreach (Image image in ImageGrid.Children.Cast<Image>())
{
yRotations.Add(image, ((PlaneProjection)image.Projection).RotationY);
}
}

private void YSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
foreach (var imageRotation in yRotations)
{
((PlaneProjection) imageRotation.Key.Projection).RotationY = imageRotation.Value + ((Slider) sender).Value;
}
}
}


 



Produces the following:



BasicCube



 



I used basic images with text on them so I could verify the positioning and rotation of the cube’s sides, but it looks nicer with some images:



BasicPhotoCube



 



Next time – some mouse handling to rotate the cube, some animation easing for the cube’s movement and why it is much harder to rotate the cube along more than one axis…