Archive

Posts Tagged ‘ZUI’

Nice DeepZoom image samples via ClipFlair Studio’s Image component

Here are some DeepZoom image samples to enjoy via ClipFlair‘s Image component, in our ClipFlair Studio app (Silverlight-based):

Another impressive one is this Mandelbrot fractal, a dynamically generated DeepZoom image from Google AppEngine SDK demos:

http://studio.clipflair.net/?image=http://mattjmandelbrot.appspot.com/mandelbrot256.dzi

You can point the mouse and scroll the mouse wheel or double click to zoom in where you want.

One can make DeepZoom tiled images and DeepZoom image collections (can read more at http://msdn.microsoft.com/en-us/library/cc645050%28VS.95%29.aspx) with various authoring tools, including DeepZoom Composer (can read more about that tool at http://msdn.microsoft.com/en-us/library/dd409068%28loband%29.aspx)

However, the easiest way is to use ZoomIt service (http://zoom.it/pages/create/) to make a DeepZoom image wrapper (hosted at that site) for any big image on the web (giving it the original image URL). e.g. http://zoom.it/wR4i is a ClipFlair Poster (the respective DZI image is at http://cache.zoom.it/content/wR4i.dzi – internally ClipFlair’s image component converts zoom.it URLs to get the real .DZI image and show it)

HowTo: Scale control arround its center using a render transform

In the context of ClipFlair development, I was recently adding independent scaling (zooming) functionality to its ZUI container’s floating windows (apart from the container zooming functionality) and came across some strange behavior, where the windows seemed to also move apart from getting scaled.

After banging my head a bit I decided to take a closer look at Microsoft’s documentation and noticed the following text at http://msdn.microsoft.com/en-us/library/system.windows.uielement.rendertransformorigin.aspx

"RenderTransformOrigin has a somewhat nonstandard use of the Point structure value, in that the Point does not represent an absolute location in a coordinate system. Instead, values between 0 and 1 are interpreted as a factor for the range of the current element in each x,y axis. For example, (0.5,0.5) will cause the render transform to be centered on the element, or (1,1) would place the render transform at the bottom right corner of the element. NaN is not an accepted value. Values beyond 0 and 1 are also accepted, and will result in more unconventional transform effects. For instance, if you set RenderTransformOrigin to be (5,5), and then apply a RotateTransform, the rotation point will be well outside the bounds of the element itself. The transform will spin your element around in a big circle that originates beyond bottom right. The origin might be somewhere inside its parent element and could possibly be possibly out of frame or view. Negative point values are similar, these will go beyond the top left bounds. Render transforms do not affect layout, and are typically used to animate or apply a temporary effect to an element."

Since ClipFlair’s FloatingWindowHostZUI template uses a Canvas to host its FloatingWindows, I obviously didn’t care about the phrase “Render transforms do not affect layout”, but the phrase “values between 0 and 1 are interpreted as a factor for the range of the current element in each x,y axis” rang a bell immediately.

Misguided by the poor Intellisense info for the RenderTransformOrigin property, I had thought that point was in the control’s coordinate system, and since I wanted to scale the control arround its center, I had used the following erroneous statement:

window.RenderTransformOrigin = new Point(window.ActualWidth/2, window.ActualHeight/2);
window.RenderTransform = new ScaleTransform().SetScale((double)e.NewValue);

instead of the correct one:

window.RenderTransformOrigin = new Point(0.5, 0.5);
                                                        //scale arround the window center
window.RenderTransform = new ScaleTransform().SetScale((double)e.NewValue);

That is the range 0 to 1 for x & y coordinates of RenderTransformOrigin refers to the UIElement region, whereas less or greater values are (proportionally) outside of it, useful for example if you want to rotate an object arround an external point with a RotateTransform.

Don’t get puzzled by the expression new ScaleTransform().SetScale(…), it’s a syntax I use for authoring portable source code between WPF and Silverlight (since Silverlight only has a parameter-less constructor for ScaleTransform and anyway WPF doesn’t have a constructor that takes a single parameter for both X and Y scale values).

To sum up, here’s the “Scale” property I added to the FloatingWindow class:

#region public double Scale   
/// <summary> /// Gets or sets current window scale. /// </summary> /// <value>Current scale.</value>
public double Scale {  
get { return (double)GetValue(ScaleProperty); }
  set { SetValue(ScaleProperty, value); } } /// <summary>
/// Identifies the <see cref="FloatingWindow.Scale" /> dependency property.
/// </summary>
/// <value>
/// The identifier for the <see cref="FloatingWindow.Scale" /> dependency property.
/// </value>
public static readonly DependencyProperty ScaleProperty =
    DependencyProperty.Register("Scale", typeof(double), typeof(FloatingWindow),
      new PropertyMetadata(1d, OnScalePropertyChanged));
//Must use 1d here, not 1 (else will get XAMLParseException at runtime)
/// <summary>
/// ScaleProperty PropertyChangedCallback call back static function.
/// </summary>
/// <param name="d">FloatingWindow object whose Scale property is changed.</param>
/// <param name="e">DependencyPropertyChangedEventArgs contains old and new values.</param>
private static void OnScalePropertyChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e) {  
FloatingWindow window = (FloatingWindow)d;
  if (window != null)
  { 
    window.RenderTransformOrigin = new Point(0.5, 0.5); //scale arround the window center
    window.RenderTransform = new ScaleTransform().SetScale((double)e.NewValue);
}
} #endregion

Zoom and Pan control for WPF and Silverlight (via WPF compatibility layer)

I’ve managed to combine

http://www.codeproject.com/Articles/85603/A-WPF-custom-control-for-zooming-and-panning

and a cut-down version of it for Silverlight that was out there:

http://www.codeproject.com/Articles/167453/A-Silverlight-custom-control-for-zooming-and-panni

I did it in a way that the Silverlight version is source-code compatible with the WPF version (via a WPF compatibility layer that even implements value coercion), so that both the WPF and the Silverlight projects share the same source code files.

The resulting libraries for WPF and Silverlight work with the existing samples (from the two articles mentioned above) without any changes to them.

Can checkout the latest version (plan to add the samples there too for convenience) under the "Client" subfolder of ClipFlair source-code base at http://clipflair.codeplex.com

Categories: Posts Tags: , , , , ,

workaround: Silverlight Rect class missing ‘bool Contains(Rect)’ method

In ClipFlair, I’ve been looking into porting Kael Rowan’s ZoomableCanvas (related to ZUI code used in Code Canvas, Debugger Canvas etc. VisualStudio add-ons) from WPF to Silverlight.

One of the issues I found was that Silverlight’s Rect class (I guess this is a case for .NET Compact Framework in general) doesn’t have a method to check if a Rect contains another Rect, as does the full .NET framework which is available in WPF. That method was needed in Kael’s PriorityQuadTree source code.

Since we can check whether a Rect is contained in another Rect by checking if both its top-left and top-right points are contained, I edited Kael’s RectExtensions class, adding the following method:

/// <summary>
/// Indicates whether the current rectangle contains the specified rectangle, 
/// by checking for containment of the specified rectangle's top-left and
/// bottom-right points.
/// </summary> /// <param name="self">The current rectangle.</param> /// <param name="rect">The rectangle to check.</param> /// <returns><c>true</c> if the current rectangle contains the specified
/// rectangle; otherwise, <c>false</c>.</returns> public static bool Contains(this Rect self, Rect rect){ return self.Contains(new Point(rect.Left, rect.Top))
&& self.Contains(new Point(rect.Right, rect.Bottom)); }

Note that RectExtensions is a static (with only static methods, not instantiatable) class containing static extension methods (a feature of recent .NET versions), for the Rect class. So, once you’ve imported the RectExtensions class in a Silverlight class (assuming you have RectExtensions in the same project or in some referenced Silverlight or Portable library), all Rect instances gain a new method “bool Contains(Rect)” apart from the “bool Contains(Point)” one.

BTW, speaking of Zoomable User Interfaces (ZUIs) above, an important difference between WPF and Silverlight is that WPF has a theoretically unboundeded coordinate system, in contrast to Silverlight’s bounded one, where the maximum value for a coordinate component is approximately +32768. As stated in Microsoft’s WPF Compatibility article:

WPF has a theoretically unbounded coordinate system. Silverlight has a bounded coordinate system. The maximum value for a coordinate component in Silverlight is approximately +32678. APIs that represent a Silverlight coordinate might throw a native-level error or raise an exception if this maximum value is exceeded. This is true even for values that might be temporary and not intended for the final rendering value. 32678 is well beyond the maximum screen size, so this should only be an issue if you are performing math or applying transforms to coordinate values.

%d bloggers like this: